fix Issue 12486 - Function returning struct isn't called if enum of its result is accessed#8013
Conversation
|
Thanks for your pull request and interest in making D better, @FeepingCreature! 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
|
04df52e to
b31b110
Compare
enum of its result is accessed
enum of its result is accessedenum of its result is accessed
|
Ah, I see, it has to be an expression but it is a struct. |
221f387 to
6f1efcc
Compare
|
Is there a way to say "the expression is trivial"? I don't like enumerating all the different kinds of trivial expressions manually. Maybe optimize just the comma expression, somehow? |
469af15 to
809989c
Compare
No need to wait. auto-tester automatically refreshes itself though it might take a bit until it reruns your PR as there's usually a lot of traffic. |
|
Okay. |
809989c to
fbb2755
Compare
|
:stares at ci Jenkins: Do you ever get that feeling that, while all the tests are green, they really shouldn't be? |
Jenkins doesn't run the testsuite. It just compiles dmd and then builds ~40 projects and runs their testsuite. |
|
:stares at AppVeyor: Do you ever get that feeling that, while all the tests are green, they really shouldn't be? I feel like the only reason this works is that nobody, me included, has come up with a sufficiently clever piece of code to break it yet. And yet, it does seem to work everywhere, and the error it fixes was really bad, so... |
|
Is it stuck? auto-tester has been at pending: 8 for hours now. |
No, auto-tester deletes all builds whenever a new PR is merged at DMD, druntime or phobos. Click on the link to see the history. |
fbb2755 to
1fd4769
Compare
|
How do I find a reviewer? |
1fd4769 to
be22c7d
Compare
src/dmd/typesem.d
Outdated
| ve = ve.expressionSemantic(sc); | ||
| if (e.op == TOK.type || e.op == TOK.this_ || e.op == TOK.super_ || e.op == TOK.variable) | ||
| { | ||
| ve = ve.expressionSemantic(sc); |
There was a problem hiding this comment.
The point here is to avoid being forced to optimize the expression if we know the lhs of the comma expression would be trivial, since optimization may change the meaning but is sometimes necessary so we don't have a pointless comma expression lying around. I wish there was a better way to do this.
There was a problem hiding this comment.
Please add this explanation as a comment in the code. It will be useful in the future when people will try to understand the code.
There was a problem hiding this comment.
Documentation added.
Ping or assign me and I'll get round to it. 😉 |
|
Um ... @ibuclaw ping? I don't know how to assign people. I don't think I can do that. |
|
I debugged what's happening with the Phobos tests. The Cartesian products being computed in std.setops are ridiculously too large. They look small, but due to the fact that 3D nested Cartesian product is strongly (quadratically) biased towards spread along the first dimension, actually take billions to trillions of cycles to run. This was not noticed before because due to the bug, they essentially passed by coincidence. ( I'll make a pr to reduce the sizes of the last dimensions massively. edit: To clarify what's happening here: DMD is right, the Phobos tests are wrong. edit: Fix merged in Phobos. Tests should start passing now. |
be22c7d to
3be7e80
Compare
src/dmd/typesem.d
Outdated
| // variable.enum or type.enum, where we know the lhs does not have side effects, | ||
| // just use the enum directly. | ||
| // It would be preferable if we could just directly check if the left hand side of the | ||
| // expression is obviously side-effect free, but there does not seem to be a way to do that. |
There was a problem hiding this comment.
There was a problem hiding this comment.
As I understand it, a function that never returns but is pure will still be considered as not having side effects by this code? But we must not optimize it away: we may optimize >1 calls to 1 call, but we mustn't optimize 1 call to 0 calls.
src/dmd/typesem.d
Outdated
| ve = ve.expressionSemantic(sc); | ||
| } | ||
| else | ||
| { |
There was a problem hiding this comment.
This fix looks like it's in the wrong place. expressionSemantic() should not be removing expressions with side effects. Putting the fix here is papering over the problem for this case, and it'll just cause other problems. Need to find the root problem.
There was a problem hiding this comment.
Right, the problem is if I keep the side effects via comma expression, as I have to in order to get correct code in the case call().ENUM, then I end up with unusable comma expressions downstream in the cases where the lhs genuinely is trivial and can be discarded safely. I can't fix it earlier, because this is where the access resolves.
edit: To clarify, what DMD does right now is removing side effects from expressions in that code: call().ENUM becomes ENUM, with call() being discarded. The problem is fixing that without breaking enum access for trivial lhs, say alias foo = this.bar.
There was a problem hiding this comment.
what DMD does right now is removing side effects from expressions in that code: call().ENUM becomes ENUM, with call() being discarded.
That's a bug, as call() has side effects. That's where it needs to be fixed.
The problem is fixing that without breaking enum access for trivial lhs, say alias foo = this.bar.
Maybe that's there the special case should go.
There was a problem hiding this comment.
I don't understand what you mean. This is where that happens, isn't it?
WalterBright
left a comment
There was a problem hiding this comment.
Fix is in the wrong place.
|
Ping? See review comments. The code is the place where the side effects are currently discarded. |
|
@FeepingCreature I think what Walter meant is that the proper fix should probably be somewhere in |
|
More specifically, there may be a defect somewhere in |
|
Well, are infinite loops supposed to count as side effects? If not, istm there is a lack of an idiom here. |
All calls should be considered as a side effect until inlined. There is |
|
Thanks, that seems sensible. I'll fix it in a while. I still don't get why Walter thinks the fix is in the wrong place. This is the place that throws away the side effect right now. edit: Trying! edit: Okay, how about this now. edit: This is not working at all. It treats it like a property access, despite being an enum. Going back to the version that worked, for now. |
003dadf to
416d231
Compare
…ember on its returned type
416d231 to
775ee59
Compare
|
@WalterBright I tried to keep it as a DotExp instead of forcing it into a CommaExp, but if I do so all the way through codegen, it produces invalid code because (I guess) it seems to try and treat the enum access as a struct read. ISTM that CommaExp is just how enum dot accesses to expressions with side effects have to look right now. If it's possible to make a better fix for this, I don't know how. I don't think I can get the code materially better than this. |
|
ping |
ibuclaw
left a comment
There was a problem hiding this comment.
Seems reasonable to me.
@FeepingCreature - please rebase to restart failing tests.
@WalterBright - check this again.
No need. auto-tester will automatically restart itself and all other CIs are passing. which hopefully helps to weed them out. |
|
ping |
|
I'll compile this patch in locally and inspect the change done, then will try to break it. 😉 If all is OK I see no problem with committing this. |
| } | ||
| checkAccess(e.loc, sc, null, v); | ||
| Expression ve = new VarExp(e.loc, v); | ||
| if (!isTrivialExp(e)) |
There was a problem hiding this comment.
@FeepingCreature - you could also use hasSideEffects(e) (or e.hasSideEffects() if you prefer UFCS). When having an initial glance at the implementation, it didn't seem like it was a strong enough guarantee, but having a closer look, only strongly pure functions would have their calls omitted here. Which is harmless.
There was a problem hiding this comment.
As far as I can tell, pure void foo() { while (true) { } } is strongly pure, but must under no circumstances be optimized away. That exact case was what triggered this bug in the first place.
Pure expressions can be reduced from two to one, but only trivial expressions can be reduced from one to zero.
There was a problem hiding this comment.
Ah, bugger those looping pure functions. :-)
Yep, makes sense, and LGTM.
There was a problem hiding this comment.
@FeepingCreature - For brownie points, you can add a comment with a link to the bugzilla reference.
|
@WalterBright - Any last comments before I dismiss your stale review? |
Fix issue 12486
When accessing an enum member of a struct returned by an expression with side effects, don't discard the expression but evaluate it anyways. (No, this is not safe even if the expression is pure: it might loop infinitely.)