Difference between revisions of "DIP66"

From D Wiki
Jump to: navigation, search
(Small rephrasing)
(Grammar and fluency edits)
Line 76: Line 76:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Semantic==
+
==Semantics==
  
Multiple alias this can cause conflicts. This chapter tells how compiler should resolve them.
+
Multiple <code>alias this</code> can cause conflicts. This section explains how the compiler should resolve them.
At the AliasThis declaration semantic stage, compiler can do the initial checks and reject the obviously incorrect AliasThis declarations.
+
At the AliasThis declaration semantic stage, compiler can perform the initial checks and reject the obviously incorrect AliasThis declarations.
  
 
<syntaxhighlight lang=D>
 
<syntaxhighlight lang=D>
Line 87: Line 87:
 
         int b;
 
         int b;
 
         alias a this;
 
         alias a this;
         alias b this; //Error: alias b this conflicts with alias a this;
+
         alias b this; // Error: alias b this conflicts with alias a this;
 
     }
 
     }
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 107: Line 107:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The other checks will be done during alias this resolving stage.
+
The other checks will be done when <code>alias this</code> is needed for typing expressions.
When compiler see ex(a) expression, it can resolve it as ex(a.aliasThisSymbol).
+
When the compiler types an expression such as <code>fun(a)</code>, it can resolve it as <code>fun(a.aliasThisSymbol)</code>.
(Hereinafter ex(a) means any case when alias this can be used: type conversion, <code>.member</code> expression, operator expression et c.)
+
(Hereinafter <code>fun(a)</code> means any case when <code>alias this</code> can be used: type conversion, <code>.member</code> expression, operator expression etc.)
However compiler will try ex(a.aliasThisSymbol) only if another ways to resolve expression is wrong.
+
However compiler will try <code>fun(a.aliasThisSymbol)</code> only if the expression cannot be typed otherwise.
E.g. while running semantic <code>obj.member</code>, the compiler will search for <code>.member</code> among obj members, baseclasses members, will try to use opDispatch and if all that fails, the compiler will try alias this.
+
E.g. while running semantic <code>obj.member</code>, the compiler will search for <code>.member</code> among <code>obj</code>'s members, base class members, will try to use <code>opDispatch</code> and if all that fails, the compiler will try <code>alias this</code>.
However alias this will be tried before UFCS resolving.
+
However <code>alias this</code> 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:
+
When the compiler is trying to resolve <code>alias this</code> it iterates all <code>alias this</code> declarations and tries to apply each. For each successful application, the compiler adds the result expression into a result set. If application fails, the compiler tries to recursively resolve the <code>alias this</code> expression. The following pseudo-code illustrates this:
  
 
<code>
 
<code>
Line 134: Line 134:
 
If resultSet contains more then one candidates, the compiler raises an error.
 
If resultSet contains more then one candidates, the compiler raises an error.
  
There are allowed recursive alias this:
+
Recursive <code>alias this</code> may occur:
  
 
<syntaxhighlight lang=D>
 
<syntaxhighlight lang=D>
Line 157: Line 157:
  
  
For resolving this situation, resolveAliasThis function stores a set of types (<code>visitedTypes</code>), which can be visited higher in the call stack. If visitedTypes contains typeof(obj), compiler will not check obj subtypes.
+
For resolving this situation, the resolveAliasThis function stores a set of types (<code>visitedTypes</code>), which can be visited higher in the call stack. If visitedTypes contains <code>typeof(obj)</code>, compiler will not check <code>obj</code>'s subtypes.
  
When compiler resolves binary expressions, where both args can have a alias this declaraions, compiler proceeds as follows:
+
When compiler resolves binary expressions, where both arguments have a alias this declarations, compiler proceeds as follows:
At the first stage compiler tries to resolve alias this only for one term:
+
At the first stage compiler tries to resolve <code>alias this</code> only for one term:
binex(a, b) -> binex(a.aliasthis, b)
+
<code>binex(a, b) -> binex(a.aliasthis, b)</code>
binex(a, b) -> binex(a, b.aliasthis)
+
<code>binex(a, b) -> binex(a, b.aliasthis)</code>
  
 
If there is only one candidate, compiler chooses it, if there are many candidates, compiler raises an error.
 
If there is only one candidate, compiler chooses it, if there are many candidates, compiler raises an error.
 
If there isn't candidates, compiler tries to resolve both terms:
 
If there isn't candidates, compiler tries to resolve both terms:
 
binex(a, b) -> binex(a.aliasthis, b.aliasthis)
 
binex(a, b) -> binex(a.aliasthis, b.aliasthis)
If there is only one candidate, compiler chooses it, if there are many candidates, compiler raises an error.
+
If there is only one candidate, compiler chooses it. If there are several candidates, compiler raises an error.
  
 
[[Category: DIP]]
 
[[Category: DIP]]

Revision as of 15:16, 22 October 2014

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 code below...

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

... the construction alias symbol this; means that wherever 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 access expression, operator overloading, foreach expression (foreach(args; obj)) etc. symbol can be a field or a get-property (method annotated with @property and taking zero parameters). If more than one alias this can be used to solve the same lookup, the compiler should raise an error.

    struct A
    {
        int i;
        alias i this;
    }

    struct B
    {
        int 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.

Semantics

Multiple alias this can cause conflicts. This section explains how the compiler should resolve them. At the AliasThis declaration semantic stage, compiler can perform 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 when alias this is needed for typing expressions. When the compiler types an expression such as fun(a), it can resolve it as fun(a.aliasThisSymbol). (Hereinafter fun(a) means any case when alias this can be used: type conversion, .member expression, operator expression etc.) However compiler will try fun(a.aliasThisSymbol) only if the expression cannot be typed otherwise. E.g. while running semantic obj.member, the compiler will search for .member among obj's members, base class 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 the compiler is trying to resolve alias this it iterates all alias this declarations and tries to apply each. For each successful application, the compiler adds the result expression into a result set. If application 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.

Recursive alias this may occur:

    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, the 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's subtypes.

When compiler resolves binary expressions, where both arguments have a alias this declarations, compiler proceeds as follows: At the first stage compiler tries to resolve alias this only for one term: binex(a, b) -> binex(a.aliasthis, b) binex(a, b) -> binex(a, b.aliasthis)

If there is only one candidate, compiler chooses it, if there are many candidates, compiler raises an error. If there isn't candidates, compiler tries to resolve both terms: binex(a, b) -> binex(a.aliasthis, b.aliasthis) If there is only one candidate, compiler chooses it. If there are several candidates, compiler raises an error.