www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 23305] New: Tuple.expand generates garbage values when passed

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: nobody puremagic.com
          Reporter: krzysztof.jajesnica 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.

--
Aug 25 2022