www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 23891] New: [DIP1000] delegate sometimes ignores lifetimes

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

          Issue ID: 23891
           Summary: [DIP1000] delegate sometimes ignores lifetimes
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: major
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: qs.il.paperinik gmail.com

It seems that when a delegate is invoked, lifetime checks are not done, unless
the delegate is directly named. (Usually, that is the case when the delegate is
an rvalue, but if an expression returns a delegate by reference, which makes it
an lvalue, lifetime checks are not done.

This does not apply to function pointers, function pointers work as expected,
including those returned by a delegate.

Here are some test cases that demonstrate the claims:

```d
struct S
{
    int value;
    ref int member(int x) return  safe => value;
}
ref int func(return ref S s)  safe => s.member(0);

// THOSE FAIL (AND SHOULD)
ref int h1(ref S s)  safe => return func(s);
ref int h2(ref S s)  safe => return (&func)(s);
ref int h3(ref S s)  safe
{
    static f() => &func;
    return f()(s); // rvalue function pointer

    alias Func = ref int function(return ref S s)  safe;
    alias F = Func function() pure nothrow  nogc  safe;
    static assert(is(typeof(&f) == F));
}
ref int h4(ref S s)  safe
{
    auto fptr = &func;
    ref dg() => fptr;
    return dg()(s); // unnamed lvalue function pointer

    alias Func = ref int function(return ref S s)  safe;
    alias DG = ref Func delegate() pure nothrow  nogc  safe;
    static assert(is(typeof(&dg) == DG));
}

// THOSE FAIL (AND SHOULD)
ref int f1(ref S s)  safe => s.member(0);
ref int f2(ref S s)  safe => (__traits(child, s, S.member))(0);
ref int f3(ref S s)  safe
{
    auto dg = &s.member;
    return dg(0);
}
ref int f4(ref S s)  safe
{
    auto dg = &__traits(child, s, S.member);
    return dg(0);
}
ref int f5(ref S s)  safe
{
    static f(ref S s) => &s.member;
    auto dg = f(s);
    return dg(0); // Note: dg is an lvalue delegate (cf. g4)
}

// THOSE PASS AND SHOULD NOT
ref int g1(ref S s)  safe => (&s.member)(0);
ref int g2(ref S s)  safe => (&__traits(child, s, S.member))(0);
ref int g3(ref S s)  safe
{
    auto dg = &s.member;
    auto f = ref () => dg;
    // f() is not an rvalue:
    static assert(__traits(compiles, f() = null));
    return f()(0);
}
ref int g4(ref S s)  safe
{
    static f(ref S s) => &s.member;
        return f(s)(0); // Note: f(s) is an rvalue delegate

    // f(s) is of a delegate type that has "return" on it:
    alias DG = ref int delegate(int x) return  safe;
    static assert(is(typeof(f(s)) == DG));

    // f is of type "function that returns DG":
    alias F = DG function(ref S s) pure nothrow  nogc  safe;
    static assert(is(typeof(&f) == F));
}
```

--
May 05 2023