Bug 20752 – __traits(isReturnOnStack, func) is incomplete and can't be trusted

Status
NEW
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2020-04-20T02:08:44Z
Last change time
2024-12-13T19:08:12Z
Assigned to
No Owner
Creator
Mathias LANG
Moved to GitHub: dmd#19693 →

Comments

Comment #0 by pro.mathias.lang — 2020-04-20T02:08:44Z
Since half of the logic for NRVO is in the backend, this traits simply cannot be relied on and is mostly useless. For example, the following compiles and triggers the assert with DMD: ``` import std.meta; import std.traits; import std.stdio; T deserializeFull (T) (scope ubyte[] delegate(size_t) dg) { static if (is(T == ulong)) { ulong ret = *cast(ulong*)(dg(ulong.sizeof).ptr); return ret; } else static if (is(T == ulong*)) { return null; } else static if (is(T == struct)) { Target convert (Target) () { auto x = deserializeFull!Target(dg); return x; } auto enableNRVO = T(staticMap!(convert, Fields!T)); return enableNRVO; } else static assert(0); } struct FooBar { this(Repeat!(6, ulong) vals, ulong* ignored) { this.tupleof[0 .. vals.length] = vals; this.ptr = &this.a; } ulong a; ulong b; ulong c; ulong d; ulong e; ulong f; ulong* ptr; } struct WrapMe { FooBar fb; FooBar xc; } void main () { //FooBar f; ubyte[WrapMe.sizeof] binary = 42; ubyte[] tmp = binary; scope ubyte[] delegate(size_t v) getter = (v) { scope(success) tmp = tmp[v .. $]; return tmp[0 .. v]; }; auto final_ = deserializeFull!WrapMe(getter); writeln("ptr: ", final_.fb.ptr, " == ", &final_.fb.a); assert(final_.fb.ptr is &final_.fb.a); static assert(__traits(isReturnOnStack, deserializeFull!WrapMe)); } ``` LDC v1.20.1 compiles and run this just fine, but not DMD: ``` % dmd -run repro.d ptr: 7FFEE998E3D8 == 7FFEE998E520 [email protected](61): Assertion failure ---------------- ??:? _d_assertp [0x10628dcbd] ??:? _Dmain [0x10627192a] ``` I would expect a compilation error, or the assert to pass.
Comment #1 by ibuclaw — 2020-04-21T15:55:00Z
(In reply to Mathias LANG from comment #0) > Since half of the logic for NRVO is in the backend, this traits simply > cannot be relied on and is mostly useless. For finding out if a struct is to be returned using (N)RVO, the right test to use would be __traits(isPOD). If a struct is non-POD, then it should _always_ be passed around by invisible reference, no ifs, no buts, no excuses. However, that's ignoring static arrays, which is a little more hairy to find out, as they need to be big enough to be considered returning in memory. But just what is "big enough" is a very target-specific question. > For example, the following compiles and triggers the assert with DMD: > [--snip--] > > I would expect a compilation error, or the assert to pass. Worse, some back-ends can't provide any useful information as there's no access to "target". This is from the gdc-ddmd branch with included explanation. --- bool Target::isReturnOnStack (TypeFunction *, bool) { /* Need the back-end type to determine this, but this is called from the frontend before semantic processing is finished. An accurate value is not currently needed anyway. */ return true; } --- A type could be synthesized, however. But it would be a best guess approach.
Comment #2 by kinke — 2020-04-21T17:51:44Z
(In reply to Iain Buclaw from comment #1) > For finding out if a struct is to be returned using (N)RVO, the right test > to use would be __traits(isPOD). > > If a struct is non-POD, then it should _always_ be passed around by > invisible reference, no ifs, no buts, no excuses. > > However, that's ignoring static arrays, which is a little more hairy to find > out, as they need to be big enough to be considered returning in memory. > But just what is "big enough" is a very target-specific question. That goes for large structs too, not just static arrays, so isPOD is definitely not sufficient. The isReturnOnStack trait isn't directly related to NRVO and only depends on the ABI; in LLVM lingo, passing a pointer to the pre-allocated result is called sret, struct-return. NRVO just means that a named variable in the callee will be emplaced into that memory directly, so an sret ABI requirement for the callee is an orthogonal prerequisite for NRVO to work (in the callee).
Comment #3 by bugzilla — 2020-10-05T09:59:28Z
> the following compiles and triggers the assert with DMD: A much smaller example would be nice. For example, all those templates seem an unnecessary complication.
Comment #4 by robert.schadek — 2024-12-13T19:08:12Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19693 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB