Thread overview
[Issue 21471] Backend assertion triggered with `-checkation=context` and `-inline`
Feb 01, 2021
Dlang Bot
January 31, 2021
https://issues.dlang.org/show_bug.cgi?id=21471

moonlightsentinel@disroot.org changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |moonlightsentinel@disroot.o
                   |                            |rg

--- Comment #1 from moonlightsentinel@disroot.org ---
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).

--
January 31, 2021
https://issues.dlang.org/show_bug.cgi?id=21471

--- Comment #2 from moonlightsentinel@disroot.org ---
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

--
January 31, 2021
https://issues.dlang.org/show_bug.cgi?id=21471

--- Comment #3 from moonlightsentinel@disroot.org ---
(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

--
February 01, 2021
https://issues.dlang.org/show_bug.cgi?id=21471

Dlang Bot <dlang-bot@dlang.rocks> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|---                         |FIXED

--- Comment #4 from Dlang Bot <dlang-bot@dlang.rocks> ---
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

--