| /** | |
| * Implements the `alias this` symbol. | |
| * | |
| * Specification: $(LINK2 https://dlang.org/spec/class.html#alias-this, Alias This) | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.d, _aliasthis.d) | |
| * Documentation: https://dlang.org/phobos/dmd_aliasthis.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aliasthis.d | |
| */ | |
| module dmd.aliasthis; | |
| import core.stdc.stdio; | |
| import dmd.aggregate; | |
| import dmd.dscope; | |
| import dmd.dsymbol; | |
| import dmd.expression; | |
| import dmd.expressionsem; | |
| import dmd.globals; | |
| import dmd.identifier; | |
| import dmd.mtype; | |
| import dmd.opover; | |
| import dmd.tokens; | |
| import dmd.visitor; | |
| /*********************************************************** | |
| * alias ident this; | |
| */ | |
| extern (C++) final class AliasThis : Dsymbol | |
| { | |
| Identifier ident; | |
| /// The symbol this `alias this` resolves to | |
| Dsymbol sym; | |
| /// Whether this `alias this` is deprecated or not | |
| bool isDeprecated_; | |
| extern (D) this(const ref Loc loc, Identifier ident) | |
| { | |
| super(loc, null); // it's anonymous (no identifier) | |
| this.ident = ident; | |
| } | |
| override AliasThis syntaxCopy(Dsymbol s) | |
| { | |
| assert(!s); | |
| auto at = new AliasThis(loc, ident); | |
| at.comment = comment; | |
| return at; | |
| } | |
| override const(char)* kind() const | |
| { | |
| return "alias this"; | |
| } | |
| AliasThis isAliasThis() | |
| { | |
| return this; | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| override bool isDeprecated() const | |
| { | |
| return this.isDeprecated_; | |
| } | |
| } | |
| /************************************* | |
| * Find the `alias this` symbol of e's type. | |
| * Params: | |
| * sc = context | |
| * e = expression forming the `this` | |
| * gag = if true do not print errors, return null instead | |
| * findOnly = don't do further processing like resolving properties, | |
| * i.e. just return plain dotExp() result. | |
| * Returns: | |
| * Expression that is `e.aliasthis` | |
| */ | |
| Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false) | |
| { | |
| import dmd.typesem : dotExp; | |
| for (AggregateDeclaration ad = isAggregate(e.type); ad;) | |
| { | |
| if (ad.aliasthis) | |
| { | |
| Loc loc = e.loc; | |
| Type tthis = (e.op == EXP.type ? e.type : null); | |
| const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0); | |
| uint olderrors = gag ? global.startGagging() : 0; | |
| e = dotExp(e.type, sc, e, ad.aliasthis.ident, flags); | |
| if (!e || findOnly) | |
| return gag && global.endGagging(olderrors) ? null : e; | |
| if (tthis && ad.aliasthis.sym.needThis()) | |
| { | |
| if (auto ve = e.isVarExp()) | |
| { | |
| if (auto fd = ve.var.isFuncDeclaration()) | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=13009 | |
| // Support better match for the overloaded alias this. | |
| bool hasOverloads; | |
| if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads)) | |
| { | |
| if (!hasOverloads) | |
| fd = f; // use exact match | |
| e = new VarExp(loc, fd, hasOverloads); | |
| e.type = f.type; | |
| e = new CallExp(loc, e); | |
| goto L1; | |
| } | |
| } | |
| } | |
| /* non-@property function is not called inside typeof(), | |
| * so resolve it ahead. | |
| */ | |
| { | |
| int save = sc.intypeof; | |
| sc.intypeof = 1; // bypass "need this" error check | |
| e = resolveProperties(sc, e); | |
| sc.intypeof = save; | |
| } | |
| L1: | |
| e = new TypeExp(loc, new TypeTypeof(loc, e)); | |
| e = e.expressionSemantic(sc); | |
| } | |
| e = resolveProperties(sc, e); | |
| if (!gag) | |
| ad.aliasthis.checkDeprecatedAliasThis(loc, sc); | |
| else if (global.endGagging(olderrors)) | |
| e = null; | |
| } | |
| import dmd.dclass : ClassDeclaration; | |
| auto cd = ad.isClassDeclaration(); | |
| if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object) | |
| { | |
| ad = cd.baseClass; | |
| continue; | |
| } | |
| break; | |
| } | |
| return e; | |
| } | |
| /** | |
| * Check if an `alias this` is deprecated | |
| * | |
| * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to | |
| * check if `expression` uses a deprecated `aliasthis`, but this calls | |
| * `toPrettyChars` which lead to the following message: | |
| * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated" | |
| * | |
| * Params: | |
| * at = The `AliasThis` object to check | |
| * loc = `Loc` of the expression triggering the access to `at` | |
| * sc = `Scope` of the expression | |
| * (deprecations do not trigger in deprecated scopes) | |
| * | |
| * Returns: | |
| * Whether the alias this was reported as deprecated. | |
| */ | |
| bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) | |
| { | |
| import dmd.errors : deprecation, Classification; | |
| import dmd.dsymbolsem : getMessage; | |
| if (global.params.useDeprecated != DiagnosticReporting.off | |
| && at.isDeprecated() && !sc.isDeprecated()) | |
| { | |
| const(char)* message = null; | |
| for (Dsymbol p = at; p; p = p.parent) | |
| { | |
| message = p.depdecl ? p.depdecl.getMessage() : null; | |
| if (message) | |
| break; | |
| } | |
| if (message) | |
| deprecation(loc, "`alias %s this` is deprecated - %s", | |
| at.sym.toChars(), message); | |
| else | |
| deprecation(loc, "`alias %s this` is deprecated", | |
| at.sym.toChars()); | |
| if (auto ti = sc.parent ? sc.parent.isInstantiated() : null) | |
| ti.printInstantiationTrace(Classification.deprecation); | |
| return true; | |
| } | |
| return false; | |
| } | |
| /************************************** | |
| * Check and set 'att' if 't' is a recursive 'alias this' type | |
| * Params: | |
| * att = type reference used to detect recursion | |
| * t = 'alias this' type | |
| * | |
| * Returns: | |
| * Whether the 'alias this' is recursive or not | |
| */ | |
| bool isRecursiveAliasThis(ref Type att, Type t) | |
| { | |
| auto tb = t.toBasetype(); | |
| if (att && tb.equivalent(att)) | |
| return true; | |
| else if (!att && tb.checkAliasThisRec()) | |
| att = tb; | |
| return false; | |
| } |