https://issues.dlang.org/show_bug.cgi?id=23305

          Issue ID: 23305
           Summary: Tuple.expand generates garbage values when passed to
                    multiple lazy parameters
           Product: D
           Version: D2
          Hardware: x86_64
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P1
         Component: dmd
          Assignee: nob...@puremagic.com
          Reporter: krzysztof.jajesn...@student.put.poznan.pl

import std.stdio;
import std.typecons;

auto foo(lazy int a, lazy int b, lazy int c, lazy int d) {
    return tuple(d, c, b, a);
}

void main() {
    auto r = foo(tuple(3,4,5,6).expand);
    writeln(r);
}

This code prints "Tuple!(int,int,int,int)(0, 0, <random garbage>, 3)" on my
machine (DMD 2.100.0) and gives similar results on run.dlang.io (DMD 2.099.1).

The bug only occurs if Tuple is obtained directly from a function call - using
a Tuple stored in a variable works fine:

// this works
auto t = tuple(3,4,5,6);
auto r = foo(t.expand);

The bug also occurs with any struct/class declaring multiple fields using an
AliasSeq of types, not just std.typecons.Tuple:

// also affected 
struct vec2 {
    AliasSeq!(float,float) expand;
}

Looking at output of -vcg-ast it seems that when a Tuple is obtained directly
from a function call and .expand is passed to multiple lazy parameters, the
Tuple is put in a local variable inside the body of the delegate generated for
the first parameter, and the other delegates reuse the same variable. I'm
assuming this causes the other delegates to read whatever is in the same
location on the stack, hence semi-random garbage.

void main()
{
    Tuple!(int, int, int, int) r = foo(
        delegate int() pure nothrow @nogc @safe => 
            (Tuple!(int, int, int, int) __tup82 = tuple(3, 4, 5, 6);) , 
            __tup82.__expand_field_0, 
        delegate int() pure nothrow @nogc @safe => __tup82.__expand_field_1, 
        delegate int() pure nothrow @nogc @safe => __tup82.__expand_field_2, 
        delegate int() pure nothrow @nogc @safe => __tup82.__expand_field_3
    );
    writeln(r);
    return 0;
}

For reference, LDC (1.30.0) refuses to compile foo(tuple(3,4,5,6).expand) and
gives the following error message:

app.d(9): Error: function `app.main.__dgliteral3` cannot access frame of
function `app.main.__dgliteral2`
app.d(9): Error: function `app.main.__dgliteral4` cannot access frame of
function `app.main.__dgliteral2`
app.d(9): Error: function `app.main.__dgliteral5` cannot access frame of
function `app.main.__dgliteral2`

GDC (12.1.1) also gives a similar error message.

--

Reply via email to