/** * Implements the `alias this` symbol. * * Specification: $(LINK2 https://dlang.org/spec/class.html#alias-this, Alias This) * * Copyright: Copyright (C) 1999-2023 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.location; 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(ad.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; }