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
(In reply to moonlightsentinel from comment #2) > This would also solve problems like this: Reported as https://issues.dlang.org/show_bug.cgi?id=21598
Comment #4 by dlang-bot — 2021-02-01T03:55:47Z
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