Home | History | Annotate | Line # | Download | only in dmd
      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 
     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 
     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 
     54     override const(char)* kind() const
     55     {
     56         return "alias this";
     57     }
     58 
     59     AliasThis isAliasThis()
     60     {
     61         return this;
     62     }
     63 
     64     override void accept(Visitor v)
     65     {
     66         v.visit(this);
     67     }
     68 
     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  */
    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  */
    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