Bug 21471 – Backend assertion triggered with `-checkation=context` and `-inline`
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2020-12-11T16:46:33Z
Last change time
2021-02-01T03:55:47Z
Keywords
backend, wrong-code
Assigned to
No Owner
Creator
Mathias LANG
Comments
Comment #0 by pro.mathias.lang — 2020-12-11T16:46:33Z
```
// REQUIRED_ARGS: -checkaction=context -inline
struct S
{
S get()() const { return this; }
}
auto f(S s) { return s; }
void main()
{
auto s = S();
assert(f(s.get) == s);
}
```
See https://github.com/dlang/dmd/pull/11925#issuecomment-722400704
Note that if the compiler wasn't compiler with asserts on, no failure happens, hence the "wrong-code" tag.
Comment #1 by moonlightsentinel — 2021-01-31T14:41:50Z
Another self-contained example (without druntime & explicit -inline):
============= object.d =================
void main()
{
auto c = 0;
assert(isClose(c, exp(1)));
}
pragma(inline, true)
real exp(real x) pure nothrow
{
return x;
}
bool isClose(int lhs, real rhs) pure nothrow
{
return false;
}
alias string = immutable(char)[];
string _d_assert_fail(A)(string, A);
===========================================
The assertion failure depends on three things:
1. generate debug infos (-g)
2. inlined function call as a paramter
3. node side effects => no temporary
For example:
assert(isClose(c, exp(1)));
is rewritten to
assert(isClose(c, exp(1)), _d_assert_fail!bool("", isClose(c, exp(1)));
and inlined to
assert(isClose(c, ((real x = 1.0L;) , x), 0), _d_assert_fail("", isClose(c, ((real x = 1.0L;) , x), 0)));
The problem are the two temporaries x, inserting the second x into the debug info triggers the assertion failure.
Debug generation fails only for `-checkaction=context`, manually compiling the lowered expression suceeds without errors.
This indicates that the failure is caused by -checkaction=context re-using the same AST node for the call to _d_assert_fail.
Copying the CallExp resolves this issue (not sure if that is the best solution).
Comment #2 by moonlightsentinel — 2021-01-31T14:54:51Z
Another solution would be to always create a temporary for CallExp.
This would also solve problems like this:
===========================================
extern(C) int puts(const scope char*);
void main()
{
assert(foo(1));
}
int foo(int i) pure nothrow
{
debug puts("Hello");
return i - 1;
}
============================================
Currently outputs:
Hello
Hello
Comment #3 by moonlightsentinel — 2021-01-31T14:57:52Z
dlang/dmd pull request #12170 "Fix 21471,21598 - Always create a temporary for calls with checkactio…" was merged into master:
- a956cc1f12ef4ea1f2294285bfab99734c65e446 by MoonlightSentinel:
Fix 21471,21598 - Always create a temporary for calls with checkaction=context
Using a temporary for every expression that calls another function
avoids problems with the inliner and avoids an unexpected secondary
function call for `pure` functions (which might have side effects
in `debug` blocks).
See [bugzilla](https://issues.dlang.org/show_bug.cgi?id=21471) for a
more complete writeup of the inliner issue.
https://github.com/dlang/dmd/pull/12170