blob: ef839fae536aabea302c4d55fc7fb34b4cf5fe0a [file] [log] [blame]
/**
* 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;
}