Opportunities for D

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Wed Jul 9 16:57:01 PDT 2014


On Wed, Jul 09, 2014 at 03:16:37PM -0700, H. S. Teoh via Digitalmars-d wrote:
[...]
> 	https://issues.dlang.org/show_bug.cgi?id=13085
[...]

Hmm, apparently, this is a long-standing known issue:

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

Judging from this, a big missing piece of the current implementation is
the actual enforcement of 'scope'.

So here's a first stab at refining (and extending) what 'scope' should
be:

- 'scope' can be applied to any variable, and is part of its type. Let's
  call this a "scoped type", and a value of this type a "scoped value".

- Every scoped type has an associated lifetime, which is basically the
  scope in which it is declared.

   - The lifetime of a scoped variable is PART OF ITS TYPE.

   - An unscoped variable is regarded to have infinite lifetime.

   - For function parameters, this lifetime is the scope of the function
     body.

   - For local variables, the lifetime is the containing lexical scope
     where it is declared.

- Taking the address of a scoped value returns a scoped pointer, whose
  lifetime is the lexical scope where the address-of operator is used.

- A scoped type can only be assigned to another scoped type of identical
  or narrower lifetime.  Basically, the idea here is that a scoped value
  can only have its scope narrowed, never expanded. In practice, this
  means:

   - If a scoped type is a reference type (class or pointer or ref), it
     can only be assigned to another scoped type whose associated
     lifetime is equal or contained within the source value's associated
     lifetime.

   - If a scoped type is a value type with indirections, it can only be
     assigned to an lvalue of the same scoped type (with the same
     associated lifetime).

   - If a scoped type is a value type with no indirections, it's freely
     assignable to a non-scoped lvalue of compatible type.

   - A function's return type can be scoped (not sure what syntax to use
     here, since it may clash with scope delegates).

      - The lifetime of the return value is the containing scope of the
        function definition -- if it's a module-level function, then its
        lifetime is infinite. If it's an inner function, then its
        lifetime is the containing lexical scope of its definition.
        Example:

            class C {}
            void func() {
                // return type of helper is scope(C) with lifetime up to
                // the end of func's body.
                scope(C) helper() { ... }
            }

      - Returning a value from a function is considered to be equivalent
        to assigning the value to a variable of the return type of the
        function. Thus:

            class C {}
            void func() {
                scope(C) c1;

                // helper's return type has lifetime == func's body
                scope(C) helper() {
                    scope(C) c2;
                    if (cond)
                        return c1; // OK, c1's lifetime == func's body
                    else
                        return c2; // ILLEGAL: c2's lifetime < func's body
                }
            }

      - Since a scoped return type has its lifetime as part of its type,
        the type system ensures that scoped values never escape their
        lifetime. For example, if we are sneaky and return a pointer to
        an inner function, the type system will prevent leakage of the
        scoped value:

            class C {}
            auto func() {
                scope(C) c1;

                // Return type of sneaky is scope(C) with lifetime =
                // body of func.
                scope(C) sneaky() {
                    // This is OK, because c1's lifetime == body of
                    // func, which is compatible with its return type.
                    return c1;
                }

                // Aha! we now we have broken scope... or have we?
                return &sneaky;
            }

            void main() {
                // Let's see. Get a function pointer to a function that
                // "leaks" a scoped value...
                auto funcptr = func();

                // But this doesn't compile, because the return type of
                // funcptr() is a scoped variable whose lifetime is
                // inside the body of func(), but since we're outside of
                // func here, the lifetime of x doesn't match the
                // lifetime of funcptr()'s return value, so the
                // following assignment is rejected as having
                // incompatible types:
                auto x = funcptr();

                // This will actually work... but it's OK, because we
                // aren't actually storing the return value of
                // funcptr(), so the scoped value is actually not leaked
                // after all.
                funcptr();
            }

- Aggregates:

   - It's turtles all the way down: members of scoped aggregates also
     have scoped type, with lifetime inherited from the parent
     aggregate. In other words, the lifetime of the aggregate is
     transitive to the lifetime of its members. For example:

        class C {}
        struct S {
            C c;
            int x;
        }
        int func(scope S s) {
            // N.B. lifetime of s is func's body.
            auto c1 = s.c; // typeof(c1) == scope(C) with lifetime = func's body
            C d;
            d = c1; // illegal: c1 has shorter lifetime than d.
            return s.x; // OK, even though typeof(s.x) has lifetime =
                        // func's body, it's a value type so we're
                        // actually copying it to func's return value,
                        // not returning the actual scoped int.
        }

- Passing parameters: since unscoped values are regarded to have
  infinite lifetime, it's OK to pass unscoped values into scoped
  function parameters: it's a narrowing of lifetime of the original
  value, which is allowed. (What's not allowed is expanding the lifetime
  of a scoped value.)

I'm sure there are plenty of holes in this proposal, so destroy away.
;-)


T

-- 
If I were two-faced, would I be wearing this one? -- Abraham Lincoln


More information about the Digitalmars-d mailing list