www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - segfault in ldc release only - looks like some kind of optimization

reply aliak <something something.com> writes:
Hi,

so I had this weird bug that was driving me crazy and only 
segfaulted with ldc in release build - (I'm using ldc 1.16.0).

This is the code that segfaults. All parts seem to be necessary 
for it to happen, or at least I think so. I've gone in circles 
minimizing so I've probably missed something. But this seems to 
be it:

import std;

struct W(T) {
     T value;
     ref inout(T) front() inout { return value; }
}

auto ref get(T)(W!T value) {
     return value.front;
}

struct S {
     string a;
}

void main() {
     S[string] aa;
     aa = ["one" : S("a")];
     auto b = W!S(aa["one"]).get;
     writeln(b);
}

Running with ldc and -O3 crashes: "ldc2 -O3 -run source.d"

Some things I've noticed:
- if you remove the call to .get and use .value directly, the 
crash goes
- if you remove the inout specifier on front(), the crash goes
- if you remove the ref specifier on front(), the crash goes
- if you don't call writeln(b), the crash goes
- if you don't use the s returned by the aa, the crash goes
- if you *add* a return qualifier on the "front" function, the 
crash goes away

(what is that return thing btw and when do you use it?)

Any ideas?
Jul 22 2019
parent reply Exil <Exil gmall.com> writes:
auto ref get(T)(W!T value) {
     return value.front;
}

You're returning a reference to a temporary that gets deleted at 
the end of the function's scope. The "auto ref" here will be a 
"ref".
Jul 22 2019
parent reply aliak <something something.com> writes:
On Tuesday, 23 July 2019 at 00:36:49 UTC, Exil wrote:
 auto ref get(T)(W!T value) {
     return value.front;
 }

 You're returning a reference to a temporary that gets deleted 
 at the end of the function's scope. The "auto ref" here will be 
 a "ref".
..... oh ... shit.... you're right. Ok so this was minimized from this: const config = Config.ghApp(ghDomain) .orElseThrow!(() => new Exception( "could not find config for domain '%s'".format(ghDomain) )); Where Config.ghApp return an Optional!GhApp, and orElseThrow checks if a range has is not empty and returns front. The front in Optional is defined as the front above... So is that an incorrect idiom to use when writing a library then? I pretty sure I've seen it in phobos too. Slapping return on the function also fixes it. Is that the correct way to write a .front? Thanks!
Jul 22 2019
parent Exil <Exil gmall.com> writes:
On Tuesday, 23 July 2019 at 00:54:08 UTC, aliak wrote:
 On Tuesday, 23 July 2019 at 00:36:49 UTC, Exil wrote:
 auto ref get(T)(W!T value) {
     return value.front;
 }

 You're returning a reference to a temporary that gets deleted 
 at the end of the function's scope. The "auto ref" here will 
 be a "ref".
..... oh ... shit.... you're right. Ok so this was minimized from this: const config = Config.ghApp(ghDomain) .orElseThrow!(() => new Exception( "could not find config for domain '%s'".format(ghDomain) )); Where Config.ghApp return an Optional!GhApp, and orElseThrow checks if a range has is not empty and returns front. The front in Optional is defined as the front above... So is that an incorrect idiom to use when writing a library then? I pretty sure I've seen it in phobos too. Slapping return on the function also fixes it. Is that the correct way to write a .front? Thanks!
Yes you can use "return". It basically tells the compiler that the function or method returns something that is referenced by a passed in parameter so to keep it alive. https://dlang.org/spec/function.html#return-ref-parameters Your orElseThrow() probably shouldn't be taking in a copy though. That's the catch, then you can't use it in a UFCS chain like you are using now. Using "return" is not an ideal fix, as it is still going to be calling the destructor on the object. So you might run into an issue somewhere. It just so happens to generate assembly that works though I guess when there is no destructor.
Jul 22 2019