1 /**
2  * Implements the `alias this` symbol.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/class.html#alias-this, Alias This)
5  *
6  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.d, _aliasthis.d)
10  * Documentation:  https://dlang.org/phobos/dmd_aliasthis.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aliasthis.d
12  */
13 
14 module dmd.aliasthis;
15 
16 import core.stdc.stdio;
17 import dmd.aggregate;
18 import dmd.dscope;
19 import dmd.dsymbol;
20 import dmd.expression;
21 import dmd.expressionsem;
22 import dmd.globals;
23 import dmd.identifier;
24 import dmd.mtype;
25 import dmd.opover;
26 import dmd.tokens;
27 import dmd.visitor;
28 
29 /***********************************************************
30  * alias ident this;
31  */
32 extern (C++) final class AliasThis : Dsymbol
33 {
34     Identifier ident;
35     /// The symbol this `alias this` resolves to
36     Dsymbol sym;
37     /// Whether this `alias this` is deprecated or not
38     bool isDeprecated_;
39 
this(const ref Loc loc,Identifier ident)40     extern (D) this(const ref Loc loc, Identifier ident)
41     {
42         super(loc, null);    // it's anonymous (no identifier)
43         this.ident = ident;
44     }
45 
syntaxCopy(Dsymbol s)46     override AliasThis syntaxCopy(Dsymbol s)
47     {
48         assert(!s);
49         auto at = new AliasThis(loc, ident);
50         at.comment = comment;
51         return at;
52     }
53 
kind()54     override const(char)* kind() const
55     {
56         return "alias this";
57     }
58 
isAliasThis()59     AliasThis isAliasThis()
60     {
61         return this;
62     }
63 
accept(Visitor v)64     override void accept(Visitor v)
65     {
66         v.visit(this);
67     }
68 
isDeprecated()69     override bool isDeprecated() const
70     {
71         return this.isDeprecated_;
72     }
73 }
74 
75 Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false)
76 {
77     for (AggregateDeclaration ad = isAggregate(e.type); ad;)
78     {
79         if (ad.aliasthis)
80         {
81             uint olderrors = gag ? global.startGagging() : 0;
82             Loc loc = e.loc;
83             Type tthis = (e.op == TOK.type ? e.type : null);
84             e = new DotIdExp(loc, e, ad.aliasthis.ident);
85             e = e.expressionSemantic(sc);
86             if (tthis && ad.aliasthis.sym.needThis())
87             {
88                 if (e.op == TOK.variable)
89                 {
90                     if (auto fd = (cast(VarExp)e).var.isFuncDeclaration())
91                     {
92                         // https://issues.dlang.org/show_bug.cgi?id=13009
93                         // Support better match for the overloaded alias this.
94                         bool hasOverloads;
95                         if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads))
96                         {
97                             if (!hasOverloads)
98                                 fd = f;     // use exact match
99                             e = new VarExp(loc, fd, hasOverloads);
100                             e.type = f.type;
101                             e = new CallExp(loc, e);
102                             goto L1;
103                         }
104                     }
105                 }
106                 /* non-@property function is not called inside typeof(),
107                  * so resolve it ahead.
108                  */
109                 {
110                     int save = sc.intypeof;
111                     sc.intypeof = 1; // bypass "need this" error check
112                     e = resolveProperties(sc, e);
113                     sc.intypeof = save;
114                 }
115             L1:
116                 e = new TypeExp(loc, new TypeTypeof(loc, e));
117                 e = e.expressionSemantic(sc);
118             }
119             e = resolveProperties(sc, e);
120             if (!gag)
121                 ad.aliasthis.checkDeprecatedAliasThis(loc, sc);
122             else if (global.endGagging(olderrors))
123                 e = null;
124         }
125 
126         import dmd.dclass : ClassDeclaration;
127         auto cd = ad.isClassDeclaration();
128         if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
129         {
130             ad = cd.baseClass;
131             continue;
132         }
133         break;
134     }
135     return e;
136 }
137 
138 /**
139  * Check if an `alias this` is deprecated
140  *
141  * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to
142  * check if `expression` uses a deprecated `aliasthis`, but this calls
143  * `toPrettyChars` which lead to the following message:
144  * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated"
145  *
146  * Params:
147  *   at  = The `AliasThis` object to check
148  *   loc = `Loc` of the expression triggering the access to `at`
149  *   sc  = `Scope` of the expression
150  *         (deprecations do not trigger in deprecated scopes)
151  *
152  * Returns:
153  *   Whether the alias this was reported as deprecated.
154  */
checkDeprecatedAliasThis(AliasThis at,const ref Loc loc,Scope * sc)155 bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc)
156 {
157     import dmd.errors : deprecation, Classification;
158     import dmd.dsymbolsem : getMessage;
159 
160     if (global.params.useDeprecated != DiagnosticReporting.off
161         && at.isDeprecated() && !sc.isDeprecated())
162     {
163         const(char)* message = null;
164         for (Dsymbol p = at; p; p = p.parent)
165         {
166             message = p.depdecl ? p.depdecl.getMessage() : null;
167             if (message)
168                 break;
169         }
170         if (message)
171             deprecation(loc, "`alias %s this` is deprecated - %s",
172                         at.sym.toChars(), message);
173         else
174             deprecation(loc, "`alias %s this` is deprecated",
175                         at.sym.toChars());
176 
177         if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
178             ti.printInstantiationTrace(Classification.deprecation);
179 
180         return true;
181     }
182     return false;
183 }
184 
185 /**************************************
186  * Check and set 'att' if 't' is a recursive 'alias this' type
187  * Params:
188  *   att = type reference used to detect recursion
189  *   t   = 'alias this' type
190  *
191  * Returns:
192  *   Whether the 'alias this' is recursive or not
193  */
isRecursiveAliasThis(ref Type att,Type t)194 bool isRecursiveAliasThis(ref Type att, Type t)
195 {
196     auto tb = t.toBasetype();
197     if (att && tb.equivalent(att))
198         return true;
199     else if (!att && tb.checkAliasThisRec())
200         att = tb;
201     return false;
202 }
203