Introduce alias for variables
axic opened this issue ยท 17 comments
An aliased variable works like a macro - the original variable is looked up at every access.
Any example of the alias
keyword:
contract A {
uint alias number = block.number;
function a() returns (uint) {
return number; // this returns the *current* block.number every time it is called
}
}
(Split off #992.)
@chriseth I'm not convinced this is a good feature. Can you explain it more where alias
is useful?
Having proper macros is a different case.
I'm skeptical too. Maybe there's a better example of where it would be useful?
@axic I'm also not convinced. The idea was to use a much more generic thing, which can bind any identifier to any other identifier:
contract c {
using a = uint;
using myType = SomeLibrary.SomeNamespace.SomeType;
using selfdestruct = suicide;
}
This would also work for block.number
, but perhaps not for expressions like using tomorrow = now + 1 day
.
using a = uint;
using myType = SomeLibrary.SomeNamespace.SomeType;
These two seem more like what a C typedef
is, while
using selfdestruct = suicide;
is just an alias
(or a dumb macro
).
using a = uint;
I think typedef
ing primitives should be disallowed. It creates a maze of code for no benefit.
@axic I'm not so sure about this. Of course, every feature can be abused, but it can also allow you to create new types on the ground of existing types:
using counter = uint;
library L {
function increment(counter c) returns (counter) { return counter + 1; }
}
using L for counter;
contract C {
function f() {
counter x = 2;
x.increment();
uint y = 3;
y.increment(); // does not work
}
}
For establishing invariants I look for places where a storage content is changed. Say balance[]
. Usually I just grep the whole source with balance
and I find places where it is changed. With aliases this becomes one-step harder because the array content can change for assignments into aliases.
@chriseth that seems to be a nice use case. I still fear it will be grossly misused - if only there would be a way to prevent that a bit more.
using
is probably misleading, alias
or define
seem a bit more fitting.
I want readonly aliases, something like uint view alias number = some_struct.member
, or even, make it readonly by default.
my 2 cents:
uint256 stayDayNum = (now.sub(payment[msg.sender][_siteAddress][_orderId].paymentDate)).div(86400);
uint256 _value = payment[msg.sender][_siteAddress][_orderId].sellerValue;
address currency = payment[msg.sender][_siteAddress][_orderId].currency;
address payable buyerAddress = payment[msg.sender][_siteAddress][_orderId].buyerAddress;
Because of the stack size limit I can not do something like this:
MyStruct s = payment[msg.sender][_siteAddress][_orderId];
uint256 stayDayNum = (now.sub(s.paymentDate)).div(86400);
uint256 _value = s.sellerValue;
address currency = s.currency;
address payable buyerAddress = s.buyerAddress;
Is not it the perfect usecase for alias/macro/using? I would be happy for any construct that would help me get rid of such ugly code without stack limit problem downside.
I do not fully understand - why would you want to have another local variable like currency
if you can use s.currency
?
This feature scares me and seems to make code audit more difficult since new side effects are introduced.
@fulldecent I agree.
I'm not sure this is on point, but for generic code like this:
function (uint myParam) public pure returns (uint) {
uint param2 = someFunction(myParam);
return someOtherFunction(param2);
}
It costs less gas, because it uses less memory to do something like this:
function (uint myParam) public pure returns (uint) {
myParam = someFunction(myParam);
return someOtherFunction(myParam);
}
However, there are cases where this could be much more difficult to read: you cannot assign the variable you are working with a meaningful name other than the one it previously had. Thus, if you want to reuse this variable, you have to keep the same name in order not to use another memory slot.
What I had in mind was something like this:
function (uint myParam) public pure returns (uint) {
uint alias param2 = someFunction(myParam);
return someOtherFunction(param2);
}
where, at compile time, every instance of param2 is replaced by myParam. It would thus allow to save some gas and to improve code readability. Furthermore, if, after an alias a = b
, the compiler does not authorize the use of b
anymore, I don't see any side-effect that would occur.
I don't know whether that's possible, nor whether you had this case in mind when discussing this idea. If this is not on point, please tell me and I would be happy to delete my comment and open an appropriate issue ๐
Reusing variables to reduce gas costs requires the userland programmer to make assumptions about how the compiler works.
As a userland programmer, don't make assumptions about how the compiler works.
Now if you want to be a solc developer then do this:
- Create a minimally viable program using both syntaxes
- Profile them
- Open an issue stating that the optimizer has an opportunity to improve
Right, if the more readable version produces suboptimal code, please just open a separate issue and we'll see what we can do. We'd rather improve the optimizer than add new syntax just to work around its deficiencies. Also note that the upcoming Yul-based codegen has a much more powerful optimizer and that generator is going to become the default in the near future. You can pass the --experimental-via-ir
option to solc to use it - before submitting an issue please make sure that the new optimizer doesn't already handle it better.