www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 21626] New: foreach create reference to rvalue tuple returned

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

          Issue ID: 21626
           Summary: foreach create reference to rvalue tuple returned by
                    front
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: major
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: submada gmail.com

If range return rvalue tuple from front and foreach unpack tuple then temporary
value to front is ref and after leaving scope destructor is not called:

import std.range;
import std.stdio;
import std.typecons;

int created, destroyed;

///struct count destructions and constructions
struct S{
    int val;

     disable this();

    this(int val) {
        this.val = val;
        created++;
    }

    this(ref inout const typeof(this) rhs)inout {
        this.val = rhs.val;
        created++;
    }

    ~this() {
        destroyed++;
    }
}

//range, for each call of front return rvalue Tuple!(S, S)
struct Generator(T){
    int n;
    this(int n){
        this.n = n;
    }

     property Tuple!(T, T) front(){
        return tuple(T(n), T(n));
    }

     property void popFront(){
        n -= 1;
    }

     property bool empty()const{
        return n < 1;
    }

}

///This work OK:
void test_A(){
    foreach(s; Generator!S(10)){
        assert(s[0].val == s[1].val);

    }

    assert(created == destroyed);
}

///This fail:
void test_B(){

    foreach(s1, s2; Generator!S(10)){
        assert(s1.val == s2.val);

    }

    writeln(": created ", created, " vs destroyed ", destroyed);   ///created
!= destroyed

    assert(created == destroyed);       ///FAIL!!
}


void main() {
        test_A();
        test_B();
}


test_B AST:

void test_B()
{
        {
                Generator!(S) __r101 = __r101 = 0 , __r101.this(10);
                for (; !__r101.empty(); __r101.popFront())
                {
                        ref Tuple!(S, S) __front102 = __r101.front();  
///PROBLEM!  reference to rvalue, destructor is not called!
                        ref S s1 = __front102.__expand_field_0;
                        ref S s2 = __front102.__expand_field_1;
                        assert(s1.val == s2.val);
                }
        }
        writeln(": created ", created, " vs destroyed ", destroyed);
        assert(created == destroyed);
}

--
Feb 10 2021