I'm trying to figure out how to make my manually written containers have scope-aware element(s)-accessing functions. I've come up with 5 different situations as follows

@safe pure nothrow @nogc:

struct S(T)
{
    static private struct Range
    {
        S!T* _parent;
    }

    scope inout(Range) range() inout return
    {
        return typeof(return)(&this);
    }

    scope inout(T)[] opSlice() inout return
    {
        return x[];
    }

    scope inout(T)[] slice() inout return
    {
        return x[];
    }

    scope ref inout(T) front() inout return
    {
        return x[0];
    }

    scope inout(T)* pointer() inout return
    {
        return &x[0];
    }

    T[128] x;
}

/// this correctly fails
int[] testOpSlice()
{
    S!int s;
    return s[];                 // errors with -dip1000
}

/// this correctly fails
int[] testSlice()
{
    S!int s;
    return s.slice;             // errors with -dip1000
}

/// this correctly fails
auto testRange()
{
    S!int s;
    return s.range;             // errors with -dip1000
}

/// TODO this should fail
ref int testFront()
{
    S!int s;
    return s.front;             // should error with -dip1000
}

/// TODO this should fail
int* testPointer()
{
    S!int s;
    return s.pointer;           // should error with -dip1000
}

Compiling this with dmd version 2.076.0-b1 along with -dip25 and -dip1000 flags gives three errors:

test_scope.d(42,13): Error: returning `s.opSlice()` escapes a reference to local variable `s` test_scope.d(49,12): Error: returning `s.slice()` escapes a reference to local variable `s` test_scope.d(56,12): Error: returning `s.range()` escapes a reference to local variable `s`

It's very nice that the scope-analysis figures out that even the `range` member function contains an escaping pointer to the owning struct.

However, the other two `testFront` and `testPointer` don't error. Why are these two simpler cases allowed to escape a scoped reference and pointer which both outlive the lifetime of the owning struct `S`?

Reply via email to