Translate _d_arrayappend{T,cTX} to templates#9982
Conversation
There was a problem hiding this comment.
No tests? You can try to write a test that verifies the AST rewriting works correctly. Have a look here https://github.com/dlang/dmd/tree/master/test/unit
|
Instead of rewriting back to |
30ca22f to
037cff9
Compare
I'm not sure if/how I can do this is a way that does not duplicate code as |
|
As mentioned in dlang/druntime#2632 (comment) |
037cff9 to
7a22ac5
Compare
I see. Then let's keep the rewrite for now 👍 |
7a22ac5 to
96424a4
Compare
96424a4 to
433198f
Compare
433198f to
4fc9e5e
Compare
|
Thanks for your pull request and interest in making D better, @Vild! 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 referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + dmd#9982" |
4fc9e5e to
2f9a108
Compare
2f9a108 to
9da12ca
Compare
9da12ca to
edccf51
Compare
|
This should work now, but it requires the fixes in dlang/druntime#2718 This will now lower I've added two patches to the backend that I think is important to be reviews by someone that know the backend so I don't break anything. The second fix is 1f4d9bf. This make it only compile the Should I move these to a separate PR? It is needed for this PR, but it is also a bit off-topic. |
src/dmd/expressionsem.d
Outdated
|
|
||
| *output = e0.expressionSemantic(sc); | ||
| if (global.params.verbose) | ||
| message("lowered % =>\n `%s`", oldExp.toChars(), output.toChars()); |
There was a problem hiding this comment.
This looks like debug code which should probably removed before this is merged?
| // so don't print anything to avoid double error messages. | ||
| if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT)) | ||
| if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT | ||
| || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX)) |
There was a problem hiding this comment.
If this list grows much longer, we should probably use a switch statement instead? But I guess with 4 entries this is still ok.
| /* CatAssignExp will exist in `__traits(compiles, ...)` and in the `.e1` branch of a `__ctfe ? :` CondExp. | ||
| * The other branch will be `_d_arrayappendcTX(e1, 1), e1[$-1]=e2` which will generate the warning about | ||
| * GC usage. See visit(CallExp). | ||
| */ |
There was a problem hiding this comment.
Hmm, could we change visit(ConExp) instead to not visit __ctfe only code? I guess that right now we probably do not differentiate, but in the long run it should be possible to ignore everything in __ctfe only blocks for nogc/codegen. As you already have the patch for codegen, why not do the same for nogc checks?
And then add a Test + Changelog entry for this. Some people might be quite happy about this change: https://run.dlang.io/is/G6fv55 ;-)
There was a problem hiding this comment.
Sadly I cannot change so it doesn't visit this function because of two reasons.
-
nogc.d is using
dmd.apply.walkPostorderwhich walks it in this order:walk(econd),walk(e1),walk(e2), and lastlyvisit(CondExp). https://github.com/dlang/dmd/blob/master/src/dmd/apply.d#L144. As this walker is used all over the codebase I don't think it is a good idea to introduce changes to it. -
It still needs to set
err = trueto mark the appending asnon-@nogcbecause the lowering code will not lower code that is in a__traits(compiles, ...). Printing error here is not needed because the error will always be gagged forSCOPE.compile, and if errors were printed they would print the exact same message as the one invisit(CallExp).
So this visit function will now just silently error.
| elem_setLoc(e, ce.loc); | ||
| result = e; | ||
| return; | ||
| } |
There was a problem hiding this comment.
Seems like a good idea to me. Needs to be in its own PR though and I guess there's already a bug report for this somewhere.
Ideally, this should not hard-detect the ctfe variable though. You code will fail on constructs such as these:
void foo(bool dummy)
{
if (__ctfe && dummy)
{
// CTFE-only Code goes here
}
}What should be done instead is this: Set __ctfe (somehow) to false, then constant fold / interpret the condition. If the result is a constant false, the if part of the condition is a ctfe only part. If it is constant true, the else branch is a ctfe only branch. If it is not const, both branches may be used at runtime.
Maybe that's out of scope for GSoC though, but it would be very useful for betterC.
There was a problem hiding this comment.
I wonder how all that stuff integrates with SCOPEctfe though, maybe what should really happen is that the if block content should be set to SCOPEctfe instead and the toElem / nogc visitors should skip these scopes.
OTOH there's no scope introduced when using CondExp, so for CondExp your code here seems to be corret.
There was a problem hiding this comment.
ping @JinShil as IIRC your working on SCOPEctfe issues, so you may be interested in this.
There was a problem hiding this comment.
What is this code doing and why?
There was a problem hiding this comment.
It detects an __ctfe ? e1 : e2 expression, then ignores e1 and only sends e2 to the code generation stage. I guess without this, dropping e1 code occurs later in the backend. @Vild can you elaborate why exactly this is necessary?
There was a problem hiding this comment.
Without this change the codegen would generate code for both e1 and e2, and while generating code for e1 it would trigger this assert(0) https://github.com/dlang/dmd/pull/9982/files#diff-7a340f667e49df4f91bcab621efa8521R3294
It will also only trigger on code that looks like __ctfe ? e1 : e2, as this is the only form I'm outputting inside expressionsem.d. But I probably need to extend this to atleast support if statements as the inline can rewrite CondExp to IfStatement.
There was a problem hiding this comment.
Without this change the codegen would generate code for both e1 and e2, and while generating code for e1 it would trigger this assert(0).
OK. But somehow the backend currently also drops the e1 branch, the code is not in the generated object file. Does this happen only as dead-code-elimination after the initial codegen stage?
There was a problem hiding this comment.
Does this happen only as dead-code-elimination after the initial codegen stage?
I have been unable to find where in the backend were it gets removed, all I know is that it is removed.
There was a problem hiding this comment.
It detects an
__ctfe ? e1 : e2expression, then ignorese1and only sendse2to the code generation stage.
So, this is basically a workaround because we don't have version(CTFE)?
Without this change the codegen would generate code for both
e1ande2, and while generating code fore1it would trigger thisassert(0).
Why wasn't e1 lowered in the semantic phase?
There was a problem hiding this comment.
So, this is basically a workaround because we don't have version(CTFE)?
ya
Why wasn't e1 lowered in the semantic phase?
Becausee1is the__ctfecode-path, which is the original expression.
src/dmd/expression.d
Outdated
| */ | ||
| extern (C++) class CatAssignExp : BinAssignExp | ||
| { | ||
| bool alreadyLowered; |
There was a problem hiding this comment.
Why would the compiler attempt to lower an expression more than once?
There was a problem hiding this comment.
In this lowering commit:
*output = ce.expressionSemantic(sc);
on the final ConditionExpression does call expressionSemantic on e1 and e2, IIRC. As we repurpose CatAssignExp when lowering CatAssignExp ==> __ctfe ? CatAssignExp : _d_arraycat we have to be careful not to do the lowering on the newly generated CatAssignExp.
However, maybe isCTFEHook or something like that is a better term.
| return; | ||
| } | ||
| f.printGCUsage(e.loc, "operator `~=` may cause a GC allocation"); | ||
| } |
There was a problem hiding this comment.
I don't quite understand the need for these error messages. If the runtime hook causes a GC allocation, then the error message will already be emitted there. However, the error message will be in terms of the lowered code, and not the user's code.
If the purpose of this code is to make the error message more relevant to user code, then that is the case that I mentioned earlier on slack. What we could do instead is pass the file, line number, and even the user's code to the runtime hook and have the runtime hook emit a more meaningful error message, perhaps with a static assert.
Regardless, I'm ok with keeping this as is to maintain the status quo.
There was a problem hiding this comment.
Yes, long-term moving error generation into the hooks would be nicer*. For now I think this is fine to keep compatibility for the error messages.
EDIT: Actually, thinking about this some more, the problem below should be unrelated. I'll keep it here though, as I think it's an interesting, although unrelated, thought.
(*) Right now, main1 and main2 won't compile:
string aCTFEOnlyHelper()(string a)
{
return "Hello" ~ a;
}
void nogcAppend(ref string r, string val) @nogc
{
}
void main1() @nogc
{
string result;
__ctfe ? result ~= "foo" : nogcAppend(result, "foo");
}
void main2() @nogc
{
enum val = aCTFEOnlyHelper("foo");
}With the changes suggested here, main1 will actually compile.
For main2, you have the problem that as soon as you instantiate aCTFEOnlyHelper, even if it's in a CTFE-only context, the code for aCTFEOnlyHelper will also be emitted to the object file, so you need the GC there. This is probably harder to fix than the main1 problem, I've filed an issue here: https://issues.dlang.org/show_bug.cgi?id=20101
(Actually, we writing the issue I had epiphany: What we want there is actually already largely available in speculative template instantiation. So it might actually not be too difficult to get the main2 case working).
There was a problem hiding this comment.
This code was moved from visit(CatAssignExp) as a call to _d_arrayappend{T,cTX} is now what symbolizes array appending, and the message will be printed when compiling with -vgc.
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
edccf51 to
a890f13
Compare
|
I'm happy with the code, but it still requires this druntime PR to be merged: dlang/druntime#2718 |
Signed-off-by: Teodor Dutu <teodor.dutu@gmail.com>
|
@teodutu is in the process of reviving this. |
druntime PR needed for this: dlang/druntime#2632
Cat{,Elem}AssignExp}to the templatesobject._d_arrayappend{T,cTX}.CallExp, that will run the new hooks, back toCat{,Elem}AssignExp.