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
[stable] Fix misc. issues wrt. temporaries for -checkaction=context lowering #11005
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,8 @@ void test20114() | |
assert(c == "1 != 0"); | ||
} | ||
|
||
version (DigitalMars) version (Win64) version = DMD_Win64; | ||
|
||
void test20375() @safe | ||
{ | ||
static struct RefCounted | ||
|
@@ -87,6 +89,7 @@ void test20375() @safe | |
} | ||
|
||
static int instances; | ||
static int postblits; | ||
|
||
this(bool) @safe | ||
{ | ||
|
@@ -96,10 +99,14 @@ void test20375() @safe | |
this(this) @safe | ||
{ | ||
instances++; | ||
postblits++; | ||
} | ||
|
||
~this() @safe | ||
{ | ||
// make the dtor non-nothrow (we are tracking clean-ups during AssertError unwinding) | ||
if (postblits > 100) | ||
throw new Exception(""); | ||
assert(instances > 0); | ||
instances--; | ||
} | ||
|
@@ -112,19 +119,54 @@ void test20375() @safe | |
|
||
{ | ||
auto a = RefCounted.create(); | ||
assert(a == RefCounted.create()); | ||
RefCounted.instances++; // we're about to construct an instance below, increment manually | ||
assert(a == RefCounted()); // both operands are pure expressions => no temporaries | ||
} | ||
|
||
assert(RefCounted.instances == 0); | ||
assert(RefCounted.postblits == 0); | ||
|
||
{ | ||
auto a = RefCounted.create(); | ||
const msg = getMessage(assert(a != RefCounted.create())); | ||
// assert(msg == "RefCounted() == RefCounted()"); // Currently not formatted | ||
assert(msg == "assert(a != RefCounted.create()) failed"); | ||
assert(a == RefCounted.create()); // impure rhs is promoted to a temporary lvalue => copy for a.opEquals(rhs) | ||
} | ||
|
||
assert(RefCounted.instances == 0); | ||
assert(RefCounted.postblits == 1); | ||
RefCounted.postblits = 0; | ||
|
||
{ | ||
const msg = getMessage(assert(RefCounted.create() != RefCounted.create())); // both operands promoted to temporary lvalues | ||
assert(msg == "RefCounted() == RefCounted()"); | ||
} | ||
|
||
version (DMD_Win64) // FIXME: temporaries apparently not destructed when unwinding via AssertError | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm mildly worried about a FIXME targeting stable, is tis problem EH, ABI, Codegen, something else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think many of these tests here are actually testing undefined behavior, as the lifetime of temporaries in statements throwing an AssertError is tracked, continuing in the tests after catching the I'm unsure about the spec. Win64 with DMD seems to be the only platform eliding the cleanups, so I found it mildly worrying too, hence the comment, but at the same time I don't care about the potential DMD-specific codegen or druntime EH issue, as it's definitely unrelated to this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough. |
||
{ | ||
assert(RefCounted.instances >= 0 && RefCounted.instances <= 2); | ||
RefCounted.instances = 0; | ||
} | ||
else | ||
assert(RefCounted.instances == 0); | ||
assert(RefCounted.postblits == 1); | ||
RefCounted.postblits = 0; | ||
|
||
static int numGetLvalImpureCalls = 0; | ||
ref RefCounted getLvalImpure() @trusted | ||
{ | ||
numGetLvalImpureCalls++; | ||
__gshared lval = RefCounted(); // not incrementing RefCounted.instances | ||
return lval; | ||
} | ||
|
||
{ | ||
const msg = getMessage(assert(getLvalImpure() != getLvalImpure())); // both operands promoted to ref temporaries | ||
assert(msg == "RefCounted() == RefCounted()"); | ||
} | ||
|
||
assert(numGetLvalImpureCalls == 2); | ||
assert(RefCounted.instances == 0); | ||
assert(RefCounted.postblits == 1); | ||
RefCounted.postblits = 0; | ||
} | ||
|
||
string getMessage(T)(lazy T expr) @trusted | ||
|
@@ -140,10 +182,22 @@ string getMessage(T)(lazy T expr) @trusted | |
} | ||
} | ||
|
||
void testMixinExpression() @safe | ||
{ | ||
static struct S | ||
{ | ||
bool opEquals(S) @safe { return true; } | ||
} | ||
|
||
const msg = getMessage(assert(mixin("S() != S()"))); | ||
assert(msg == "S() == S()"); | ||
} | ||
|
||
void main() | ||
{ | ||
test8765(); | ||
test9255(); | ||
test20114(); | ||
test20375(); | ||
testMixinExpression(); | ||
} |
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 early expression sema of
exp.e1
, the assert condition, was one of the problems. E.g.:Here, an lvalue
a
is passed toS.opEquals()
, which takes the arg by value. The earlyCallExp
sema already lowers the argument expressiona
to a temporary, to something likeS __copytmp = (__copytmp = a).__postblit()
. That expression (instead ofa
) was previously stored as a further__assertOp
temporary, and the original argument replaced by that temporary (without postblit).Previous lowering:
New:
A related 2nd issue concerns rvalue arguments - the early
CallExp
sema detects that no temporary is needed (move; elide postblit and dtor in the caller); the rvalue was previously correctly stored as__assertOp
temporary, but as it is now an lvalue (possibly accessed a 2nd time for the_d_assert_fail()
call if the assertion fails), it must be copied + postblitted. This can be fixed by deferring theCallExp
sema until the arguments have been replaced by required temporaries (and hence possibly promoted from rvalues to lvalues).Previous lowering:
New: