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