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-2022 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://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 /*************************************
76 * Find the `alias this` symbol of e's type.
77 * Params:
78 * sc = context
79 * e = expression forming the `this`
80 * gag = if true do not print errors, return null instead
81 * findOnly = don't do further processing like resolving properties,
82 * i.e. just return plain dotExp() result.
83 * Returns:
84 * Expression that is `e.aliasthis`
85 */
86 Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
87 {
88 import dmd.typesem : dotExp;
89 for (AggregateDeclaration ad = isAggregate(e.type); ad;)
90 {
91 if (ad.aliasthis)
92 {
93 Loc loc = e.loc;
94 Type tthis = (e.op == EXP.type ? e.type : null);
95 const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0);
96 uint olderrors = gag ? global.startGagging() : 0;
97 e = dotExp(e.type, sc, e, ad.aliasthis.ident, flags);
98 if (!e || findOnly)
99 return gag && global.endGagging(olderrors) ? null : e;
100
101 if (tthis && ad.aliasthis.sym.needThis())
102 {
103 if (auto ve = e.isVarExp())
104 {
105 if (auto fd = ve.var.isFuncDeclaration())
106 {
107 // https://issues.dlang.org/show_bug.cgi?id=13009
108 // Support better match for the overloaded alias this.
109 bool hasOverloads;
110 if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads))
111 {
112 if (!hasOverloads)
113 fd = f; // use exact match
114 e = new VarExp(loc, fd, hasOverloads);
115 e.type = f.type;
116 e = new CallExp(loc, e);
117 goto L1;
118 }
119 }
120 }
121 /* non-@property function is not called inside typeof(),
122 * so resolve it ahead.
123 */
124 {
125 int save = sc.intypeof;
126 sc.intypeof = 1; // bypass "need this" error check
127 e = resolveProperties(sc, e);
128 sc.intypeof = save;
129 }
130 L1:
131 e = new TypeExp(loc, new TypeTypeof(loc, e));
132 e = e.expressionSemantic(sc);
133 }
134 e = resolveProperties(sc, e);
135 if (!gag)
136 ad.aliasthis.checkDeprecatedAliasThis(loc, sc);
137 else if (global.endGagging(olderrors))
138 e = null;
139 }
140
141 import dmd.dclass : ClassDeclaration;
142 auto cd = ad.isClassDeclaration();
143 if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
144 {
145 ad = cd.baseClass;
146 continue;
147 }
148 break;
149 }
150 return e;
151 }
152
153 /**
154 * Check if an `alias this` is deprecated
155 *
156 * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to
157 * check if `expression` uses a deprecated `aliasthis`, but this calls
158 * `toPrettyChars` which lead to the following message:
159 * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated"
160 *
161 * Params:
162 * at = The `AliasThis` object to check
163 * loc = `Loc` of the expression triggering the access to `at`
164 * sc = `Scope` of the expression
165 * (deprecations do not trigger in deprecated scopes)
166 *
167 * Returns:
168 * Whether the alias this was reported as deprecated.
169 */
checkDeprecatedAliasThis(AliasThis at,const ref Loc loc,Scope * sc)170 bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc)
171 {
172 import dmd.errors : deprecation, Classification;
173 import dmd.dsymbolsem : getMessage;
174
175 if (global.params.useDeprecated != DiagnosticReporting.off
176 && at.isDeprecated() && !sc.isDeprecated())
177 {
178 const(char)* message = null;
179 for (Dsymbol p = at; p; p = p.parent)
180 {
181 message = p.depdecl ? p.depdecl.getMessage() : null;
182 if (message)
183 break;
184 }
185 if (message)
186 deprecation(loc, "`alias %s this` is deprecated - %s",
187 at.sym.toChars(), message);
188 else
189 deprecation(loc, "`alias %s this` is deprecated",
190 at.sym.toChars());
191
192 if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
193 ti.printInstantiationTrace(Classification.deprecation);
194
195 return true;
196 }
197 return false;
198 }
199
200 /**************************************
201 * Check and set 'att' if 't' is a recursive 'alias this' type
202 * Params:
203 * att = type reference used to detect recursion
204 * t = 'alias this' type
205 *
206 * Returns:
207 * Whether the 'alias this' is recursive or not
208 */
isRecursiveAliasThis(ref Type att,Type t)209 bool isRecursiveAliasThis(ref Type att, Type t)
210 {
211 auto tb = t.toBasetype();
212 if (att && tb.equivalent(att))
213 return true;
214 else if (!att && tb.checkAliasThisRec())
215 att = tb;
216 return false;
217 }
218