DIP66

From D Wiki
Revision as of 14:59, 9 October 2014 by IgorStepanov (talk | contribs) (Multiple alias this)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Title: (Multiple) alias this
DIP: 66
Version: 1
Status: Draft
Created: 2014-10-09
Last Modified: 2014-10-19
Author: Igor Stepanov
Links:

Abstract

An AliasThis declaration names a member to subtype. Multiple AliasThis declarations are allowed. Order of AliasThis declarations does not matter.

Description

In the next code ...

    struct Foo
    {
        //...
        alias symbol this;
    }

... construction alias symbol this; means that anywhere when typeof(Foo.symbol) is needed, obj (object of type Foo) can be substituted with obj.symbol. This rule applies to implicit and explicit conversion, .member expression, operator overloading, foreach expression (foreach(args; obj)) et c. symbol can be field, or get-property (method which annotated with @property and takes zero paremeters). If there are many ways to resolve alias this, the compiler should raise an error.


    struct A
    {
        int i;
        alias i this;
    }

    struct B
    {
        int {| class="wikitable"i;
        alias i this;
    }

    struct C
    {
        A a;
        B b;

        alias a this;
        alias b this;
    }
    
    void test()
    {
        C c;
        int i = c; //Error: c.a.i vs c.b.i
    }
    
    static assert(is(C : int)); //Ok, because C is subtype of int anyway.

Semantic

Multiple alias this can cause conflicts. This chapter tells how compiler should resolve them. At the AliasThis declaration semantic stage, compiler can do the initial checks and reject the obviously incorrect AliasThis declarations.

    struct Test1
    {
        int a;
        int b;
        alias a this;
        alias b this; //Error: alias b this conflicts with alias a this;
    }
    class Test2a
    {
    }

    class Test2b : Test2a
    {
    }

    class Test2 : Test2b
    {
        Test2a a;
        alias a this; //Error: alias a this tries to hide inherited type Test2a; 
    }

The other checks will be done during alias this resolving stage. When compiler see ex(a) expression, it can resolve it as ex(a.aliasThisSymbol). (Hereinafter ex(a) means any case when alias this can be used: type conversion, .member expression, operator expression et c.) However compiler will try ex(a.aliasThisSymbol) only if another ways to resolve expression is wrong. E.g. while running semantic obj.member, the compiler will search for .member among obj members, baseclasses members, will try to use opDispatch and if all that fails, the compiler will try alias this. However alias this will be tried before UFCS resolving.

When compiler is trying to resolve alias this it iterates all alias this declarations and tries to apply it. If applying is successful, the compiler will add the result expression into the result set. If applying fails, the compiler tries to recursively resolve the alias this expression. The following pseudo-code illustrates this:

   resolveAliasThis(obj, ex):
       Set resultSet;
       foreach currentAliasThis in obj.aliasThisSymbols do
           if try(`ex(obj.currentAliasThis))` == Success then
               resultSet.add(`ex(obj.currentAliasThis)`)
           else
               resultSet.add(resolveAliasThis(`obj.currentAliasThis`, ex))
       if obj is class then
           foreach currentBaseClass in obj.baseClasses do
               resultSet.add(resolveAliasThis(`cast(currentBaseClass)obj`, ex))
       return resultSet

Finally, if resultSet contains only one candidate, the compiler will accept it. If resultSet is empty, compiler tries another ways to resolve ex(obj): UFCS et c. If resultSet contains more then one candidates, the compiler raises an error.

There are allowed recursive alias this:

    class A
    {
        C c;
        alias c this;
    }

    class B
    {
        A a;
        alias a this;
    }

    class C
    {
        B b;
        alias b this;
    }


For resolving this situation, resolveAliasThis function stores a set of types (visitedTypes), which can be visited higher in the call stack. If visitedTypes contains typeof(obj), compiler will not check obj subtypes.