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.
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
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.
(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.
https://github.com/dlang/dmd/pull/9081
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