-
-
Notifications
You must be signed in to change notification settings - Fork 608
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
Fix Issues 23408 and 23403 - __FUNCTION__ does not resolve properly #14549
Conversation
|
Thanks for your pull request and interest in making D better, @RazvanN7! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla references
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "stable + dmd#14549" |
| @@ -11,7 +11,7 @@ struct Line | |||
|
|
|||
| void foo(Line line1 = __LINE__, int line2 = __LINE__, int line3 = int(__LINE__)) | |||
| { | |||
| assert(line1 == 12); | |||
| assert(line1 == 21); | |||
| assert(line2 == 21); | |||
| assert(line3 == 12); | |||
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 can also be modified to print 21, however, according to @WalterBright in this comment this sort of construction is used to retain the C++ behavior of LINE
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 also have bugs about __FUNCTION__.ptr not resolving correctly so I don't think that's a good argument.
Nonsense, just merge stable back into master. |
|
I'm all for properly resolving special tokens in default arguments, but why only do it for struct literals and call expressions? |
You can always merge stable to master immediately afterwards. |
Fair enough.
Those are the ones I've identified so far. We can incrementally add them as soon as we identify them.
OK. I just hoped I could avoid doing that. Don't want to deal with potential merge conflicts. |
|
Now targeting stable. |
|
Anything else blocking this? I wish we could move on with this so that we can unblock other work. |
|
I don't like how this PR appears to be about solving a bug with
Special tokens can be part of any expression. void foo(string f = toLower(__FUNCTION__[0] ~ "()") );
void foo(int f = (__LINE__ > 10) ? 3 : 5);It's pretty obvious, cases are not waiting to be identified. Currently special tokens are deliberately only resolved in the top level, and not when part of a larger expression. This PR moves away from 'shallow' resolution to 'deep' resolution, but only 10% there. If the plan is to move 100% there, then @WalterBright should give green light since he objected to that in the comment you linked. If we go through with it, an expression visitor might be re-used instead of implementing void f(int x = T!__LINE__); // is the type correct? Depends on where it's called from. |
|
I looked at what's blocked because of this, and the test case added in this PR is the culprit: I wasn't aware you implemented #14550 can be unblocked by simply modifying the test case. |
|
I would argue we should commit to 100%. The spec currently does not specify anything about shallow vs. deep, rather that FUNCTION and other special function are evaluated at the point of instantiation. So, I think that most users expectation would be the behavior that this PR is introducing. Anyway, currently, if you use FUNCTION nested in other constructs you either get an unexpected result or a compiler crash. |
|
IMO it would be much better to either move the proverbial "needle" back to 0% and keep it there, or commit to having all default arguments evaluated at the call site, regardless of whether they contain a special keyword like The middle-ground approach, where we have both kinds of evaluation in the language and the switch to select between them is "does this expression's AST contain a special keyword anywhere", is going to result in a rat's nest of edge-case interactions (some of which @dkorpel has already pointed out), and will encourage D programmers to invent "idioms" like the following: // Use `readln()` as default argument
// Evaluated at call site due to inclusion of __LINE__
auto fun(string s = () { if (__LINE__) return readln(); }())
{
// ...
}This is exactly the sort of thing Andrei was referring to when he coined the term "Good Work". It's the sort of thing that makes D more like C++—full of obscure, dark corners that only compiler developers and obsessive readers of the language spec fully understand. I hope we can agree that this is not a path we want to go down. |
|
@pbackus I think that keeping the needle at 0% is not the behavior users expect. This causes reports such as [1][2] and many other duplicated/sightly modified examples. The current design (if I may call it so, because it looks like unintended behavior rather than a design choice) is surprising and borderline useless. By looking at the minimally provided spec [3] ("The attributes of the AssignExpression are applied where the default expression is used." - but of course the provided example has always compiled), it seems that doing the analysis of default arguments at the call site is what was intended, but not implemented. Also, there is [4] ("FUNCTION expands to the fully qualified name of the function at the point of instantiation."). Since we must evaluate default arguments at the call site in order to check for attributes, how can we make a case for the special tokens to be evaluated at the call site only when they are not nested in other expressions? This does not make any sense to me. The situation that we have now is that you can break the type system through default arguments, if you want to fix that, you need to evaluate the default arguments at the call site, otherwise there is no way of fixing [1]. In my view, this is just a small fix to get things in the right direction. Or we can just drop this PR and keep this broken state, but let's not argue that the current "design" is what's intended and the correct way. [1] https://issues.dlang.org/show_bug.cgi?id=11048 |
I don't think anyone is arguing that, I'm all for going 100%. But this PR adds a little more top-down analysis which Walter is against. What if we resolved |
|
If we can get #14309 in, then semantic analysis is done for default arguments at each call site, which may also help with fixing this issue. |
|
@RazvanN7 If the plan is to ultimately evaluate all default arguments at the call site, that's fine. The outcome I want to avoid is "some default arguments are evaluated at the call site, some are evaluated at the definition site, and the rule for which is which is complex and difficult to document/teach." |
| { | ||
| for (int i = 0; i < (*elements).length; i++) | ||
| { | ||
| auto elem = (*elements)[i]; |
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.
foreach (ref elem; (*elements)[])
if (elem)
elem = elem.resolveLoc(loc, sc);
| auto arg = (*arguments)[i]; | ||
| if (arg) | ||
| (*arguments)[i] = arg.resolveLoc(loc, sc); | ||
| } |
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.
ditto
|
@WalterBright I take it you agree with this addition? |
|
@RazvanN7 still pursuing or found another way to unblock the template work? |
Properly resolves
__FUNCTION__and folks by addingresolveLocforStructLiteralExpandCallExp. The newresolveLocoverrides callresolveLocon the arguments of the 2 AST constructions.I am not targeting stable because this is blocking @teodutu from implementing the array concatenation template hook and lowering (which target master).