D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 16652 - [Reg 2.071] returned rvalue destroyed too early
Summary: [Reg 2.071] returned rvalue destroyed too early
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P1 regression
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-11-01 00:57 UTC by Martin Nowak
Modified: 2018-12-16 20:54 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Martin Nowak 2016-11-01 00:57:44 UTC
cat > bug.d << CODE
import std.stdio;

struct Vector
{
    ubyte[] opSlice()
    {
        writeln("opslice");
        return buf[];
    }

    ~this()
    {
        writeln("dtor");
    }

    ubyte[4] buf;
}

void bar(ubyte[])
{
    writeln("bar");
}

void main()
{
    bar(Vector()[]);
}
CODE
dmd -inline -run bug
---
opslice
dtor <- !!! destroyed
bar <- stale reference
---

The order of evaluation changes when -inline is passed to the compiler. With that the destructor runs before the function call finishes, thus possibly passing a stale reference.

Also see https://github.com/rejectedsoftware/vibe.d/pull/1578 and https://github.com/etcimon/botan/issues/23.
Comment 1 Martin Nowak 2016-11-01 01:11:17 UTC
Turns out this is actually a regression introduced by https://github.com/dlang/dmd/pull/5292.

Also here is the test case with an assertion instead of writeln.

cat > bug.d << CODE
struct Vector
{
    this(ubyte a)
    {
        buf[] = a;
    }

    ubyte[] opSlice()
    {
        return buf[];
    }

    ~this()
    {
        buf[] = 0;
    }

    ubyte[4] buf;
}

void bar(ubyte[] v)
{
    assert(v[0] == 1);
}

void main()
{
    bar(Vector(1)[]);
}
CODE
dmd -inline -run bug
Comment 2 Walter Bright 2018-12-14 06:57:57 UTC
A further reduction:

struct Vector {
    this(ubyte a) {
	pragma(inline, false);
        buf = a;
    }

    ~this() {
	pragma(inline, false);
        buf = 0;
    }

    ubyte buf;
}

void bar(ubyte* v) {
    pragma(inline, true);
    assert(*v == 1);
}

void main() {
    bar(&Vector(1).buf);
}

It's the inlining of bar() that elicits the bug.
Comment 3 Walter Bright 2018-12-14 11:05:51 UTC
(In reply to Walter Bright from comment #2)
> It's the inlining of bar() that elicits the bug.

Looking at the AST for main() without inlining:

  _D4test3barFPhZv call  (dctor  info  ((__slVecto3 = 0) , (Vector.ctor call  (1  param  &__slVecto3))));
  ddtor (Vector.dtor call  &__slVecto);
  0L ;

and it looks correct. With inlining:


  v = (dctor info ((__slVecto3 =  0) , (Vector.ctor call  (1  param  &__slVecto3))));
  ddtor (Vector.dtor call  &__slVecto3);
  (*v == 1) || (_d_assertp call  (19L  param  #__a6_746573742e64));
  0L ;

and the destructor call is clearly too soon.
Comment 4 Walter Bright 2018-12-15 11:25:52 UTC
https://github.com/dlang/dmd/pull/9081
Comment 5 github-bugzilla 2018-12-16 20:54:11 UTC
Commits pushed to master at https://github.com/dlang/dmd

https://github.com/dlang/dmd/commit/535e8a4f47393fda584d315718833e504f4e69e7
fix Issue 16652 - [Reg 2.071] returned rvalue destroyed too early

https://github.com/dlang/dmd/commit/040320dbb881961ed1f00b4eac39f67400af1d02
Merge pull request #9081 from WalterBright/fix16652

fix Issue 16652 - [Reg 2.071] returned rvalue destroyed too early