-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Fix crash when declaring function selector of parent class as public constant #16053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
78b8931
to
53c747d
Compare
} | ||
|
||
contract C is B { | ||
bytes4 public constant s2 = B.g.selector; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is enough as a repro for the issue, but for completeness you should do something with the constant, because we only run the ConstantEvaluator
at that point.
E.g. use negation on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test also does not really check if the value of the constant is correct (I'd have to calculate it and compare to be sure). Would be better to have the test verify it:
assert(s2 == B.g.selector);
} | ||
|
||
contract C is B { | ||
bytes4 public constant s2 = B.g.selector; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bytes4 public constant s2 = B.g.selector; | |
bytes4 public constant SELECTOR = B.g.selector; |
"\n"; | ||
auto const it = m_context.mostDerivedContract().annotation().internalFunctionIDs.find(&_referencedFunction); | ||
if (it == m_context.mostDerivedContract().annotation().internalFunctionIDs.end()) | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not declaring the variable is not necessarily the right solution in a general case. It helps in the repro because we're only accessing the selector, so the variable would go unused, but if we were instead trying to get a pointer to the function, we'd end up trying to access a variable that does not exist.
Theoretically, you should be able to reproduce that by making a constant of an internal function type:
function() constant G = B.g;
But we don't allow this:
Error: Initial value for constant variable has to be compile-time constant.
It actually looks to me like an unrelated bug - I can't see a good reason not to treat references to functions as compile-time constants. It's not like functions can change at runtime.
The weird thing is that the crash happens only when you try to access the selector inside a constant initializer, and not e.g. in the body of a function. You should check why. I'd expect assignInternalFunctionIDIfNotCalledDirectly()
to be called in this case too so apparently it does find the ID then. I suspect that the actual cause might be in how (or when) internalFunctionIDs
is populated.
} | ||
|
||
contract C is B { | ||
bytes4 public constant s2 = B.g.selector; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add some coverage for related cases:
- Using selector in other places outside of functions. E.g.:
- constructor args when called as a modifier
- constructor args when called after
is
- immutable initialization
- state variable initialization
- file-level constant
- constant in an unrelated contract or in a library
- Using the reference to the function itself (but still without calling it).
This needs a changelog entry. Also, please triage the issue before starting to work on it :) |
It does not seem to be related to Anyway, like I mentioned in #16053 (comment), I'd expect problems even when accessing just the function.
Could be. One of the things we use
Not sure I understand this bit. What do you meant by it being treated as variable/function? |
Looking at the contract C is Base {
bytes4 constant extConst = Base.ext.selector;
bytes4 constant pubConst = Base.pub.selector;
} the only difference being that Still, the call graph does find the call to {"C", {
{"Entry", "function Base.ext()"},
{"Entry", "function Base.pub()"},
{"function Base.ext()", "function free()"},
{"function Base.pub()", "function free()"},
}}, Unless the fact that it has a getter affects the graph. |
Fixes #16050.
I wonder if this can occur for more than just the selector, though. It is somewhat related to PR #11014. Declaring
s2
in the test asconstant
results in it being treated as variable (which has no referenced function entry), declaring itimmutable
treats it as function and we don't error out. Not sure if we should add some safeguard here.