dmd/compiler/src/dmd/dsymbolsem.d
2025-04-15 20:22:18 +08:00

9065 lines
316 KiB
D

/**
* Does the semantic 1 pass on the AST, which looks at symbol declarations but not initializers
* or function bodies.
*
* Copyright: Copyright (C) 1999-2025 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/compiler/src/dmd/dsymbolsem.d, _dsymbolsem.d)
* Documentation: https://dlang.org/phobos/dmd_dsymbolsem.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dsymbolsem.d
*/
module dmd.dsymbolsem;
import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
import dmd.aliasthis;
import dmd.arraytypes;
import dmd.astcodegen;
import dmd.astenums;
import dmd.attrib;
import dmd.attribsem;
import dmd.clone;
import dmd.cond;
import dmd.timetrace;
import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.deps;
import dmd.dimport;
import dmd.dinterpret;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.dversion;
import dmd.enumsem;
import dmd.errors;
import dmd.escape;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.funcsem;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.importc;
import dmd.init;
import dmd.initsem;
import dmd.intrange;
import dmd.hdrgen;
import dmd.lexer;
import dmd.location;
import dmd.mtype;
import dmd.mustuse;
import dmd.nspace;
import dmd.objc;
import dmd.opover;
import dmd.optimize;
import dmd.parse;
debug import dmd.printast;
import dmd.root.array;
import dmd.root.filename;
import dmd.root.string;
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.rootobject;
import dmd.safe;
import dmd.semantic2;
import dmd.semantic3;
import dmd.sideeffect;
import dmd.staticassert;
import dmd.tokens;
import dmd.utils;
import dmd.statement;
import dmd.target;
import dmd.templateparamsem;
import dmd.templatesem;
import dmd.typesem;
import dmd.visitor;
version (IN_GCC) {}
else version (IN_LLVM) {}
else version = MARS;
enum LOG = false;
/*************************************
* Does semantic analysis on the public face of declarations.
*/
void dsymbolSemantic(Dsymbol dsym, Scope* sc)
{
scope v = new DsymbolSemanticVisitor(sc);
dsym.accept(v);
}
/***************************************************
* Determine the numerical value of the AlignmentDeclaration
* Params:
* ad = AlignmentDeclaration
* sc = context
* Returns:
* ad with alignment value determined
*/
AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
{
if (!ad.salign.isUnknown()) // UNKNOWN is 0
return ad;
if (!ad.exps)
{
ad.salign.setDefault();
return ad;
}
dinteger_t strictest = 0; // strictest alignment
bool errors;
foreach (ref exp; (*ad.exps)[])
{
sc = sc.startCTFE();
auto e = exp.expressionSemantic(sc);
e = resolveProperties(sc, e);
sc = sc.endCTFE();
e = e.ctfeInterpret();
exp = e; // could be re-evaluated if exps are assigned to more than one AlignDeclaration by CParser.applySpecifier(),
// e.g. `_Alignas(8) int a, b;`
if (e.op == EXP.error)
errors = true;
else
{
auto n = e.toInteger();
if (sc.inCfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
continue;
if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isIntegral())
{
error(ad.loc, "alignment must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
errors = true;
}
if (n > strictest) // C11 6.7.5-6
strictest = n;
}
}
if (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect
ad.salign.setDefault();
else
ad.salign.set(cast(uint) strictest);
return ad;
}
const(char)* getMessage(DeprecatedDeclaration dd)
{
if (auto sc = dd._scope)
{
dd._scope = null;
sc = sc.startCTFE();
dd.msg = dd.msg.expressionSemantic(sc);
dd.msg = resolveProperties(sc, dd.msg);
sc = sc.endCTFE();
dd.msg = dd.msg.ctfeInterpret();
if (auto se = dd.msg.toStringExp())
dd.msgstr = se.toStringz().ptr;
else
error(dd.msg.loc, "compile time constant expected, not `%s`", dd.msg.toChars());
}
return dd.msgstr;
}
bool checkDeprecated(Dsymbol d, Loc loc, Scope* sc)
{
if (global.params.useDeprecated == DiagnosticReporting.off)
return false;
if (!d.isDeprecated())
return false;
// Don't complain if we're inside a deprecated symbol's scope
if (sc.isDeprecated())
return false;
// Don't complain if we're inside a template constraint
// https://issues.dlang.org/show_bug.cgi?id=21831
if (sc.inTemplateConstraint)
return false;
const(char)* message = null;
for (Dsymbol p = d; p; p = p.parent)
{
message = p.depdecl ? p.depdecl.getMessage() : null;
if (message)
break;
}
if (message)
deprecation(loc, "%s `%s` is deprecated - %s", d.kind, d.toPrettyChars, message);
else
deprecation(loc, "%s `%s` is deprecated", d.kind, d.toPrettyChars);
if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
ti.printInstantiationTrace(Classification.deprecation);
else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null)
ti.printInstantiationTrace(Classification.deprecation);
return true;
}
/*********************************
* Check type to see if it is based on a deprecated symbol.
*/
private void checkDeprecated(Type type, Loc loc, Scope* sc)
{
if (Dsymbol s = type.toDsymbol(sc))
{
s.checkDeprecated(loc, sc);
}
if (auto tn = type.nextOf())
tn.checkDeprecated(loc, sc);
}
// Returns true if a contract can appear without a function body.
package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
{
assert(!funcdecl.fbody);
/* Contracts can only appear without a body when they are virtual
* interface functions or abstract.
*/
Dsymbol parent = funcdecl.toParent();
InterfaceDeclaration id = parent.isInterfaceDeclaration();
if (!funcdecl.isAbstract() &&
(funcdecl.fensures || funcdecl.frequires) &&
!(id && funcdecl.isVirtual()))
{
auto cd = parent.isClassDeclaration();
if (!(cd && cd.isAbstract()))
return false;
}
return true;
}
/*
If sd has a copy constructor and ctor is an rvalue constructor,
issue an error.
Params:
sd = struct declaration that may contain both an rvalue and copy constructor
ctor = constructor that will be checked if it is an rvalue constructor
ti = template instance the ctor is part of
Return:
`true` if sd has a copy constructor and ctor is an rvalue constructor
*/
bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
{
//printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
/* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
* so use isRvalueConstructor()
*/
if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
{
.error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
.errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`",
ti.toPrettyChars(), sd.toChars());
return true;
}
return false;
}
/************************************************
* Check if ctor is an rvalue constructor.
* A constructor that receives a single parameter of the same type as
* `Unqual!typeof(this)` is an rvalue constructor.
* Params:
* sd = struct that ctor is a member of
* ctor = constructor to test
* Returns:
* true if it is an rvalue constructor
*/
bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
{
// note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
auto tf = ctor.type.isTypeFunction();
const dim = tf.parameterList.length;
if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
{
auto param = tf.parameterList[0];
if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
{
return true;
}
}
return false;
}
/*************************************
* Find the `alias this` symbol of e's type.
* Params:
* sc = context
* e = expression forming the `this`
* gag = 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)
{
//printf("resolveAliasThis() %s\n", toChars(e));
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 = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag));
const 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.
*/
private bool checkDeprecatedAliasThis(AliasThis at, Loc loc, Scope* sc)
{
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;
}
// Save the scope and defer semantic analysis on the Dsymbol.
void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx)
{
s._scope = scx ? scx : sc.copy();
s._scope.setNoFree();
Module.addDeferredSemantic(s);
}
private extern(C++) final class DsymbolSemanticVisitor : Visitor
{
alias visit = Visitor.visit;
Scope* sc;
this(Scope* sc) scope @safe
{
this.sc = sc;
}
override void visit(Dsymbol dsym)
{
.error(dsym.loc, "%s `%s` %p has no semantic routine", dsym.kind, dsym.toPrettyChars, dsym);
}
override void visit(ScopeDsymbol) { }
override void visit(Declaration) { }
override void visit(AliasThis dsym)
{
if (dsym.semanticRun != PASS.initial)
return;
if (dsym._scope)
{
sc = dsym._scope;
dsym._scope = null;
}
if (!sc)
return;
dsym.semanticRun = PASS.semantic;
dsym.isDeprecated_ = !!(sc.stc & STC.deprecated_);
Dsymbol p = sc.parent.pastMixin();
AggregateDeclaration ad = p.isAggregateDeclaration();
if (!ad)
{
error(dsym.loc, "alias this can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
return;
}
assert(ad.members);
Dsymbol s = ad.search(dsym.loc, dsym.ident);
if (!s)
{
Dsymbol pscopesym;
s = sc.search(dsym.loc, dsym.ident, pscopesym);
if (s)
error(dsym.loc, "`%s` is not a member of `%s`", s.toChars(), ad.toChars());
else
error(dsym.loc, "undefined identifier `%s`", dsym.ident.toChars());
return;
}
if (ad.aliasthis && s != ad.aliasthis)
{
error(dsym.loc, "there can be only one alias this");
return;
}
/* disable the alias this conversion so the implicit conversion check
* doesn't use it.
*/
ad.aliasthis = null;
Dsymbol sx = s;
if (sx.isAliasDeclaration())
sx = sx.toAlias();
Declaration d = sx.isDeclaration();
if (d && !d.isTupleDeclaration())
{
/* https://issues.dlang.org/show_bug.cgi?id=18429
*
* If the identifier in the AliasThis declaration
* is defined later and is a voldemort type, we must
* perform semantic on the declaration to deduce the type.
*/
if (!d.type)
d.dsymbolSemantic(sc);
Type t = d.type;
assert(t);
if (ad.type.implicitConvTo(t) > MATCH.nomatch)
{
error(dsym.loc, "alias this is not reachable as `%s` already converts to `%s`", ad.toChars(), t.toChars());
}
}
dsym.sym = s;
// Restore alias this
ad.aliasthis = dsym;
dsym.semanticRun = PASS.semanticdone;
}
override void visit(AliasDeclaration dsym)
{
if (dsym.semanticRun >= PASS.semanticdone)
return;
assert(dsym.semanticRun <= PASS.semantic);
if (!sc)
return;
dsym.semanticRun = PASS.semantic;
dsym.storage_class |= sc.stc & STC.deprecated_;
dsym.visibility = sc.visibility;
dsym.userAttribDecl = sc.userAttribDecl;
if (!sc.func && dsym.inNonRoot())
return;
aliasSemantic(dsym, sc);
}
override void visit(AliasAssign dsym)
{
//printf("visit(AliasAssign)\n");
if (dsym.semanticRun >= PASS.semanticdone)
return;
assert(dsym.semanticRun <= PASS.semantic);
if (!sc.func && dsym.inNonRoot())
return;
aliasAssignSemantic(dsym, sc);
}
override void visit(VarDeclaration dsym)
{
version (none)
{
printf("VarDeclaration::semantic('%s', parent = '%s') sem = %d\n",
dsym.toChars(), sc.parent ? sc.parent.toChars() : null, dsym.semanticRun);
printf(" type = %s\n", dsym.type ? dsym.type.toChars() : "null");
printf(" stc = x%llx\n", dsym.storage_class);
printf(" storage_class = x%llx\n", dsym.storage_class);
printf("linkage = %d\n", dsym._linkage);
//if (strcmp(toChars(), "mul") == 0) assert(0);
}
//if (semanticRun > PASS.initial)
// return;
//semanticRun = PSSsemantic;
if (dsym.semanticRun >= PASS.semanticdone)
return;
if (sc && sc.inunion && sc.inunion.isAnonDeclaration())
dsym.overlapped = true;
dsym.sequenceNumber = global.varSequenceNumber++;
if (!dsym.isScope())
dsym.maybeScope = true;
Scope* scx = null;
if (dsym._scope)
{
sc = dsym._scope;
scx = sc;
dsym._scope = null;
}
if (!sc)
return;
dsym.semanticRun = PASS.semantic;
// 'static foreach' variables should not inherit scope properties
// https://issues.dlang.org/show_bug.cgi?id=19482
if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local))
{
dsym._linkage = LINK.d;
dsym.visibility = Visibility(Visibility.Kind.public_);
dsym.overlapped = false; // unset because it is modified early on this function
dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope()
}
else
{
/* Pick up storage classes from context, but except synchronized,
* override, abstract, and final.
*/
dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_));
dsym.userAttribDecl = sc.userAttribDecl;
dsym.cppnamespace = sc.namespace;
dsym._linkage = sc.linkage;
dsym.visibility = sc.visibility;
dsym.alignment = sc.alignment();
}
if (dsym.storage_class & STC.extern_ && dsym._init)
{
if (sc.inCfile)
{
// https://issues.dlang.org/show_bug.cgi?id=24447
// extern int x = 3; is allowed in C
dsym.storage_class &= ~STC.extern_;
}
else
.error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars);
}
AggregateDeclaration ad = dsym.isThis();
if (ad)
dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
if ((dsym.storage_class & STC.auto_) && (dsym.storage_class & STC.ref_))
{
if (!(dsym.storage_class & STC.autoref))
{
.error(dsym.loc, "%s `%s` - `auto ref` variable must have `auto` and `ref` adjacent", dsym.kind, dsym.toChars());
dsym.storage_class |= STC.autoref;
}
}
/* If auto type inference, do the inference
*/
int inferred = 0;
if (!dsym.type)
{
dsym.inuse++;
// Infering the type requires running semantic,
// so mark the scope as ctfe if required
bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func;
if (needctfe)
{
sc.condition = true;
sc = sc.startCTFE();
}
//printf("inferring type for %s with init %s\n", dsym.toChars(), dsym._init.toChars());
dsym._init = dsym._init.inferType(sc);
dsym.type = dsym._init.initializerToExpression(null, sc.inCfile).type;
if (needctfe)
sc = sc.endCTFE();
dsym.inuse--;
inferred = 1;
/* This is a kludge to support the existing syntax for RAII
* declarations.
*/
dsym.storage_class &= ~STC.auto_;
dsym.originalType = dsym.type.syntaxCopy();
}
else
{
if (!dsym.originalType)
dsym.originalType = dsym.type.syntaxCopy();
/* Prefix function attributes of variable declaration can affect
* its type:
* pure nothrow void function() fp;
* static assert(is(typeof(fp) == void function() pure nothrow));
*/
Scope* sc2 = sc.push();
sc2.stc |= (dsym.storage_class & STC.FUNCATTR);
dsym.inuse++;
dsym.type = dsym.type.typeSemantic(dsym.loc, sc2);
dsym.inuse--;
sc2.pop();
}
//printf(" semantic type = %s\n", dsym.type ? dsym.type.toChars() : "null");
if (dsym.type.ty == Terror)
dsym.errors = true;
dsym.type.checkDeprecated(dsym.loc, sc);
dsym.parent = sc.parent;
//printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
/* If scope's alignment is the default, use the type's alignment,
* otherwise the scope overrrides.
*/
if (dsym.alignment.isDefault())
dsym.alignment = dsym.type.alignment(); // use type's alignment
//printf("sc.stc = %x\n", sc.stc);
//printf("storage_class = x%x\n", storage_class);
dsym.type.checkComplexTransition(dsym.loc, sc);
// Calculate type size + safety checks
if (dsym.storage_class & STC.gshared && !dsym.isMember())
{
sc.setUnsafe(false, dsym.loc, "using `__gshared` instead of `shared`");
}
Dsymbol parent = dsym.toParent();
Type tb = dsym.type.toBasetype();
Type tbn = tb.baseElemOf();
if (tb.ty == Tvoid && !(dsym.storage_class & STC.lazy_))
{
if (inferred)
{
.error(dsym.loc, "%s `%s` - type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`",
dsym.kind, dsym.toPrettyChars, dsym.type.toChars(), toChars(dsym._init));
}
else
.error(dsym.loc, "%s `%s` - variables cannot be of type `void`", dsym.kind, dsym.toPrettyChars);
dsym.type = Type.terror;
tb = dsym.type;
}
if (tb.ty == Tfunction)
{
.error(dsym.loc, "%s `%s` cannot be declared to be a function", dsym.kind, dsym.toPrettyChars);
dsym.type = Type.terror;
tb = dsym.type;
}
if (auto ts = tb.isTypeStruct())
{
// Require declarations, except when it's just a reference (as done for pointers)
// or when the variable is defined externally
if (!ts.sym.members && !(dsym.storage_class & (STC.ref_ | STC.extern_)))
{
.error(dsym.loc, "%s `%s` - no definition of struct `%s`", dsym.kind, dsym.toPrettyChars, ts.toChars());
// Explain why the definition is required when it's part of another type
if (!dsym.type.isTypeStruct())
{
// Prefer Loc of the dependant type
const s = dsym.type.toDsymbol(sc);
const loc = (s ? s : dsym).loc;
loc.errorSupplemental("required by type `%s`", dsym.type.toChars());
}
errorSupplemental(dsym.loc, "see https://dlang.org/spec/struct.html#opaque_struct_unions");
errorSupplemental(dsym.loc, "perhaps declare a variable with pointer type `%s*` instead", dsym.type.toChars());
// Flag variable as error to avoid invalid error messages due to unknown size
dsym.type = Type.terror;
}
}
if ((dsym.storage_class & STC.auto_) && !inferred && !(dsym.storage_class & STC.autoref))
.error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars);
if (auto tt = tb.isTypeTuple())
{
/* Instead, declare variables for each of the tuple elements
* and add those.
*/
size_t nelems = Parameter.dim(tt.arguments);
Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, sc.inCfile) : null;
if (ie)
ie = ie.expressionSemantic(sc);
if (nelems > 0 && ie)
{
auto iexps = new Expressions();
iexps.push(ie);
auto exps = new Expressions();
for (size_t pos = 0; pos < iexps.length; pos++)
{
Lexpand1:
Expression e = (*iexps)[pos];
Parameter arg = Parameter.getNth(tt.arguments, pos);
arg.type = arg.type.typeSemantic(dsym.loc, sc);
//printf("[%d] iexps.length = %d, ", pos, iexps.length);
//printf("e = (%s %s, %s), ", Token.tochars[e.op], e.toChars(), e.type.toChars());
//printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
if (e != ie)
{
if (iexps.length > nelems)
goto Lnomatch;
if (e.type.implicitConvTo(arg.type))
continue;
}
if (auto te = e.isTupleExp())
{
if (iexps.length - 1 + te.exps.length > nelems)
goto Lnomatch;
iexps.remove(pos);
iexps.insert(pos, te.exps);
(*iexps)[pos] = Expression.combine(te.e0, (*iexps)[pos]);
goto Lexpand1;
}
else if (isAliasThisTuple(e))
{
auto v = copyToTemp(STC.none, "__tup", e);
v.dsymbolSemantic(sc);
auto ve = new VarExp(dsym.loc, v);
ve.type = e.type;
exps.setDim(1);
(*exps)[0] = ve;
expandAliasThisTuples(exps, 0);
for (size_t u = 0; u < exps.length; u++)
{
Lexpand2:
Expression ee = (*exps)[u];
arg = Parameter.getNth(tt.arguments, pos + u);
arg.type = arg.type.typeSemantic(dsym.loc, sc);
//printf("[%d+%d] exps.length = %d, ", pos, u, exps.length);
//printf("ee = (%s %s, %s), ", Token.tochars[ee.op], ee.toChars(), ee.type.toChars());
//printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
size_t iexps_dim = iexps.length - 1 + exps.length;
if (iexps_dim > nelems)
goto Lnomatch;
if (ee.type.implicitConvTo(arg.type))
continue;
if (expandAliasThisTuples(exps, u) != -1)
goto Lexpand2;
}
if ((*exps)[0] != ve)
{
Expression e0 = (*exps)[0];
(*exps)[0] = new CommaExp(dsym.loc, new DeclarationExp(dsym.loc, v), e0);
(*exps)[0].type = e0.type;
iexps.remove(pos);
iexps.insert(pos, exps);
goto Lexpand1;
}
}
}
if (iexps.length < nelems)
goto Lnomatch;
ie = new TupleExp(dsym._init.loc, iexps);
}
Lnomatch:
if (ie && ie.op == EXP.tuple)
{
auto te = ie.isTupleExp();
size_t tedim = te.exps.length;
if (tedim != nelems)
{
error(dsym.loc, "sequence of %d elements cannot be assigned to sequence of %d elements", cast(int)tedim, cast(int)nelems);
for (size_t u = tedim; u < nelems; u++) // fill dummy expression
te.exps.push(ErrorExp.get());
}
}
auto exps = new Objects(nelems);
for (size_t i = 0; i < nelems; i++)
{
Parameter arg = Parameter.getNth(tt.arguments, i);
OutBuffer buf;
buf.printf("__%s_field_%llu", dsym.ident.toChars(), cast(ulong)i);
auto id = Identifier.idPool(buf[]);
Initializer ti;
if (ie)
{
Expression einit = ie;
if (auto te = ie.isTupleExp())
{
einit = (*te.exps)[i];
if (i == 0)
einit = Expression.combine(te.e0, einit);
}
ti = new ExpInitializer(einit.loc, einit);
}
else
ti = dsym._init ? dsym._init.syntaxCopy() : null;
STC storage_class = STC.temp | dsym.storage_class;
if ((dsym.storage_class & STC.parameter) && (arg.storageClass & STC.parameter))
storage_class |= arg.storageClass;
auto v = new VarDeclaration(dsym.loc, arg.type, id, ti, storage_class);
//printf("declaring field %s of type %s\n", v.toChars(), v.type.toChars());
v.overlapped = dsym.overlapped;
v.dsymbolSemantic(sc);
Expression e = new VarExp(dsym.loc, v);
(*exps)[i] = e;
}
auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
v2.parent = dsym.parent;
v2.isexp = true;
dsym.aliasTuple = v2;
dsym.semanticRun = PASS.semanticdone;
return;
}
/* Storage class can modify the type
*/
dsym.type = dsym.type.addStorageClass(dsym.storage_class);
/* Adjust storage class to reflect type
*/
if (dsym.type.isConst())
{
dsym.storage_class |= STC.const_;
if (dsym.type.isShared())
dsym.storage_class |= STC.shared_;
}
else if (dsym.type.isImmutable())
dsym.storage_class |= STC.immutable_;
else if (dsym.type.isShared())
dsym.storage_class |= STC.shared_;
else if (dsym.type.isWild())
dsym.storage_class |= STC.wild;
if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_))
{
if (stc == STC.final_)
.error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars);
else
{
OutBuffer buf;
stcToBuffer(buf, stc);
.error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
}
dsym.storage_class &= ~stc; // strip off
}
// At this point we can add `scope` to the STC instead of `in`,
// because we are never going to use this variable's STC for user messages
if (dsym.storage_class & STC.constscoperef)
dsym.storage_class |= STC.scope_;
import dmd.typesem : hasPointers;
if (dsym.storage_class & STC.scope_)
{
STC stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared);
if (stc)
{
OutBuffer buf;
stcToBuffer(buf, stc);
.error(dsym.loc, "%s `%s` cannot be `scope` and `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
}
else if (dsym.isMember())
{
error(dsym.loc, "field `%s` cannot be `scope`", dsym.toChars());
}
else if (!dsym.type.hasPointers())
{
dsym.storage_class &= ~STC.scope_; // silently ignore; may occur in generic code
// https://issues.dlang.org/show_bug.cgi?id=23168
if (dsym.storage_class & STC.returnScope)
{
dsym.storage_class &= ~(STC.return_ | STC.returnScope);
}
}
}
if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
{
}
else
{
AggregateDeclaration aad = parent.isAggregateDeclaration();
if (aad)
{
if (global.params.v.field && dsym.storage_class & (STC.const_ | STC.immutable_) && dsym._init && !dsym._init.isVoidInitializer())
{
const(char)* s = (dsym.storage_class & STC.immutable_) ? "immutable" : "const";
message(dsym.loc, "`%s.%s` is `%s` field", ad.toPrettyChars(), dsym.toChars(), s);
}
dsym.storage_class |= STC.field;
if (auto ts = tbn.isTypeStruct())
if (ts.sym.noDefaultCtor)
{
if (!dsym.isThisDeclaration() && !dsym._init)
aad.noDefaultCtor = true;
}
}
InterfaceDeclaration id = parent.isInterfaceDeclaration();
if (id)
{
error(dsym.loc, "field `%s` not allowed in interface", dsym.toChars());
}
else if (aad && aad.sizeok == Sizeok.done)
{
error(dsym.loc, "cannot declare field `%s` because it will change the determined size of `%s`", dsym.toChars(), aad.toChars());
}
/* Templates cannot add fields to aggregates
*/
TemplateInstance ti = parent.isTemplateInstance();
if (ti)
{
// Take care of nested templates
while (1)
{
TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
if (!ti2)
break;
ti = ti2;
}
// If it's a member template
AggregateDeclaration ad2 = ti.tempdecl.isMember();
if (ad2 && dsym.storage_class != STC.none)
{
.error(dsym.loc, "%s `%s` - cannot use template to add field to aggregate `%s`", dsym.kind, dsym.toPrettyChars, ad2.toChars());
}
}
}
/* If the alignment of a stack local is greater than the stack alignment,
* note it in the enclosing function's alignSectionVars
*/
version (MARS)
{
if (!dsym.alignment.isDefault() && sc.func &&
dsym.alignment.get() > target.stackAlign() &&
sc.func && !dsym.isDataseg() && !dsym.isParameter() && !dsym.isField())
{
auto fd = sc.func;
if (!fd.alignSectionVars)
fd.alignSectionVars = new VarDeclarations();
fd.alignSectionVars.push(dsym);
}
}
if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This)
{
.error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars);
}
if (dsym.type.hasWild())
{
if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field) || dsym.isDataseg())
{
.error(dsym.loc, "%s `%s` - only parameters or stack-based variables can be `inout`", dsym.kind, dsym.toPrettyChars);
}
FuncDeclaration func = sc.func;
if (func)
{
if (func.fes)
func = func.fes.func;
bool isWild = false;
for (FuncDeclaration fd = func; fd; fd = fd.toParentDecl().isFuncDeclaration())
{
if (fd.type.isTypeFunction().iswild)
{
isWild = true;
break;
}
}
if (!isWild)
{
.error(dsym.loc, "%s `%s` - `inout` variables can only be declared inside `inout` functions", dsym.kind, dsym.toPrettyChars);
}
}
}
if (!(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.ref_ | STC.result)) &&
tbn.ty == Tstruct && tbn.isTypeStruct().sym.noDefaultCtor)
{
if (!dsym._init)
{
if (dsym.isField())
{
/* For fields, we'll check the constructor later to make sure it is initialized
*/
dsym.storage_class |= STC.nodefaultctor;
}
else if (dsym.storage_class & STC.parameter)
{
}
else
.error(dsym.loc, "%s `%s` - default construction is disabled for type `%s`", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
}
}
bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_;
if (dsymIsRef)
{
if (!dsym._init && dsym.ident != Id.This)
{
if (dsym.storage_class & STC.autoref)
{
dsymIsRef = false;
dsym.storage_class &= ~STC.ref_;
}
else
.error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);
}
else if (dsym._init.isVoidInitializer())
{
.error(dsym.loc, "%s `%s` - void initializer not allowed for `ref` variable", dsym.kind, dsym.toPrettyChars);
}
}
FuncDeclaration fd = parent.isFuncDeclaration();
if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
{
if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd)
{
.error(dsym.loc, "%s `%s` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`", dsym.kind, dsym.toPrettyChars);
}
// @@@DEPRECATED_2.097@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
// Deprecated in 2.087
// Remove this when the feature is removed from the language
if (!(dsym.storage_class & STC.scope_))
{
if (!(dsym.storage_class & STC.parameter) && dsym.ident != Id.withSym)
.error(dsym.loc, "%s `%s` reference to `scope class` must be `scope`", dsym.kind, dsym.toPrettyChars);
}
}
// Calculate type size + safety checks
if (sc && sc.func)
{
if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp))
{
// Don't do these checks for STC.temp vars because the generated `opAssign`
// for a struct with postblit and destructor void initializes a temporary
// __swap variable, which can be trusted
if (dsym.type.hasPointers()) // also computes type size
sc.setUnsafe(false, dsym.loc,
"`void` initializing a pointer");
else if (dsym.type.hasInvariant())
sc.setUnsafe(false, dsym.loc,
"`void` initializing a struct with an invariant");
else if (dsym.type.toBasetype().ty == Tbool)
sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
"void intializing a bool (which must always be 0 or 1)");
else if (dsym.type.hasUnsafeBitpatterns())
sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
"`void` initializing a type with unsafe bit patterns");
}
else if (!dsym._init &&
!(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&
dsym.type.hasVoidInitPointers())
{
sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers");
}
}
if ((!dsym._init || dsym._init.isVoidInitializer) && !fd)
{
// If not mutable, initializable by constructor only
dsym.setInCtorOnly = true;
}
if (dsym._init)
{ } // remember we had an explicit initializer
else if (dsym.storage_class & STC.manifest)
.error(dsym.loc, "%s `%s` - manifest constants must have initializers", dsym.kind, dsym.toPrettyChars);
// Don't allow non-extern, non-__gshared variables to be interfaced with C++
if (dsym._linkage == LINK.cpp && !(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.gshared)) && dsym.isDataseg())
{
const char* p = (dsym.storage_class & STC.shared_) ? "shared" : "static";
.error(dsym.loc, "%s `%s` cannot have `extern(C++)` linkage because it is `%s`", dsym.kind, dsym.toPrettyChars, p);
errorSupplemental(dsym.loc, "perhaps declare it as `__gshared` instead");
dsym.errors = true;
}
bool isBlit = false;
uinteger_t sz;
if (sc.inCfile && !dsym._init)
{
addDefaultCInitializer(dsym);
}
if (!dsym._init &&
!(dsym.storage_class & (STC.static_ | STC.gshared | STC.extern_)) &&
fd &&
(!(dsym.storage_class & (STC.field | STC.in_ | STC.foreach_ | STC.parameter | STC.result)) ||
(dsym.storage_class & STC.out_)) &&
(sz = dsym.type.size()) != 0)
{
// Provide a default initializer
//printf("Providing default initializer for '%s'\n", dsym.toChars());
if (sz == SIZE_INVALID && dsym.type.ty != Terror)
.error(dsym.loc, "%s `%s` - size of type `%s` is invalid", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
Type tv = dsym.type;
while (tv.ty == Tsarray) // Don't skip Tenum
tv = tv.nextOf();
if (tv.needsNested())
{
/* Nested struct requires valid enclosing frame pointer.
* In StructLiteralExp::toElem(), it's calculated.
*/
assert(tbn.ty == Tstruct);
checkFrameAccess(dsym.loc, sc, tbn.isTypeStruct().sym);
Expression e = tv.defaultInitLiteral(dsym.loc);
e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
e = e.expressionSemantic(sc);
dsym._init = new ExpInitializer(dsym.loc, e);
goto Ldtor;
}
if (tv.ty == Tstruct && tv.isTypeStruct().sym.zeroInit)
{
/* If a struct is all zeros, as a special case
* set its initializer to the integer 0.
* In AssignExp::toElem(), we check for this and issue
* a memset() to initialize the struct.
* Must do same check in interpreter.
*/
Expression e = IntegerExp.literal!0;
e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
e.type = dsym.type; // don't type check this, it would fail
dsym._init = new ExpInitializer(dsym.loc, e);
goto Ldtor;
}
if (dsym.type.baseElemOf().ty == Tvoid)
{
.error(dsym.loc, "%s `%s` of type `%s` does not have a default initializer", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
}
else if (auto e = dsym.type.defaultInit(dsym.loc))
{
dsym._init = new ExpInitializer(dsym.loc, e);
}
// Default initializer is always a blit
isBlit = true;
}
if (dsym._init)
{
sc = sc.push();
sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable);
if (sc.inCfile &&
dsym.type.isTypeSArray() &&
dsym.type.isTypeSArray().isIncomplete() &&
dsym._init.isVoidInitializer() &&
!(dsym.storage_class & STC.field))
{
.error(dsym.loc, "%s `%s` - incomplete array type must have initializer", dsym.kind, dsym.toPrettyChars);
}
ExpInitializer ei = dsym._init.isExpInitializer();
if (ei) // https://issues.dlang.org/show_bug.cgi?id=13424
// Preset the required type to fail in FuncLiteralDeclaration::semantic3
ei.exp = inferType(ei.exp, dsym.type);
// If inside function, there is no semantic3() call
if (sc.func || sc.intypeof == 1)
{
// If local variable, use AssignExp to handle all the various
// possibilities.
if (fd && !(dsym.storage_class & (STC.manifest | STC.static_ | STC.gshared | STC.extern_)) && !dsym._init.isVoidInitializer())
{
//printf("fd = '%s', var = '%s'\n", fd.toChars(), dsym.toChars());
if (!ei)
{
ArrayInitializer ai = dsym._init.isArrayInitializer();
Expression e;
if (ai && tb.ty == Taarray)
e = ai.toAssocArrayLiteral();
else
e = dsym._init.initializerToExpression(null, sc.inCfile);
if (!e)
{
// Run semantic, but don't need to interpret
dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret);
e = dsym._init.initializerToExpression(null, sc.inCfile);
if (!e)
{
.error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars);
e = ErrorExp.get();
}
}
ei = new ExpInitializer(dsym._init.loc, e);
dsym._init = ei;
}
else if (sc.inCfile && dsym.type.isTypeSArray() &&
dsym.type.isTypeSArray().isIncomplete())
{
// C11 6.7.9-22 determine the size of the incomplete array,
// or issue an error that the initializer is invalid.
dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
}
if (ei && dsym.isScope())
{
Expression ex = ei.exp.lastComma();
if (ex.op == EXP.blit || ex.op == EXP.construct)
ex = (cast(AssignExp)ex).e2;
if (auto ne = ex.isNewExp())
{
if (ne.placement)
{
}
/* See if initializer is a NewExp that can be allocated on the stack.
*/
else if (dsym.type.toBasetype().ty == Tclass)
{
/* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak.
* https://issues.dlang.org/show_bug.cgi?id=23145
*/
if (ne.member && !(ne.member.storage_class & STC.scope_))
{
import dmd.escape : setUnsafeDIP1000;
const inSafeFunc = sc.func && sc.func.isSafeBypassingInference(); // isSafeBypassingInference may call setUnsafe().
if (setUnsafeDIP1000(*sc, false, dsym.loc, "`scope` allocation of `%s` with a non-`scope` constructor", dsym))
errorSupplemental(ne.member.loc, "is the location of the constructor");
}
ne.onstack = 1;
dsym.onstack = true;
}
}
else if (auto fe = ex.isFuncExp())
{
// or a delegate that doesn't escape a reference to the function
FuncDeclaration f = fe.fd;
if (f.tookAddressOf)
f.tookAddressOf--;
}
else if (auto ale = ex.isArrayLiteralExp())
{
// or an array literal assigned to a `scope` variable
if (sc.useDIP1000 == FeatureState.enabled
&& !dsym.type.nextOf().needsDestruction())
ale.onstack = true;
}
}
Expression exp = ei.exp;
Expression e1 = new VarExp(dsym.loc, dsym);
void constructInit(bool isBlit)
{
if (isBlit)
exp = new BlitExp(dsym.loc, e1, exp);
else
exp = new ConstructExp(dsym.loc, e1, exp);
dsym.canassign++;
exp = exp.expressionSemantic(sc);
dsym.canassign--;
}
if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach()
{
dsym.storage_class |= STC.nodtor;
exp = exp.expressionSemantic(sc);
Type tp = dsym.type;
Type ta = exp.type;
if (!exp.isLvalue())
{
if (dsym.storage_class & STC.autoref)
{
dsym.storage_class &= ~STC.ref_;
constructInit(isBlit);
}
else
{
.error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
exp = ErrorExp.get();
}
}
else if (!ta.constConv(tp))
{
if (dsym.storage_class & STC.autoref)
{
dsym.storage_class &= ~STC.ref_;
constructInit(false);
}
else
{
.error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
exp = ErrorExp.get();
}
}
else
{
constructInit(false);
}
}
else
{
constructInit(isBlit);
}
if (exp.op == EXP.error)
{
dsym._init = new ErrorInitializer();
ei = null;
}
else
ei.exp = exp.optimize(WANTvalue);
}
else
{
// https://issues.dlang.org/show_bug.cgi?id=14166
// Don't run CTFE for the temporary variables inside typeof
dsym._init = dsym._init.initializerSemantic(sc, dsym.type, sc.intypeof == 1 ? INITnointerpret : INITinterpret);
import dmd.semantic2 : lowerStaticAAs;
lowerStaticAAs(dsym, sc);
auto init_err = dsym._init.isExpInitializer();
if (init_err && init_err.exp.op == EXP.showCtfeContext)
{
init_err.exp = ErrorExp.get();
errorSupplemental(dsym.loc, "compile time context created here");
}
}
}
else if (parent.isAggregateDeclaration())
{
dsym._scope = scx ? scx : sc.copy();
dsym._scope.setNoFree();
}
else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
dsym.type.isConst() || dsym.type.isImmutable() ||
sc.inCfile)
{
/* Because we may need the results of a const declaration in a
* subsequent type, such as an array dimension, before semantic2()
* gets ordinarily run, try to run semantic2() now.
* If a C array is of unknown size, the initializer can provide the size. Do this
* eagerly because C does it eagerly.
* Ignore failure.
*/
if (!inferred)
{
const errors = global.errors;
dsym.inuse++;
// Bug 20549. Don't try this on modules or packages, syntaxCopy
// could crash (inf. recursion) on a mod/pkg referencing itself
if (ei && (ei.exp.op != EXP.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
{
if (ei.exp.type)
{
// If exp is already resolved we are done, our original init exp
// could have a type painting that we need to respect
// e.g. ['a'] typed as string, or [['z'], ""] as string[]
// See https://issues.dlang.org/show_bug.cgi?id=15711
}
else
{
Expression exp = ei.exp.syntaxCopy();
bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
if (needctfe)
sc = sc.startCTFE();
sc = sc.push();
sc.varDecl = dsym; // https://issues.dlang.org/show_bug.cgi?id=24051
exp = exp.expressionSemantic(sc);
exp = resolveProperties(sc, exp);
sc = sc.pop();
if (needctfe)
sc = sc.endCTFE();
ei.exp = exp;
}
Type tb2 = dsym.type.toBasetype();
Type ti = ei.exp.type.toBasetype();
/* The problem is the following code:
* struct CopyTest {
* double x;
* this(double a) { x = a * 10.0;}
* this(this) { x += 2.0; }
* }
* const CopyTest z = CopyTest(5.3); // ok
* const CopyTest w = z; // not ok, postblit not run
* static assert(w.x == 55.0);
* because the postblit doesn't get run on the initialization of w.
*/
if (auto ts = ti.isTypeStruct())
{
StructDeclaration sd = ts.sym;
/* Look to see if initializer involves a copy constructor
* (which implies a postblit)
*/
// there is a copy constructor
// and exp is the same struct
if (sd.postblit && tb2.toDsymbol(null) == sd)
{
// The only allowable initializer is a (non-copy) constructor
if (ei.exp.isLvalue())
.error(dsym.loc, "%s `%s` of type struct `%s` uses `this(this)`, which is not allowed in static initialization", dsym.kind, dsym.toPrettyChars, tb2.toChars());
}
}
}
dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
dsym.inuse--;
if (global.errors > errors)
{
dsym._init = new ErrorInitializer();
dsym.type = Type.terror;
}
}
else
{
dsym._scope = scx ? scx : sc.copy();
dsym._scope.setNoFree();
}
}
sc = sc.pop();
}
Ldtor:
/* Build code to execute destruction, if necessary
*/
dsym.edtor = dsym.callScopeDtor(sc);
if (dsym.edtor)
{
if (sc.func && dsym.storage_class & (STC.static_ | STC.gshared))
dsym.edtor = dsym.edtor.expressionSemantic(sc._module._scope);
else
dsym.edtor = dsym.edtor.expressionSemantic(sc);
version (none)
{
// currently disabled because of std.stdio.stdin, stdout and stderr
if (dsym.isDataseg() && !(dsym.storage_class & STC.extern_))
.error(dsym.loc, "%s `%s` static storage variables cannot have destructors", dsym.kind, dsym.toPrettyChars);
}
}
dsym.semanticRun = PASS.semanticdone;
if (dsym.type.toBasetype().ty == Terror)
dsym.errors = true;
if(sc.scopesym && !sc.scopesym.isAggregateDeclaration())
{
for (ScopeDsymbol sym = sc.scopesym; sym && dsym.endlinnum == 0;
sym = sym.parent ? sym.parent.isScopeDsymbol() : null)
dsym.endlinnum = sym.endlinnum;
}
}
override void visit(TypeInfoDeclaration dsym)
{
assert(dsym._linkage == LINK.c);
}
override void visit(CAsmDeclaration dsym)
{
if (dsym.semanticRun >= PASS.semanticdone)
return;
import dmd.iasm : asmSemantic;
asmSemantic(dsym, sc);
dsym.semanticRun = PASS.semanticdone;
}
override void visit(BitFieldDeclaration dsym)
{
//printf("BitField::semantic('%s')\n", dsym.toChars());
if (dsym.semanticRun >= PASS.semanticdone)
return;
visit(cast(VarDeclaration)dsym);
if (dsym.errors)
return;
if (!(sc.previews.bitfields || sc.inCfile))
{
version (IN_GCC)
.error(dsym.loc, "%s `%s` use `-fpreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars);
else
.error(dsym.loc, "%s `%s` use -preview=bitfields for bitfield support", dsym.kind, dsym.toPrettyChars);
}
if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
{
.error(dsym.loc, "%s `%s` - bit-field must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
}
sc = sc.startCTFE();
auto width = dsym.width.expressionSemantic(sc);
sc = sc.endCTFE();
width = width.ctfeInterpret();
if (!dsym.type.isIntegral())
{
// C11 6.7.2.1-5
error(width.loc, "bit-field type `%s` is not an integer type", dsym.type.toChars());
dsym.errors = true;
}
if (!width.isIntegerExp())
{
error(width.loc, "bit-field width `%s` is not an integer constant", dsym.width.toChars());
dsym.errors = true;
}
const uwidth = width.toInteger(); // uwidth is unsigned
if (uwidth == 0 && !dsym.isAnonymous())
{
error(width.loc, "bit-field `%s` has zero width", dsym.toChars());
dsym.errors = true;
}
const sz = dsym.type.size();
if (sz == SIZE_INVALID)
dsym.errors = true;
const max_width = sz * 8;
if (uwidth > max_width)
{
error(width.loc, "width `%lld` of bit-field `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
dsym.errors = true;
}
dsym.fieldWidth = cast(uint)uwidth;
}
override void visit(Import imp)
{
timeTraceBeginEvent(TimeTraceEventType.sema1Import);
scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp);
static if (LOG)
{
printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars());
scope(exit)
printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg);
}
if (imp.semanticRun > PASS.initial)
return;
if (imp._scope)
{
sc = imp._scope;
imp._scope = null;
}
if (!sc)
return;
imp.parent = sc.parent;
imp.semanticRun = PASS.semantic;
// Load if not already done so
if (!imp.mod)
{
// https://issues.dlang.org/show_bug.cgi?id=22857
// if parser errors occur when loading a module
// we should just stop compilation
if (imp.load(sc))
{
for (size_t i = 0; i < imp.aliasdecls.length; i++)
imp.aliasdecls[i].type = Type.terror;
return;
}
if (imp.mod)
{
imp.mod.importAll(null);
imp.mod.checkImportDeprecation(imp.loc, sc);
}
}
if (!imp.mod)
{
imp.semanticRun = PASS.semanticdone;
addImportDep(global.params.moduleDeps, imp, sc._module);
}
// Modules need a list of each imported module
// if inside a template instantiation, the instantianting
// module gets the import.
// https://issues.dlang.org/show_bug.cgi?id=17181
Module importer = sc._module;
if (sc.minst && sc.tinst)
{
importer = sc.minst;
if (!sc.tinst.importedModules.contains(imp.mod))
sc.tinst.importedModules.push(imp.mod);
}
//printf("%s imports %s\n", importer.toChars(), imp.mod.toChars());
if (!importer.aimports.contains(imp.mod))
importer.aimports.push(imp.mod);
if (sc.explicitVisibility)
imp.visibility = sc.visibility;
if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import
{
ScopeDsymbol scopesym = sc.getScopesym();
if (!imp.isstatic)
{
scopesym.importScope(imp.mod, imp.visibility);
}
imp.addPackageAccess(scopesym);
}
// if a module has errors it means that parsing has failed.
if (!imp.mod.errors)
imp.mod.dsymbolSemantic(null);
if (imp.mod.needmoduleinfo)
{
//printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
importer.needmoduleinfo = 1;
}
sc = sc.push(imp.mod);
sc.visibility = imp.visibility;
for (size_t i = 0; i < imp.aliasdecls.length; i++)
{
AliasDeclaration ad = imp.aliasdecls[i];
//printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope);
Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports);
if (sym)
{
import dmd.access : symbolIsVisible;
if (!symbolIsVisible(sc, sym) && !sym.errors)
{
.error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars,
imp.names[i].toChars(), sc._module.toChars());
sym.errors = true;
}
ad.dsymbolSemantic(sc);
// If the import declaration is in non-root module,
// analysis of the aliased symbol is deferred.
// Therefore, don't see the ad.aliassym or ad.type here.
}
else
{
Dsymbol s = imp.mod.search_correct(imp.names[i]);
// https://issues.dlang.org/show_bug.cgi?id=23908
// Don't suggest symbols from the importer's module
if (s && s.parent != importer)
.error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars());
else
.error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars());
ad.type = Type.terror;
}
}
sc = sc.pop();
imp.semanticRun = PASS.semanticdone;
addImportDep(global.params.moduleDeps, imp, sc._module);
}
void attribSemantic(AttribDeclaration ad)
{
if (ad.semanticRun != PASS.initial)
return;
ad.semanticRun = PASS.semantic;
Dsymbols* d = ad.include(sc);
//printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
if (!d)
{
ad.semanticRun = PASS.semanticdone;
return;
}
Scope* sc2 = ad.newScope(sc);
bool errors;
for (size_t i = 0; i < d.length; i++)
{
Dsymbol s = (*d)[i];
s.dsymbolSemantic(sc2);
errors |= s.errors;
}
if (errors)
ad.errors = true;
if (sc2 != sc)
sc2.pop();
ad.semanticRun = PASS.semanticdone;
}
override void visit(AttribDeclaration atd)
{
attribSemantic(atd);
}
override void visit(AnonDeclaration scd)
{
//printf("\tAnonDeclaration::semantic isunion:%d ptr:%p\n", scd.isunion, scd);
assert(sc.parent);
auto p = sc.parent.pastMixin();
auto ad = p.isAggregateDeclaration();
if (!ad)
{
error(scd.loc, "%s can only be a part of an aggregate, not %s `%s`", scd.kind(), p.kind(), p.toChars());
scd.errors = true;
return;
}
if (!scd.decl)
return;
sc = sc.push();
sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared);
sc.inunion = scd.isunion ? scd : null;
sc.resetAllFlags();
for (size_t i = 0; i < scd.decl.length; i++)
{
Dsymbol s = (*scd.decl)[i];
if (auto var = s.isVarDeclaration)
{
if (scd.isunion)
var.overlapped = true;
}
s.dsymbolSemantic(sc);
}
sc = sc.pop();
}
override void visit(PragmaDeclaration pd)
{
import dmd.pragmasem : pragmaDeclSemantic;
pragmaDeclSemantic(pd, sc);
}
override void visit(StaticIfDeclaration sid)
{
attribSemantic(sid);
}
override void visit(StaticForeachDeclaration sfd)
{
attribSemantic(sfd);
}
private Dsymbols* compileIt(MixinDeclaration cd)
{
//printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
OutBuffer buf;
if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true))
return null;
const errors = global.errors;
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
const bool doUnittests = global.params.parsingUnittestsRequired();
scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
adjustLocForMixin(str, cd.loc, *p.baseLoc, global.params.mixinOut);
p.linnum = p.baseLoc.startLine;
p.nextToken();
auto d = p.parseDeclDefs(0);
if (global.errors != errors)
return null;
if (p.token.value != TOK.endOfFile)
{
.error(cd.loc, "%s `%s` incomplete mixin declaration `%s`", cd.kind, cd.toPrettyChars, str.ptr);
return null;
}
return d;
}
/***********************************************************
* https://dlang.org/spec/module.html#mixin-declaration
*/
override void visit(MixinDeclaration cd)
{
//printf("MixinDeclaration::semantic()\n");
if (!cd.compiled)
{
cd.decl = compileIt(cd);
attribAddMember(cd, sc, cd.scopesym);
cd.compiled = true;
if (cd._scope && cd.decl)
{
for (size_t i = 0; i < cd.decl.length; i++)
{
Dsymbol s = (*cd.decl)[i];
s.setScope(cd._scope);
}
}
}
attribSemantic(cd);
}
override void visit(CPPNamespaceDeclaration ns)
{
Identifier identFromSE (StringExp se)
{
const sident = se.toStringz();
if (!sident.length || !Identifier.isValidIdentifier(sident))
{
error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%s`", se.toErrMsg());
return null;
}
else
return Identifier.idPool(sident);
}
if (ns.ident !is null)
return attribSemantic(ns);
ns.cppnamespace = sc.namespace;
sc = sc.startCTFE();
ns.exp = ns.exp.expressionSemantic(sc);
ns.exp = resolveProperties(sc, ns.exp);
sc = sc.endCTFE();
ns.exp = ns.exp.ctfeInterpret();
// Can be either a tuple of strings or a string itself
if (auto te = ns.exp.isTupleExp())
{
expandTuples(te.exps);
CPPNamespaceDeclaration current = ns.cppnamespace;
for (size_t d = 0; d < te.exps.length; ++d)
{
auto exp = (*te.exps)[d];
auto prev = d ? current : ns.cppnamespace;
current = (d + 1) != te.exps.length
? new CPPNamespaceDeclaration(ns.loc, exp, null)
: ns;
current.exp = exp;
current.cppnamespace = prev;
if (auto se = exp.toStringExp())
{
current.ident = identFromSE(se);
if (current.ident is null)
return; // An error happened in `identFromSE`
}
else
error(ns.exp.loc, "`%s`: index %llu is not a string constant, it is a `%s`",
ns.exp.toChars(), cast(ulong) d, ns.exp.type.toChars());
}
}
else if (auto se = ns.exp.toStringExp())
ns.ident = identFromSE(se);
// Empty Tuple
else if (ns.exp.isTypeExp() && ns.exp.isTypeExp().type.toBasetype().isTypeTuple())
{
}
else if (!ns.exp.type.isTypeError())
error(ns.exp.loc, "compile time string constant (or sequence) expected, not `%s`",
ns.exp.toChars());
attribSemantic(ns);
}
override void visit(UserAttributeDeclaration uad)
{
//printf("UserAttributeDeclaration::semantic() %p\n", this);
if (uad.decl && !uad._scope)
uad.Dsymbol.setScope(sc); // for function local symbols
arrayExpressionSemantic(uad.atts.peekSlice(), sc, true);
return attribSemantic(uad);
}
override void visit(StaticAssert sa)
{
if (sa.semanticRun < PASS.semanticdone)
sa.semanticRun = PASS.semanticdone;
else
return;
// https://issues.dlang.org/show_bug.cgi?id=24645
// This is a short-circuit. Usually, static assert conditions are evaluated
// in semantic2, but it's not uncommon to use this pattern:
// ---
// version(X)
// {}
// else
// static assert(false, "unsupported platform");
// ---
// However, without this short-circuit, the static assert error may get drowned
// out by subsequent semantic1 (import) errors. Only short-circuit at module scope though,
// inside mixin templates you want an instantiation trace (which you don't get here).
if (sc.parent && sc.parent.isModule())
if (auto i = sa.exp.isIntegerExp())
if (i.toInteger() == 0)
staticAssertFail(sa, sc);
}
override void visit(DebugSymbol ds)
{
//printf("DebugSymbol::semantic() %s\n", toChars());
if (ds.semanticRun < PASS.semanticdone)
ds.semanticRun = PASS.semanticdone;
}
override void visit(VersionSymbol vs)
{
if (vs.semanticRun < PASS.semanticdone)
vs.semanticRun = PASS.semanticdone;
}
override void visit(Package pkg)
{
if (pkg.semanticRun < PASS.semanticdone)
pkg.semanticRun = PASS.semanticdone;
}
override void visit(Module m)
{
if (m.semanticRun != PASS.initial)
return;
timeTraceBeginEvent(TimeTraceEventType.sema1Module);
scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m);
//printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
m.semanticRun = PASS.semantic;
// Note that modules get their own scope, from scratch.
// This is so regardless of where in the syntax a module
// gets imported, it is unaffected by context.
Scope* sc = m._scope; // see if already got one from importAll()
if (!sc)
{
sc = Scope.createGlobal(m, global.errorSink); // create root scope
}
//printf("Module = %p, linkage = %d\n", sc.scopesym, sc.linkage);
// Pass 1 semantic routines: do public side of the definition
m.members.foreachDsymbol( (s)
{
//printf("\tModule('%s'): '%s'.dsymbolSemantic()\n", toChars(), s.toChars());
s.dsymbolSemantic(sc);
m.runDeferredSemantic();
});
if (m.userAttribDecl)
{
m.userAttribDecl.dsymbolSemantic(sc);
}
if (!m._scope)
{
sc = sc.pop();
sc.pop(); // 2 pops because Scope.createGlobal() created 2
}
m.semanticRun = PASS.semanticdone;
//printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
}
override void visit(EnumDeclaration ed)
{
enumSemantic(sc, ed);
}
override void visit(EnumMember em)
{
enumMemberSemantic(sc, em);
}
override void visit(TemplateDeclaration tempdecl)
{
templateDeclarationSemantic(sc, tempdecl);
}
override void visit(TemplateInstance ti)
{
templateInstanceSemantic(ti, sc, ArgumentList());
}
override void visit(TemplateMixin tm)
{
static if (LOG)
{
printf("+TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
fflush(stdout);
}
if (tm.semanticRun != PASS.initial)
{
// When a class/struct contains mixin members, and is done over
// because of forward references, never reach here so semanticRun
// has been reset to PASS.initial.
static if (LOG)
{
printf("\tsemantic done\n");
}
return;
}
tm.semanticRun = PASS.semantic;
static if (LOG)
{
printf("\tdo semantic\n");
}
Scope* scx = null;
if (tm._scope)
{
sc = tm._scope;
scx = tm._scope; // save so we don't make redundant copies
tm._scope = null;
}
/* Run semantic on each argument, place results in tiargs[],
* then find best match template with tiargs
*/
if (!tm.findTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
{
if (tm.semanticRun == PASS.initial) // forward reference had occurred
{
//printf("forward reference - deferring\n");
return deferDsymbolSemantic(sc, tm, scx);
}
tm.inst = tm;
tm.errors = true;
return; // error recovery
}
auto tempdecl = tm.tempdecl.isTemplateDeclaration();
assert(tempdecl);
if (!tm.ident)
{
/* Assign scope local unique identifier, as same as lambdas.
*/
const(char)[] s = "__mixin";
if (FuncDeclaration func = sc.parent.isFuncDeclaration())
{
tm.symtab = func.localsymtab;
if (tm.symtab)
{
// Inside template constraint, symtab is not set yet.
goto L1;
}
}
else
{
tm.symtab = sc.parent.isScopeDsymbol().symtab;
L1:
assert(tm.symtab);
tm.ident = Identifier.generateId(s, tm.symtab.length + 1);
tm.symtab.insert(tm);
}
}
tm.inst = tm;
tm.parent = sc.parent;
/* Detect recursive mixin instantiations.
*/
for (Dsymbol s = tm.parent; s; s = s.parent)
{
//printf("\ts = '%s'\n", s.toChars());
TemplateMixin tmix = s.isTemplateMixin();
if (!tmix || tempdecl != tmix.tempdecl)
continue;
/* Different argument list lengths happen with variadic args
*/
if (tm.tiargs.length != tmix.tiargs.length)
continue;
for (size_t i = 0; i < tm.tiargs.length; i++)
{
RootObject o = (*tm.tiargs)[i];
Type ta = isType(o);
Expression ea = isExpression(o);
Dsymbol sa = isDsymbol(o);
RootObject tmo = (*tmix.tiargs)[i];
if (ta)
{
Type tmta = isType(tmo);
if (!tmta)
goto Lcontinue;
if (!ta.equals(tmta))
goto Lcontinue;
}
else if (ea)
{
Expression tme = isExpression(tmo);
if (!tme || !ea.equals(tme))
goto Lcontinue;
}
else if (sa)
{
Dsymbol tmsa = isDsymbol(tmo);
if (sa != tmsa)
goto Lcontinue;
}
else
assert(0);
}
.error(tm.loc, "%s `%s` recursive mixin instantiation", tm.kind, tm.toPrettyChars);
return;
Lcontinue:
continue;
}
// Copy the syntax trees from the TemplateDeclaration
tm.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
if (!tm.members)
return;
tm.symtab = new DsymbolTable();
sc.getScopesym().importScope(tm, Visibility(Visibility.Kind.public_));
static if (LOG)
{
printf("\tcreate scope for template parameters '%s'\n", tm.toChars());
}
Scope* scy = sc.push(tm);
scy.parent = tm;
/* https://issues.dlang.org/show_bug.cgi?id=930
*
* If the template that is to be mixed in is in the scope of a template
* instance, we have to also declare the type aliases in the new mixin scope.
*/
auto parentInstance = tempdecl.parent ? tempdecl.parent.isTemplateInstance() : null;
if (parentInstance)
parentInstance.declareParameters(scy);
tm.argsym = new ScopeDsymbol();
tm.argsym.parent = scy.parent;
Scope* argscope = scy.push(tm.argsym);
const errorsave = global.errors;
// Declare each template parameter as an alias for the argument type
tm.declareParameters(argscope);
// Add members to enclosing scope, as well as this scope
tm.members.foreachDsymbol(s => s.addMember(argscope, tm));
// Do semantic() analysis on template instance members
static if (LOG)
{
printf("\tdo semantic() on template instance members '%s'\n", tm.toChars());
}
Scope* sc2 = argscope.push(tm);
//size_t deferred_dim = Module.deferred.length;
__gshared int nest;
//printf("%d\n", nest);
if (++nest > global.recursionLimit)
{
global.gag = 0; // ensure error message gets printed
.error(tm.loc, "%s `%s` recursive expansion", tm.kind, tm.toPrettyChars);
fatal();
}
tm.members.foreachDsymbol( s => s.setScope(sc2) );
tm.members.foreachDsymbol( s => s.importAll(sc2) );
tm.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
nest--;
/* In DeclDefs scope, TemplateMixin does not have to handle deferred symbols.
* Because the members would already call Module.addDeferredSemantic() for themselves.
* See Struct, Class, Interface, and EnumDeclaration.dsymbolSemantic().
*/
//if (!sc.func && Module.deferred.length > deferred_dim) {}
AggregateDeclaration ad = tm.isMember();
if (sc.func && !ad)
{
tm.semantic2(sc2);
tm.semantic3(sc2);
}
// Give additional context info if error occurred during instantiation
if (global.errors != errorsave)
{
.error(tm.loc, "%s `%s` error instantiating", tm.kind, tm.toPrettyChars);
tm.errors = true;
}
sc2.pop();
argscope.pop();
scy.pop();
static if (LOG)
{
printf("-TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
}
}
override void visit(Nspace ns)
{
if (ns.semanticRun != PASS.initial)
return;
static if (LOG)
{
printf("+Nspace::semantic('%s')\n", ns.toChars());
scope(exit) printf("-Nspace::semantic('%s')\n", ns.toChars());
}
if (ns._scope)
{
sc = ns._scope;
ns._scope = null;
}
if (!sc)
return;
bool repopulateMembers = false;
if (ns.identExp)
{
// resolve the namespace identifier
sc = sc.startCTFE();
Expression resolved = ns.identExp.expressionSemantic(sc);
resolved = resolveProperties(sc, resolved);
sc = sc.endCTFE();
resolved = resolved.ctfeInterpret();
StringExp name = resolved.toStringExp();
TupleExp tup = name ? null : resolved.isTupleExp();
if (!tup && !name)
{
error(ns.loc, "expected string expression for namespace name, got `%s`", ns.identExp.toChars());
return;
}
ns.identExp = resolved; // we don't need to keep the old AST around
if (name)
{
const(char)[] ident = name.toStringz();
if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
{
error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
return;
}
ns.ident = Identifier.idPool(ident);
}
else
{
// create namespace stack from the tuple
Nspace parentns = ns;
foreach (i, exp; *tup.exps)
{
name = exp.toStringExp();
if (!name)
{
error(ns.loc, "expected string expression for namespace name, got `%s`", exp.toChars());
return;
}
const(char)[] ident = name.toStringz();
if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
{
error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
return;
}
if (i == 0)
{
ns.ident = Identifier.idPool(ident);
}
else
{
// insert the new namespace
Nspace childns = new Nspace(ns.loc, Identifier.idPool(ident), null, parentns.members);
parentns.members = new Dsymbols;
parentns.members.push(childns);
parentns = childns;
repopulateMembers = true;
}
}
}
}
ns.semanticRun = PASS.semantic;
ns.parent = sc.parent;
// Link does not matter here, if the UDA is present it will error
checkGNUABITag(ns, LINK.cpp);
if (!ns.members)
{
ns.semanticRun = PASS.semanticdone;
return;
}
assert(sc);
sc = sc.push(ns);
sc.linkage = LINK.cpp; // note that namespaces imply C++ linkage
sc.parent = ns;
foreach (s; *ns.members)
{
if (repopulateMembers)
{
s.addMember(sc, sc.scopesym);
s.setScope(sc);
}
s.importAll(sc);
}
foreach (s; *ns.members)
{
static if (LOG)
{
printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
}
s.dsymbolSemantic(sc);
}
sc.pop();
ns.semanticRun = PASS.semanticdone;
}
/// Do the semantic analysis on the external interface to the function.
override void visit(FuncDeclaration funcdecl)
{
funcDeclarationSemantic(sc, funcdecl);
}
override void visit(CtorDeclaration ctd)
{
//printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars());
if (ctd.semanticRun >= PASS.semanticdone)
return;
if (ctd._scope)
{
sc = ctd._scope;
ctd._scope = null;
}
ctd.parent = sc.parent;
Dsymbol p = ctd.toParentDecl();
AggregateDeclaration ad = p.isAggregateDeclaration();
if (!ad)
{
error(ctd.loc, "constructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
ctd.type = Type.terror;
ctd.errors = true;
return;
}
sc = sc.push();
if (sc.stc & STC.static_)
{
if (sc.stc & STC.shared_)
error(ctd.loc, "`shared static` has no effect on a constructor inside a `shared static` block. Use `shared static this()`");
else
error(ctd.loc, "`static` has no effect on a constructor inside a `static` block. Use `static this()`");
}
sc.stc &= ~STC.static_; // not a static constructor
funcDeclarationSemantic(sc, ctd);
// Check short constructor: this() => expr;
if (ctd.fbody)
{
if (auto s = ctd.fbody.isExpStatement())
{
if (s.exp)
{
auto ce = s.exp.isCallExp();
// check this/super before semantic
if (!ce || (!ce.e1.isThisExp() && !ce.e1.isSuperExp()))
{
s.exp = s.exp.expressionSemantic(sc);
if (s.exp.type.ty != Tvoid)
error(s.loc, "can only return void expression, `this` call or `super` call from constructor");
}
}
}
}
sc.pop();
if (ctd.errors)
return;
TypeFunction tf = ctd.type.toTypeFunction();
immutable dim = tf.parameterList.length;
auto sd = ad.isStructDeclaration();
/* See if it's the default constructor
* But, template constructor should not become a default constructor.
*/
if (ad && (!ctd.parent.isTemplateInstance() || ctd.parent.isTemplateMixin()))
{
if (!sd)
{
if (dim == 0 && tf.parameterList.varargs == VarArg.none)
ad.defaultCtor = ctd;
return;
}
if (dim == 0 && tf.parameterList.varargs == VarArg.none) // empty default ctor w/o any varargs
{
if (ctd.fbody || !(ctd.storage_class & STC.disable))
{
.error(ctd.loc, "%s `%s` default constructor for structs only allowed " ~
"with `@disable`, no body, and no parameters", ctd.kind, ctd.toPrettyChars);
ctd.storage_class |= STC.disable;
ctd.fbody = null;
}
sd.noDefaultCtor = true;
}
else if (dim == 0 && tf.parameterList.varargs != VarArg.none) // allow varargs only ctor
{
}
else if (dim && !tf.parameterList.hasArgsWithoutDefault)
{
if (ctd.storage_class & STC.disable)
{
.error(ctd.loc, "%s `%s` is marked `@disable`, so it cannot have default "~
"arguments for all parameters.", ctd.kind, ctd.toPrettyChars);
errorSupplemental(ctd.loc, "Use `@disable this();` if you want to disable default initialization.");
}
else
.error(ctd.loc, "%s `%s` all parameters have default arguments, "~
"but structs cannot have default constructors.", ctd.kind, ctd.toPrettyChars);
}
else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
{
//printf("tf: %s\n", toChars(tf));
auto param = tf.parameterList[0];
if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
{
//printf("copy constructor %p\n", ctd);
assert(!ctd.isCpCtor && !ctd.isMoveCtor);
if (param.storageClass & STC.ref_)
ctd.isCpCtor = true; // copy constructor
else
ctd.isMoveCtor = true; // move constructor
assert(!(ctd.isCpCtor && ctd.isMoveCtor));
}
}
}
// https://issues.dlang.org/show_bug.cgi?id=22593
else if (auto ti = ctd.parent.isTemplateInstance())
{
checkHasBothRvalueAndCpCtor(sd, ctd, ti);
}
}
override void visit(PostBlitDeclaration pbd)
{
//printf("PostBlitDeclaration::semantic() %s\n", toChars());
//printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id.dtor.toChars(), ident, Id.dtor);
//printf("stc = x%llx\n", sc.stc);
if (pbd.semanticRun >= PASS.semanticdone)
return;
if (pbd._scope)
{
sc = pbd._scope;
pbd._scope = null;
}
pbd.parent = sc.parent;
Dsymbol p = pbd.toParent2();
StructDeclaration ad = p.isStructDeclaration();
if (!ad)
{
error(pbd.loc, "postblit can only be a member of struct, not %s `%s`", p.kind(), p.toChars());
pbd.type = Type.terror;
pbd.errors = true;
return;
}
if (pbd.ident == Id.postblit && pbd.semanticRun < PASS.semantic)
ad.postblits.push(pbd);
if (!pbd.type)
pbd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, pbd.storage_class);
sc = sc.push();
sc.stc &= ~STC.static_; // not static
sc.linkage = LINK.d;
funcDeclarationSemantic(sc, pbd);
sc.pop();
}
override void visit(DtorDeclaration dd)
{
//printf("DtorDeclaration::semantic() %s\n", dd.toChars());
//printf("ident: %s, %s, %p, %p\n", dd.ident.toChars(), Id.dtor.toChars(), dd.ident, Id.dtor);
if (dd.semanticRun >= PASS.semanticdone)
return;
if (dd._scope)
{
sc = dd._scope;
dd._scope = null;
}
dd.parent = sc.parent;
Dsymbol p = dd.toParent2();
AggregateDeclaration ad = p.isAggregateDeclaration();
if (!ad)
{
error(dd.loc, "destructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
dd.type = Type.terror;
dd.errors = true;
return;
}
if (ad.isClassDeclaration() && ad.classKind == ClassKind.d)
{
// Class destructors are implicitly `scope`
dd.storage_class |= STC.scope_;
}
if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
ad.userDtors.push(dd);
if (!dd.type)
{
dd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dd.storage_class);
if (ad.classKind == ClassKind.cpp && dd.ident == Id.dtor)
{
if (auto cldec = ad.isClassDeclaration())
{
assert (cldec.cppDtorVtblIndex == -1); // double-call check already by dd.type
if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != -1)
{
// override the base virtual
cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex;
}
else if (!dd.isFinal())
{
// reserve the dtor slot for the destructor (which we'll create later)
cldec.cppDtorVtblIndex = cast(int)cldec.vtbl.length;
cldec.vtbl.push(dd);
if (target.cpp.twoDtorInVtable)
cldec.vtbl.push(dd); // deleting destructor uses a second slot
}
}
}
}
sc = sc.push();
sc.stc &= ~STC.static_; // not a static destructor
if (sc.linkage != LINK.cpp)
sc.linkage = LINK.d;
funcDeclarationSemantic(sc, dd);
sc.pop();
}
void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor)
{
if (sd.semanticRun >= PASS.semanticdone)
return;
if (sd._scope)
{
sc = sd._scope;
sd._scope = null;
}
sd.parent = sc.parent;
Dsymbol p = sd.parent.pastMixin();
const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration());
const(char)* what = isDestructor ? "destructor" : "constructor";
if (!p.isScopeDsymbol())
{
const(char)* s = isShared ? "shared " : "";
error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars());
sd.type = Type.terror;
sd.errors = true;
return;
}
if (!sd.type)
sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class);
/* If the static [dc]tor appears within a template instantiation,
* it could get called multiple times by the module constructors
* for different modules. Thus, protect it with a gate.
*/
if (sd.isInstantiated() && sd.semanticRun < PASS.semantic)
{
/* Add this prefix to the constructor:
* ```
* static int gate;
* if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor
* ```
* or, for shared constructor:
* ```
* shared int gate;
* enum op = isDestructor ? "-=" : "+=";
* enum cmp = isDestructor ? 0 : 1;
* if (core.atomic.atomicOp!op(gate, 1) != cmp) return;
* ```
*/
auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none);
auto sa = new Statements();
Statement s = new ExpStatement(Loc.initial, v);
sa.push(s);
Expression e;
if (isShared)
{
e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1));
if (e is null)
{
.error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what);
return;
}
}
else
{
IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1);
e = new AddAssignExp(
Loc.initial, new IdentifierExp(Loc.initial, v.ident), one);
}
IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1;
e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp);
s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
sa.push(s);
if (sd.fbody)
sa.push(sd.fbody);
sd.fbody = new CompoundStatement(Loc.initial, sa);
if (isDestructor)
(cast(StaticDtorDeclaration)sd).vgate = v;
}
const LINK save = sc.linkage;
if (save != LINK.d)
{
const(char)* s = isShared ? "shared " : "";
deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what);
// Just correct it
sc.linkage = LINK.d;
}
funcDeclarationSemantic(sc, sd);
sc.linkage = save;
// We're going to need ModuleInfo
Module m = sd.getModule();
if (!m)
m = sc._module;
if (m)
{
m.needmoduleinfo = 1;
//printf("module2 %s needs moduleinfo\n", m.toChars());
}
}
override void visit(StaticCtorDeclaration scd)
{
//printf("StaticCtorDeclaration::semantic()\n");
visitStaticCDtorDeclaration(scd, false);
foreachUda(scd, sc, (Expression e) {
import dmd.attrib : isEnumAttribute;
if (!isEnumAttribute(e, Id.udaStandalone))
return 0;
if (auto sharedCtor = scd.isSharedStaticCtorDeclaration())
{
auto trust = sharedCtor.type.isTypeFunction().trust;
if (trust != TRUST.system && trust != TRUST.trusted)
error(e.loc, "a module constructor using `@%s` must be `@system` or `@trusted`", Id.udaStandalone.toChars());
sharedCtor.standalone = true;
}
else
.error(e.loc, "`@%s` can only be used on shared static constructors", Id.udaStandalone.toChars());
return 1;
});
}
override void visit(StaticDtorDeclaration sdd)
{
visitStaticCDtorDeclaration(sdd, true);
}
override void visit(InvariantDeclaration invd)
{
if (invd.semanticRun >= PASS.semanticdone)
return;
if (invd._scope)
{
sc = invd._scope;
invd._scope = null;
}
invd.parent = sc.parent;
Dsymbol p = invd.parent.pastMixin();
AggregateDeclaration ad = p.isAggregateDeclaration();
if (!ad)
{
error(invd.loc, "`invariant` can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
invd.type = Type.terror;
invd.errors = true;
return;
}
if (invd.ident != Id.classInvariant &&
invd.semanticRun < PASS.semantic &&
!ad.isUnionDeclaration() // users are on their own with union fields
)
{
invd.fixupInvariantIdent(ad.invs.length);
ad.invs.push(invd);
}
if (!invd.type)
invd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, invd.storage_class);
sc = sc.push();
sc.stc &= ~STC.static_; // not a static invariant
sc.stc |= STC.const_; // invariant() is always const
sc.contract = Contract.invariant_;
sc.linkage = LINK.d;
funcDeclarationSemantic(sc, invd);
sc.pop();
}
override void visit(UnitTestDeclaration utd)
{
if (utd.semanticRun >= PASS.semanticdone)
return;
if (utd._scope)
{
sc = utd._scope;
utd._scope = null;
}
utd.visibility = sc.visibility;
utd.parent = sc.parent;
Dsymbol p = utd.parent.pastMixin();
if (!p.isScopeDsymbol())
{
error(utd.loc, "`unittest` can only be a member of module/aggregate/template, not %s `%s`", p.kind(), p.toChars());
utd.type = Type.terror;
utd.errors = true;
return;
}
if (global.params.useUnitTests)
{
if (!utd.type)
utd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, utd.storage_class);
Scope* sc2 = sc.push();
sc2.linkage = LINK.d;
funcDeclarationSemantic(sc, utd);
sc2.pop();
}
version (none)
{
// We're going to need ModuleInfo even if the unit tests are not
// compiled in, because other modules may import this module and refer
// to this ModuleInfo.
// (This doesn't make sense to me?)
Module m = utd.getModule();
if (!m)
m = sc._module;
if (m)
{
//printf("module3 %s needs moduleinfo\n", m.toChars());
m.needmoduleinfo = 1;
}
}
}
override void visit(NewDeclaration nd)
{
//printf("NewDeclaration::semantic()\n");
if (nd.semanticRun >= PASS.semanticdone)
return;
if (!nd.type)
nd.type = new TypeFunction(ParameterList(), Type.tvoid.pointerTo(), LINK.d, nd.storage_class);
funcDeclarationSemantic(sc, nd);
}
override void visit(StructDeclaration sd)
{
enum log = false;
if (log) printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
//static int count; if (++count == 20) assert(0);
if (sd.semanticRun >= PASS.semanticdone)
return;
const errors = global.errors;
//printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
Scope* scx = null;
if (sd._scope)
{
sc = sd._scope;
scx = sd._scope; // save so we don't make redundant copies
sd._scope = null;
}
if (!sd.parent)
{
assert(sc.parent && sc.func);
sd.parent = sc.parent;
}
assert(sd.parent && !sd.isAnonymous());
if (sd.errors)
sd.type = Type.terror;
if (sd.semanticRun == PASS.initial)
sd.type = sd.type.addSTC(sc.stc | sd.storage_class);
sd.type = sd.type.typeSemantic(sd.loc, sc);
auto ts = sd.type.isTypeStruct();
if (ts)
{
if (ts.sym != sd)
{
TemplateInstance ti = ts.sym.isInstantiated();
if (ti && isError(ti))
ts.sym = sd;
/* For C modules, if module A contains `struct S;` and
* module B contains `struct S { members...}` then replace
* the former with the latter
*/
else if (!ts.sym.members && sd.members)
ts.sym = sd;
}
}
// Ungag errors when not speculative
Ungag ungag = sd.ungagSpeculative();
if (sd.semanticRun == PASS.initial)
{
sd.visibility = sc.visibility;
if (sd.alignment.isUnknown()) // can be set already by `struct __declspec(align(N)) Tag { ... }`
sd.alignment = sc.alignment();
sd.storage_class |= sc.stc;
if (sd.storage_class & STC.abstract_)
.error(sd.loc, "%s `%s` structs, unions cannot be `abstract`", sd.kind, sd.toPrettyChars);
sd.userAttribDecl = sc.userAttribDecl;
if (sc.linkage == LINK.cpp)
sd.classKind = ClassKind.cpp;
else if (sc.linkage == LINK.c)
sd.classKind = ClassKind.c;
sd.cppnamespace = sc.namespace;
sd.cppmangle = sc.cppmangle;
}
else if (sd.symtab && !scx)
return;
sd.semanticRun = PASS.semantic;
checkGNUABITag(sd, sc.linkage);
if (!sd.members) // if opaque declaration
{
if (log) printf("\topaque declaration %s\n", sd.toChars());
sd.semanticRun = PASS.semanticdone;
return;
}
if (!sd.symtab)
{
sd.symtab = new DsymbolTable();
sd.members.foreachDsymbol( s => s.addMember(sc, sd) );
}
auto sc2 = sd.newScope(sc);
/* Set scope so if there are forward references, we still might be able to
* resolve individual members like enums.
*/
sd.members.foreachDsymbol( s => s.setScope(sc2) );
sd.members.foreachDsymbol( s => s.importAll(sc2) );
sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); if (sd.errors) s.errors = true; } );
if (sd.errors)
sd.type = Type.terror;
if (!sd.determineFields())
{
if (sd.type.ty != Terror)
{
.error(sd.loc, "%s `%s` circular or forward reference", sd.kind, sd.toPrettyChars);
sd.errors = true;
sd.type = Type.terror;
}
sc2.pop();
sd.semanticRun = PASS.semanticdone;
return;
}
/* Following special member functions creation needs semantic analysis
* completion of sub-structs in each field types. For example, buildDtor
* needs to check existence of elaborate dtor in type of each fields.
* See the case in compilable/test14838.d
*/
foreach (v; sd.fields)
{
Type tb = v.type.baseElemOf();
if (tb.ty != Tstruct)
continue;
auto sdec = (cast(TypeStruct)tb).sym;
if (sdec.semanticRun >= PASS.semanticdone)
continue;
sc2.pop();
if (log) printf("\tdeferring %s\n", sd.toChars());
return deferDsymbolSemantic(sc, sd, scx);
}
/* Look for special member functions.
*/
sd.disableNew = sd.search(Loc.initial, Id.classNew) !is null;
// Look for the constructor
sd.ctor = sd.searchCtor();
buildDtors(sd, sc2);
bool hasCopyCtor;
bool hasMoveCtor;
bool needCopyCtor;
bool needMoveCtor;
needCopyOrMoveCtor(sd, hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
//printf("%s hasCopy %d hasMove %d needCopy %d needMove %d\n", sd.toChars(), hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
/* When generating a move ctor, generate a copy ctor too, otherwise
* https://github.com/s-ludwig/taggedalgebraic/issues/75
*/
if (0 && needMoveCtor && !hasCopyCtor)
{
needCopyCtor = true;
}
if (needCopyCtor)
{
assert(hasCopyCtor == false);
buildCopyOrMoveCtor(sd, sc2, false); // build copy constructor
hasCopyCtor = true;
}
if (needMoveCtor)
{
assert(hasMoveCtor == false);
buildCopyOrMoveCtor(sd, sc2, true); // build move constructor
hasMoveCtor = true;
}
sd.hasCopyCtor = hasCopyCtor;
sd.hasMoveCtor = hasMoveCtor;
sd.postblit = buildPostBlit(sd, sc2);
buildOpAssign(sd, sc2);
buildOpEquals(sd, sc2);
if (!sc2.inCfile &&
global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo
{
sd.xeq = buildXopEquals(sd, sc2);
sd.xcmp = buildXopCmp(sd, sc2);
sd.xhash = buildXtoHash(sd, sc2);
}
sd.inv = buildInv(sd, sc2);
sd.semanticRun = PASS.semanticdone;
if (log) printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
sc2.pop();
if (sd.ctor)
{
Dsymbol scall = sd.search(Loc.initial, Id.opCall);
if (scall)
{
const xerrors = global.startGagging();
sc = sc.push();
sc.tinst = null;
sc.minst = null;
auto fcall = resolveFuncCall(sd.loc, sc, scall, null, null, ArgumentList(), FuncResolveFlag.quiet);
sc = sc.pop();
global.endGagging(xerrors);
if (fcall && fcall.isStatic())
{
.error(fcall.loc, "%s `%s` `static opCall` is hidden by constructors and can never be called", sd.kind, sd.toPrettyChars);
errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
}
}
}
if (ts && ts.sym != sd)
{
StructDeclaration sym = ts.sym;
if (sd.isCsymbol() && sym.isCsymbol())
{
if (!isCCompatible(sd, sym))
{
// Already issued an error.
errorSupplemental(sd.loc, "C %ss with the same name from different imports are merged", sd.kind);
}
else {
/* This is two structs imported from different C files.
* Just ignore sd, the second one. The first one will always
* be found when going through the type.
*/
}
}
else
{
version (none)
{
printf("this = %p %s\n", sd, sd.toChars());
printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars());
}
// https://issues.dlang.org/show_bug.cgi?id=19024
.error(sd.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", sd.kind, sd.toPrettyChars, sym.loc.toChars());
}
}
if (global.errors != errors)
{
// The type is no good.
sd.type = Type.terror;
sd.errors = true;
if (sd.deferred)
sd.deferred.errors = true;
}
if (sd.deferred && !global.gag)
{
sd.deferred.semantic2(sc);
sd.deferred.semantic3(sc);
}
version (none)
{
// @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
// Deprecated in 2.100
// Make an error in 2.110
if (sd.storage_class & STC.scope_)
deprecation(sd.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
}
//printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
}
//
// Checks if two structs are compatible
// Implements the rules according to C23 6.2.7
//
static bool isCCompatible(StructDeclaration a, StructDeclaration b)
{
// Get the name of a type, while avoiding exposing "__tagXXX" anonymous structs
static const(char)* typeName(Type t)
{
if (TypeStruct ts = t.isTypeStruct())
{
if (ts.sym.ident.toString().startsWith("__tag"))
return ts.sym.isUnionDeclaration() ? "(anonymous union)".ptr: "(anonymous struct)".ptr;
}
return t.toChars();
}
void incompatError()
{
.error(a.loc, "%s `%s` already exists with an incompatible definition.",
a.kind, typeName(a.type));
errorSupplemental(b.loc, "previously declared here");
}
// For recursive calls into unnamed structs (so Type.equals() doesn't work).
static bool isCCompatibleUnnamedStruct(Type a, Type b)
{
TypeStruct ats = a.isTypeStruct();
if (!ats) return false;
TypeStruct bts = b.isTypeStruct();
if (!bts) return false;
// Hack, anonymous structs within a struct are given
// an anonymous id starting with __tag.
if (!ats.sym.ident.toString().startsWith("__tag"))
return false;
if (!bts.sym.ident.toString().startsWith("__tag"))
return false;
return isCCompatible(ats.sym, bts.sym);
}
if (a.fields.length != b.fields.length)
{
incompatError();
errorSupplemental(a.loc, "`%s` has %zu field(s) while `%s` has %zu field(s)",
a.toPrettyChars(), a.fields.length, b.toPrettyChars(), b.fields.length);
return false;
}
// both are structs or both are unions
if ((a.isUnionDeclaration() is null) != (b.isUnionDeclaration() is null))
{
incompatError();
errorSupplemental(a.loc, "`%s` is a %s while `%s` is a %s",
a.toPrettyChars(), a.kind, b.toPrettyChars(), b.kind);
return false;
}
if (a.alignment != b.alignment)
{
incompatError();
errorSupplemental(a.loc, "`%s` has different alignment or packing", a.toPrettyChars());
if (a.alignment.isDefault() && ! b.alignment.isDefault())
{
errorSupplemental(a.loc, "`%s` alignment: default", a.toPrettyChars());
errorSupplemental(b.loc, "`%s` alignment: %u",
b.toPrettyChars(), cast(uint)b.alignment.get());
}
else if (!a.alignment.isDefault() && b.alignment.isDefault())
{
errorSupplemental(a.loc, "`%s` alignment: %u",
a.toPrettyChars(), cast(uint)a.alignment.get());
errorSupplemental(b.loc, "`%s` alignment: default",
b.toPrettyChars());
}
else if (a.alignment.get() != b.alignment.get())
{
errorSupplemental(a.loc, "`%s` alignment: %u",
a.toPrettyChars(), cast(uint)a.alignment.get());
errorSupplemental(b.loc, "`%s` alignment: %u",
b.toPrettyChars(), cast(uint)b.alignment.get());
}
if (a.alignment.isPack() != b.alignment.isPack())
{
errorSupplemental(a.loc, "`%s` packed: %s",
a.toPrettyChars(), a.alignment.isPack()?"true".ptr:"false".ptr);
errorSupplemental(b.loc, "`%s` packed: %s",
b.toPrettyChars(), b.alignment.isPack()?"true".ptr:"false".ptr);
}
return false;
}
foreach (size_t i, VarDeclaration a_field; a.fields[])
{
VarDeclaration b_field = b.fields[i];
//
// — there shall be a one-to-one correspondence between
// their members such that each pair of corresponding
// members are declared with compatible types;
//
if (!a_field.type.equals(b_field.type) && !isCCompatibleUnnamedStruct(a_field.type, b_field.type))
{
// Already errored, just bail
incompatError();
if (a_field.type.isTypeError()) return false;
if (b_field.type.isTypeError()) return false;
errorSupplemental(a_field.loc, "Field %zu differs in type", i);
errorSupplemental(a_field.loc, "typeof(%s): %s",
a_field.toChars(), typeName(a_field.type));
errorSupplemental(b_field.loc, "typeof(%s): %s",
b_field.toChars(), typeName(b_field.type));
return false;
}
//
// — if one member of the pair is declared with an
// alignment specifier, the second is declared with an
// equivalent alignment specifier;
//
if (a_field.alignment != b_field.alignment)
{
incompatError();
errorSupplemental(a_field.loc, "Field %zu differs in alignment or packing", i);
if (a_field.alignment.isDefault() && ! b_field.alignment.isDefault())
{
errorSupplemental(a_field.loc, "`%s.%s` alignment: default",
a.toPrettyChars(),a_field.toChars());
errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
b.toPrettyChars(), b_field.toChars(), cast(uint)b_field.alignment.get());
}
else if (!a_field.alignment.isDefault() && b_field.alignment.isDefault())
{
errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
a.toPrettyChars(), a_field.toChars(), cast(uint)a_field.alignment.get());
errorSupplemental(b_field.loc, "`%s.%s` alignment: default",
b.toPrettyChars(), b_field.toChars());
}
else if (a_field.alignment.get() != b_field.alignment.get())
{
errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
a.toPrettyChars(), a_field.toChars(),
cast(uint)a_field.alignment.get());
errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
b.toPrettyChars(), b_field.toChars(),
cast(uint)b_field.alignment.get());
}
if (a_field.alignment.isPack() != b_field.alignment.isPack())
{
errorSupplemental(a_field.loc, "`%s.%s` packed: %s",
a.toPrettyChars(), a_field.toChars(),
a_field.alignment.isPack()?"true".ptr:"false".ptr);
errorSupplemental(b_field.loc, "`%s.%s` packed: %s",
b.toPrettyChars(), b_field.toChars(),
b_field.alignment.isPack()?"true".ptr:"false".ptr);
}
return false;
}
//
// - and, if one member of the pair is declared with a
// name, the second is declared with the same name.
//
if (a_field.ident.isAnonymous())
{
if (!b_field.ident.isAnonymous())
{
incompatError();
errorSupplemental(a_field.loc, "Field %zu differs in name", i);
errorSupplemental(a_field.loc, "(anonymous)", a_field.ident.toChars());
errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
return false;
}
}
else if (b_field.ident.isAnonymous())
{
incompatError();
errorSupplemental(a_field.loc, "Field %zu differs in name", i);
errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
errorSupplemental(b_field.loc, "(anonymous)");
return false;
}
else if (a_field.ident != b_field.ident)
{
incompatError();
errorSupplemental(a_field.loc, "Field %zu differs in name", i);
errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
return false;
}
//
// For two structures or unions, corresponding bit-fields shall have the same widths.
//
BitFieldDeclaration bfa = a_field.isBitFieldDeclaration();
BitFieldDeclaration bfb = b_field.isBitFieldDeclaration();
if ((bfa is null) != (bfb is null))
{
incompatError();
errorSupplemental(a_field.loc, "Field %zu differs in being a bitfield", i);
if (bfa is null)
{
errorSupplemental(a_field.loc, "`%s.%s` is not a bitfield",
a.toPrettyChars(), a_field.toChars());
errorSupplemental(b_field.loc, "`%s.%s` is a bitfield",
b.toPrettyChars(), b_field.toChars());
}
else if (bfb is null)
{
errorSupplemental(a_field.loc, "`%s.%s` *is a bitfield",
a.toPrettyChars(), a_field.toChars());
errorSupplemental(b_field.loc, "`%s.%s` is not a bitfield",
b.toPrettyChars(), b_field.toChars());
}
return false;
}
if (bfa !is null && bfb !is null)
{
if (bfa.fieldWidth != bfb.fieldWidth)
{
incompatError();
errorSupplemental(a_field.loc, "Field %zu differs in bitfield width", i);
errorSupplemental(a_field.loc, "`%s.%s`: %u",
a.toPrettyChars(), a_field.toChars(), bfa.fieldWidth);
errorSupplemental(b_field.loc, "`%s.%s`: %u",
b.toPrettyChars(), b_field.toChars(), bfb.fieldWidth);
return false;
}
}
}
return true;
}
void interfaceSemantic(ClassDeclaration cd)
{
cd.vtblInterfaces = new BaseClasses();
cd.vtblInterfaces.reserve(cd.interfaces.length);
foreach (b; cd.interfaces)
{
cd.vtblInterfaces.push(b);
b.copyBaseInterfaces(cd.vtblInterfaces);
}
}
override void visit(ClassDeclaration cldec)
{
//printf("ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", cldec.toChars(), cldec.type, cldec.sizeok, this);
//printf("\tparent = %p, '%s'\n", sc.parent, sc.parent ? sc.parent.toChars() : "");
//printf("sc.stc = %x\n", sc.stc);
//{ static int n; if (++n == 20) *(char*)0=0; }
if (cldec.semanticRun >= PASS.semanticdone)
return;
const errors = global.errors;
//printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
Scope* scx = null;
if (cldec._scope)
{
sc = cldec._scope;
scx = cldec._scope; // save so we don't make redundant copies
cldec._scope = null;
}
if (!cldec.parent)
{
assert(sc.parent);
cldec.parent = sc.parent;
}
if (cldec.errors)
cldec.type = Type.terror;
if (cldec.semanticRun == PASS.initial)
cldec.type = cldec.type.addSTC(sc.stc | cldec.storage_class);
cldec.type = cldec.type.typeSemantic(cldec.loc, sc);
if (auto tc = cldec.type.isTypeClass())
if (tc.sym != cldec)
{
auto ti = tc.sym.isInstantiated();
if (ti && isError(ti))
tc.sym = cldec;
}
// Ungag errors when not speculative
Ungag ungag = cldec.ungagSpeculative();
if (cldec.semanticRun == PASS.initial)
{
cldec.visibility = sc.visibility;
cldec.storage_class |= sc.stc;
if (cldec.storage_class & STC.auto_)
.error(cldec.loc, "%s `%s` storage class `auto` is invalid when declaring a class, did you mean to use `scope`?", cldec.kind, cldec.toPrettyChars);
if (cldec.storage_class & STC.scope_)
cldec.stack = true;
if (cldec.storage_class & STC.abstract_)
cldec.isabstract = ThreeState.yes;
cldec.userAttribDecl = sc.userAttribDecl;
if (sc.linkage == LINK.cpp)
cldec.classKind = ClassKind.cpp;
cldec.cppnamespace = sc.namespace;
cldec.cppmangle = sc.cppmangle;
if (sc.linkage == LINK.objc)
objc.setObjc(cldec);
}
else if (cldec.symtab && !scx)
{
return;
}
cldec.semanticRun = PASS.semantic;
checkGNUABITag(cldec, sc.linkage);
checkMustUseReserved(cldec);
if (cldec.baseok < Baseok.done)
{
/* https://issues.dlang.org/show_bug.cgi?id=12078
* https://issues.dlang.org/show_bug.cgi?id=12143
* https://issues.dlang.org/show_bug.cgi?id=15733
* While resolving base classes and interfaces, a base may refer
* the member of this derived class. In that time, if all bases of
* this class can be determined, we can go forward the semantc process
* beyond the Lancestorsdone. To do the recursive semantic analysis,
* temporarily set and unset `_scope` around exp().
*/
T resolveBase(T)(lazy T exp)
{
if (!scx)
{
scx = sc.copy();
scx.setNoFree();
}
static if (!is(T == void))
{
cldec._scope = scx;
auto r = exp();
cldec._scope = null;
return r;
}
else
{
cldec._scope = scx;
exp();
cldec._scope = null;
}
}
cldec.baseok = Baseok.start;
// Expand any tuples in baseclasses[]
for (size_t i = 0; i < cldec.baseclasses.length;)
{
auto b = (*cldec.baseclasses)[i];
b.type = resolveBase(b.type.typeSemantic(cldec.loc, sc));
Type tb = b.type.toBasetype();
if (auto tup = tb.isTypeTuple())
{
cldec.baseclasses.remove(i);
size_t dim = Parameter.dim(tup.arguments);
for (size_t j = 0; j < dim; j++)
{
Parameter arg = Parameter.getNth(tup.arguments, j);
b = new BaseClass(arg.type);
cldec.baseclasses.insert(i + j, b);
}
}
else
i++;
}
if (cldec.baseok >= Baseok.done)
{
//printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
if (cldec.semanticRun >= PASS.semanticdone)
return;
goto Lancestorsdone;
}
// See if there's a base class as first in baseclasses[]
if (cldec.baseclasses.length)
{
BaseClass* b = (*cldec.baseclasses)[0];
Type tb = b.type.toBasetype();
TypeClass tc = tb.isTypeClass();
if (!tc)
{
if (b.type != Type.terror)
.error(cldec.loc, "%s `%s` base type must be `class` or `interface`, not `%s`", cldec.kind, cldec.toPrettyChars, b.type.toChars());
cldec.baseclasses.remove(0);
goto L7;
}
if (tc.sym.isDeprecated())
{
if (!cldec.isDeprecated())
{
// Deriving from deprecated class makes this one deprecated too
cldec.setDeprecated();
tc.checkDeprecated(cldec.loc, sc);
}
}
if (tc.sym.isInterfaceDeclaration())
goto L7;
for (ClassDeclaration cdb = tc.sym; cdb; cdb = cdb.baseClass)
{
if (cdb == cldec)
{
.error(cldec.loc, "%s `%s` circular inheritance", cldec.kind, cldec.toPrettyChars);
cldec.baseclasses.remove(0);
goto L7;
}
}
/* https://issues.dlang.org/show_bug.cgi?id=11034
* Class inheritance hierarchy
* and instance size of each classes are orthogonal information.
* Therefore, even if tc.sym.sizeof == Sizeok.none,
* we need to set baseClass field for class covariance check.
*/
cldec.baseClass = tc.sym;
b.sym = cldec.baseClass;
if (tc.sym.baseok < Baseok.done)
resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
if (tc.sym.baseok < Baseok.done)
{
//printf("\ttry later, forward reference of base class %s\n", tc.sym.toChars());
if (tc.sym._scope)
Module.addDeferredSemantic(tc.sym);
cldec.baseok = Baseok.none;
}
L7:
}
// Treat the remaining entries in baseclasses as interfaces
// Check for errors, handle forward references
int multiClassError = cldec.baseClass is null ? 0 : 1;
BCLoop:
for (size_t i = (cldec.baseClass ? 1 : 0); i < cldec.baseclasses.length;)
{
BaseClass* b = (*cldec.baseclasses)[i];
Type tb = b.type.toBasetype();
TypeClass tc = tb.isTypeClass();
if (!tc || !tc.sym.isInterfaceDeclaration())
{
// It's a class
if (tc)
{
if (multiClassError == 0)
{
.error(cldec.loc,"`%s`: base class must be specified first, " ~
"before any interfaces.", cldec.toPrettyChars());
multiClassError += 1;
}
else if (multiClassError >= 1)
{
if(multiClassError == 1)
.error(cldec.loc, "`%s`: multiple class inheritance is not supported." ~
" Use multiple interface inheritance and/or composition.", cldec.toPrettyChars());
multiClassError += 1;
if (tc.sym.fields.length)
errorSupplemental(cldec.loc,"`%s` has fields, consider making it a member of `%s`",
b.type.toChars(), cldec.type.toChars());
else
errorSupplemental(cldec.loc,"`%s` has no fields, consider making it an `interface`",
b.type.toChars());
}
}
// It's something else: e.g. `int` in `class Foo : Bar, int { ... }`
else if (b.type != Type.terror)
{
error(cldec.loc,"`%s`: base type must be `interface`, not `%s`",
cldec.toPrettyChars(), b.type.toChars());
}
cldec.baseclasses.remove(i);
continue;
}
// Check for duplicate interfaces
for (size_t j = (cldec.baseClass ? 1 : 0); j < i; j++)
{
BaseClass* b2 = (*cldec.baseclasses)[j];
if (b2.sym == tc.sym)
{
.error(cldec.loc, "%s `%s` inherits from duplicate interface `%s`", cldec.kind, cldec.toPrettyChars, b2.sym.toChars());
cldec.baseclasses.remove(i);
continue BCLoop;
}
}
if (tc.sym.isDeprecated())
{
if (!cldec.isDeprecated())
{
// Deriving from deprecated class makes this one deprecated too
cldec.setDeprecated();
tc.checkDeprecated(cldec.loc, sc);
}
}
b.sym = tc.sym;
if (tc.sym.baseok < Baseok.done)
resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
if (tc.sym.baseok < Baseok.done)
{
//printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
if (tc.sym._scope)
Module.addDeferredSemantic(tc.sym);
cldec.baseok = Baseok.none;
}
i++;
}
if (cldec.baseok == Baseok.none)
{
// Forward referencee of one or more bases, try again later
//printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
return deferDsymbolSemantic(sc, cldec, scx);
}
cldec.baseok = Baseok.done;
if (cldec.classKind == ClassKind.objc || (cldec.baseClass && cldec.baseClass.classKind == ClassKind.objc))
cldec.classKind = ClassKind.objc; // Objective-C classes do not inherit from Object
// If no base class, and this is not an Object, use Object as base class
if (!cldec.baseClass && cldec.ident != Id.Object && cldec.object && cldec.classKind == ClassKind.d)
{
void badObjectDotD()
{
ObjectNotFound(cldec.loc, cldec.ident);
}
if (!cldec.object || cldec.object.errors)
badObjectDotD();
Type t = cldec.object.type;
t = t.typeSemantic(cldec.loc, sc).toBasetype();
if (t.ty == Terror)
badObjectDotD();
TypeClass tc = t.isTypeClass();
assert(tc);
auto b = new BaseClass(tc);
cldec.baseclasses.shift(b);
cldec.baseClass = tc.sym;
assert(!cldec.baseClass.isInterfaceDeclaration());
b.sym = cldec.baseClass;
}
if (cldec.baseClass)
{
if (cldec.baseClass.storage_class & STC.final_)
.error(cldec.loc, "%s `%s` cannot inherit from class `%s` because it is `final`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
// Inherit properties from base class
if (cldec.baseClass.isCOMclass())
cldec.com = true;
if (cldec.baseClass.isCPPclass())
cldec.classKind = ClassKind.cpp;
if (cldec.classKind != cldec.baseClass.classKind)
.error(cldec.loc, "%s `%s` with %s linkage cannot inherit from class `%s` with %s linkage", cldec.kind, cldec.toPrettyChars,
ClassKindToChars(cldec.classKind), cldec.baseClass.toChars(), ClassKindToChars(cldec.baseClass.classKind));
if (cldec.baseClass.stack)
cldec.stack = true;
cldec.enclosing = cldec.baseClass.enclosing;
cldec.storage_class |= cldec.baseClass.storage_class & STC.TYPECTOR;
}
cldec.interfaces = cldec.baseclasses.tdata()[(cldec.baseClass ? 1 : 0) .. cldec.baseclasses.length];
foreach (b; cldec.interfaces)
{
// If this is an interface, and it derives from a COM interface,
// then this is a COM interface too.
if (b.sym.isCOMinterface())
cldec.com = true;
if (cldec.classKind == ClassKind.cpp && !b.sym.isCPPinterface())
{
.error(cldec.loc, "C++ class `%s` cannot implement D interface `%s`",
cldec.toPrettyChars(), b.sym.toPrettyChars());
}
}
interfaceSemantic(cldec);
}
Lancestorsdone:
//printf("\tClassDeclaration.dsymbolSemantic(%s) baseok = %d\n", toChars(), baseok);
if (!cldec.members) // if opaque declaration
{
cldec.semanticRun = PASS.semanticdone;
return;
}
if (!cldec.symtab)
{
cldec.symtab = new DsymbolTable();
/* https://issues.dlang.org/show_bug.cgi?id=12152
* The semantic analysis of base classes should be finished
* before the members semantic analysis of this class, in order to determine
* vtbl in this class. However if a base class refers the member of this class,
* it can be resolved as a normal forward reference.
* Call addMember() and setScope() to make this class members visible from the base classes.
*/
cldec.members.foreachDsymbol( s => s.addMember(sc, cldec) );
auto sc2 = cldec.newScope(sc);
/* Set scope so if there are forward references, we still might be able to
* resolve individual members like enums.
*/
cldec.members.foreachDsymbol( s => s.setScope(sc2) );
sc2.pop();
}
for (size_t i = 0; i < cldec.baseclasses.length; i++)
{
BaseClass* b = (*cldec.baseclasses)[i];
Type tb = b.type.toBasetype();
TypeClass tc = tb.isTypeClass();
if (tc.sym.semanticRun < PASS.semanticdone)
{
// Forward referencee of one or more bases, try again later
if (tc.sym._scope)
Module.addDeferredSemantic(tc.sym);
//printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
return deferDsymbolSemantic(sc, cldec, scx);
}
}
if (cldec.baseok == Baseok.done)
{
cldec.baseok = Baseok.semanticdone;
objc.setMetaclass(cldec, sc);
// initialize vtbl
if (cldec.baseClass)
{
if (cldec.classKind == ClassKind.cpp && cldec.baseClass.vtbl.length == 0)
{
.error(cldec.loc, "%s `%s` C++ base class `%s` needs at least one virtual function", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
}
// Copy vtbl[] from base class
assert(cldec.vtbl.length == 0);
cldec.vtbl.setDim(cldec.baseClass.vtbl.length);
memcpy(cldec.vtbl.tdata(), cldec.baseClass.vtbl.tdata(), (void*).sizeof * cldec.vtbl.length);
cldec.vthis = cldec.baseClass.vthis;
cldec.vthis2 = cldec.baseClass.vthis2;
}
else
{
// No base class, so this is the root of the class hierarchy
cldec.vtbl.setDim(0);
if (cldec.vtblOffset())
cldec.vtbl.push(cldec); // leave room for classinfo as first member
}
/* If this is a nested class, add the hidden 'this'
* member which is a pointer to the enclosing scope.
*/
if (cldec.vthis) // if inheriting from nested class
{
// Use the base class's 'this' member
if (cldec.storage_class & STC.static_)
.error(cldec.loc, "%s `%s` static class cannot inherit from nested class `%s`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
if (cldec.toParentLocal() != cldec.baseClass.toParentLocal() &&
(!cldec.toParentLocal() ||
!cldec.baseClass.toParentLocal().getType() ||
!cldec.baseClass.toParentLocal().getType().isBaseOf(cldec.toParentLocal().getType(), null)))
{
if (cldec.toParentLocal())
{
.error(cldec.loc, "%s `%s` is nested within `%s`, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
cldec.toParentLocal().toChars(),
cldec.baseClass.toChars(),
cldec.baseClass.toParentLocal().toChars());
}
else
{
.error(cldec.loc, "%s `%s` is not nested, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
cldec.baseClass.toChars(),
cldec.baseClass.toParentLocal().toChars());
}
}
if (cldec.vthis2)
{
if (cldec.toParent2() != cldec.baseClass.toParent2() &&
(!cldec.toParent2() ||
!cldec.baseClass.toParent2().getType() ||
!cldec.baseClass.toParent2().getType().isBaseOf(cldec.toParent2().getType(), null)))
{
if (cldec.toParent2() && cldec.toParent2() != cldec.toParentLocal())
{
.error(cldec.loc, "%s `%s` needs the frame pointer of `%s`, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
cldec.toParent2().toChars(),
cldec.baseClass.toChars(),
cldec.baseClass.toParent2().toChars());
}
else
{
.error(cldec.loc, "%s `%s` doesn't need a frame pointer, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
cldec.baseClass.toChars(),
cldec.baseClass.toParent2().toChars());
}
}
}
else
cldec.makeNested2();
}
else
cldec.makeNested();
}
auto sc2 = cldec.newScope(sc);
cldec.members.foreachDsymbol( s => s.importAll(sc2) );
// Note that members.length can grow due to tuple expansion during semantic()
cldec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
if (!cldec.determineFields())
{
assert(cldec.type == Type.terror);
sc2.pop();
return;
}
/* Following special member functions creation needs semantic analysis
* completion of sub-structs in each field types.
*/
foreach (v; cldec.fields)
{
Type tb = v.type.baseElemOf();
if (tb.ty != Tstruct)
continue;
auto sd = (cast(TypeStruct)tb).sym;
if (sd.semanticRun >= PASS.semanticdone)
continue;
sc2.pop();
//printf("\tdeferring %s\n", toChars());
return deferDsymbolSemantic(sc, cldec, scx);
}
/* Look for special member functions.
* They must be in this class, not in a base class.
*/
// Can be in base class
cldec.disableNew = cldec.search(Loc.initial, Id.classNew) !is null;
// Look for the constructor
cldec.ctor = cldec.searchCtor();
if (!cldec.ctor && cldec.noDefaultCtor)
{
// A class object is always created by constructor, so this check is legitimate.
foreach (v; cldec.fields)
{
if (v.storage_class & STC.nodefaultctor)
error(v.loc, "field `%s` must be initialized in constructor", v.toChars());
}
}
// If this class has no constructor, but base class has a default
// ctor, create a constructor:
// this() { }
if (!cldec.ctor && cldec.baseClass && cldec.baseClass.ctor)
{
auto fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type, ArgumentList(), FuncResolveFlag.quiet);
if (!fd) // try shared base ctor instead
fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type.sharedOf, ArgumentList(), FuncResolveFlag.quiet);
if (fd && !fd.errors)
{
//printf("Creating default this(){} for class %s\n", toChars());
auto btf = fd.type.toTypeFunction();
auto tf = new TypeFunction(ParameterList(), null, LINK.d, fd.storage_class);
tf.mod = btf.mod;
// Don't copy @safe, ... from the base class constructor and let it be inferred instead
// This is required if other lowerings add code to the generated constructor which
// is less strict (e.g. `preview=dtorfields` might introduce a call to a less qualified dtor)
auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, STC.none, tf);
ctor.storage_class |= STC.inference | (fd.storage_class & STC.scope_);
ctor.isGenerated = true;
ctor.fbody = new CompoundStatement(Loc.initial, new Statements());
cldec.members.push(ctor);
ctor.addMember(sc, cldec);
ctor.dsymbolSemantic(sc2);
cldec.ctor = ctor;
cldec.defaultCtor = ctor;
}
else
{
.error(cldec.loc, "%s `%s` cannot implicitly generate a default constructor when base class `%s` is missing a default constructor", cldec.kind, cldec.toPrettyChars,
cldec.baseClass.toPrettyChars());
}
}
buildDtors(cldec, sc2);
if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1)
{
// now we've built the aggregate destructor, we'll make it virtual and assign it to the reserved vtable slot
cldec.dtor.vtblIndex = cldec.cppDtorVtblIndex;
cldec.vtbl[cldec.cppDtorVtblIndex] = cldec.dtor;
if (target.cpp.twoDtorInVtable)
{
// TODO: create a C++ compatible deleting destructor (call out to `operator delete`)
// for the moment, we'll call the non-deleting destructor and leak
cldec.vtbl[cldec.cppDtorVtblIndex + 1] = cldec.dtor;
}
}
if (auto f = hasIdentityOpAssign(cldec, sc2))
{
if (!(f.storage_class & STC.disable))
.error(f.loc, "%s `%s` identity assignment operator overload is illegal", cldec.kind, cldec.toPrettyChars);
}
cldec.inv = buildInv(cldec, sc2);
cldec.semanticRun = PASS.semanticdone;
//printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
sc2.pop();
/* isAbstract() is undecidable in some cases because of circular dependencies.
* Now that semantic is finished, get a definitive result, and error if it is not the same.
*/
if (cldec.isabstract != ThreeState.none) // if evaluated it before completion
{
const isabstractsave = cldec.isabstract;
cldec.isabstract = ThreeState.none;
cldec.isAbstract(); // recalculate
if (cldec.isabstract != isabstractsave)
{
.error(cldec.loc, "%s `%s` cannot infer `abstract` attribute due to circular dependencies", cldec.kind, cldec.toPrettyChars);
}
}
if (cldec.type.ty == Tclass && (cast(TypeClass)cldec.type).sym != cldec)
{
// https://issues.dlang.org/show_bug.cgi?id=17492
ClassDeclaration cd = (cast(TypeClass)cldec.type).sym;
version (none)
{
printf("this = %p %s\n", cldec, cldec.toPrettyChars());
printf("type = %d sym = %p, %s\n", cldec.type.ty, cd, cd.toPrettyChars());
}
.error(cldec.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", cldec.kind, cldec.toPrettyChars, cd.loc.toChars());
}
if (global.errors != errors || (cldec.baseClass && cldec.baseClass.errors))
{
// The type is no good, but we should keep the
// the type so that we have more accurate error messages
// See: https://issues.dlang.org/show_bug.cgi?id=23552
cldec.errors = true;
if (cldec.deferred)
cldec.deferred.errors = true;
}
// Verify fields of a synchronized class are not public
if (cldec.storage_class & STC.synchronized_)
{
foreach (vd; cldec.fields)
{
if (!vd.isThisDeclaration() &&
vd.visible() >= Visibility(Visibility.Kind.public_))
{
.error(vd.loc, "%s `%s` Field members of a `synchronized` class cannot be `%s`", vd.kind, vd.toPrettyChars,
visibilityToChars(vd.visible().kind));
}
}
}
if (cldec.deferred && !global.gag)
{
cldec.deferred.semantic2(sc);
cldec.deferred.semantic3(sc);
}
//printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
version (none)
{
// @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
// Deprecated in 2.100
// Make an error in 2.110
// Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
if (cldec.storage_class & STC.scope_)
deprecation(cldec.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
}
}
override void visit(InterfaceDeclaration idec)
{
/// Returns: `true` is this is an anonymous Objective-C metaclass
static bool isAnonymousMetaclass(InterfaceDeclaration idec)
{
return idec.classKind == ClassKind.objc &&
idec.objc.isMeta &&
idec.isAnonymous;
}
//printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
if (idec.semanticRun >= PASS.semanticdone)
return;
const errors = global.errors;
//printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
Scope* scx = null;
if (idec._scope)
{
sc = idec._scope;
scx = idec._scope; // save so we don't make redundant copies
idec._scope = null;
}
if (!idec.parent)
{
assert(sc.parent && sc.func);
idec.parent = sc.parent;
}
// Objective-C metaclasses are anonymous
assert(idec.parent && !idec.isAnonymous || isAnonymousMetaclass(idec));
if (idec.errors)
idec.type = Type.terror;
idec.type = idec.type.typeSemantic(idec.loc, sc);
if (idec.type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
{
auto ti = (cast(TypeClass)idec.type).sym.isInstantiated();
if (ti && isError(ti))
(cast(TypeClass)idec.type).sym = idec;
}
// Ungag errors when not speculative
Ungag ungag = idec.ungagSpeculative();
if (idec.semanticRun == PASS.initial)
{
idec.visibility = sc.visibility;
idec.storage_class |= sc.stc;
idec.userAttribDecl = sc.userAttribDecl;
}
else if (idec.symtab)
{
if (idec.sizeok == Sizeok.done || !scx)
{
idec.semanticRun = PASS.semanticdone;
return;
}
}
idec.semanticRun = PASS.semantic;
if (idec.baseok < Baseok.done)
{
T resolveBase(T)(lazy T exp)
{
if (!scx)
{
scx = sc.copy();
scx.setNoFree();
}
static if (!is(T == void))
{
idec._scope = scx;
auto r = exp();
idec._scope = null;
return r;
}
else
{
idec._scope = scx;
exp();
idec._scope = null;
}
}
idec.baseok = Baseok.start;
// Expand any tuples in baseclasses[]
for (size_t i = 0; i < idec.baseclasses.length;)
{
auto b = (*idec.baseclasses)[i];
b.type = resolveBase(b.type.typeSemantic(idec.loc, sc));
Type tb = b.type.toBasetype();
if (auto tup = tb.isTypeTuple())
{
idec.baseclasses.remove(i);
size_t dim = Parameter.dim(tup.arguments);
for (size_t j = 0; j < dim; j++)
{
Parameter arg = Parameter.getNth(tup.arguments, j);
b = new BaseClass(arg.type);
idec.baseclasses.insert(i + j, b);
}
}
else
i++;
}
if (idec.baseok >= Baseok.done)
{
//printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
if (idec.semanticRun >= PASS.semanticdone)
return;
goto Lancestorsdone;
}
if (!idec.baseclasses.length && sc.linkage == LINK.cpp)
idec.classKind = ClassKind.cpp;
idec.cppnamespace = sc.namespace;
checkGNUABITag(idec, sc.linkage);
checkMustUseReserved(idec);
if (sc.linkage == LINK.objc)
objc.setObjc(idec);
// Check for errors, handle forward references
BCLoop:
for (size_t i = 0; i < idec.baseclasses.length;)
{
BaseClass* b = (*idec.baseclasses)[i];
Type tb = b.type.toBasetype();
TypeClass tc = (tb.ty == Tclass) ? cast(TypeClass)tb : null;
if (!tc || !tc.sym.isInterfaceDeclaration())
{
if (b.type != Type.terror)
.error(idec.loc, "%s `%s` base type must be `interface`, not `%s`", idec.kind, idec.toPrettyChars, b.type.toChars());
idec.baseclasses.remove(i);
continue;
}
// Check for duplicate interfaces
for (size_t j = 0; j < i; j++)
{
BaseClass* b2 = (*idec.baseclasses)[j];
if (b2.sym == tc.sym)
{
.error(idec.loc, "%s `%s` inherits from duplicate interface `%s`", idec.kind, idec.toPrettyChars, b2.sym.toChars());
idec.baseclasses.remove(i);
continue BCLoop;
}
}
if (tc.sym == idec || idec.isBaseOf2(tc.sym))
{
.error(idec.loc, "%s `%s` circular inheritance of interface", idec.kind, idec.toPrettyChars);
idec.baseclasses.remove(i);
continue;
}
if (tc.sym.isDeprecated())
{
if (!idec.isDeprecated())
{
// Deriving from deprecated interface makes this one deprecated too
idec.setDeprecated();
tc.checkDeprecated(idec.loc, sc);
}
}
b.sym = tc.sym;
if (tc.sym.baseok < Baseok.done)
resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
if (tc.sym.baseok < Baseok.done)
{
//printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
if (tc.sym._scope)
Module.addDeferredSemantic(tc.sym);
idec.baseok = Baseok.none;
}
i++;
}
if (idec.baseok == Baseok.none)
{
// Forward referencee of one or more bases, try again later
return deferDsymbolSemantic(sc, idec, scx);
}
idec.baseok = Baseok.done;
idec.interfaces = idec.baseclasses.tdata()[0 .. idec.baseclasses.length];
foreach (b; idec.interfaces)
{
// If this is an interface, and it derives from a COM interface,
// then this is a COM interface too.
if (b.sym.isCOMinterface())
idec.com = true;
if (b.sym.isCPPinterface())
idec.classKind = ClassKind.cpp;
}
interfaceSemantic(idec);
}
Lancestorsdone:
if (!idec.members) // if opaque declaration
{
idec.semanticRun = PASS.semanticdone;
return;
}
if (!idec.symtab)
idec.symtab = new DsymbolTable();
for (size_t i = 0; i < idec.baseclasses.length; i++)
{
BaseClass* b = (*idec.baseclasses)[i];
Type tb = b.type.toBasetype();
TypeClass tc = tb.isTypeClass();
if (tc.sym.semanticRun < PASS.semanticdone)
{
// Forward referencee of one or more bases, try again later
if (tc.sym._scope)
Module.addDeferredSemantic(tc.sym);
return deferDsymbolSemantic(sc, idec, scx);
}
}
if (idec.baseok == Baseok.done)
{
idec.baseok = Baseok.semanticdone;
objc.setMetaclass(idec, sc);
// initialize vtbl
if (idec.vtblOffset())
idec.vtbl.push(idec); // leave room at vtbl[0] for classinfo
// Cat together the vtbl[]'s from base interfaces
foreach (i, b; idec.interfaces)
{
// Skip if b has already appeared
for (size_t k = 0; k < i; k++)
{
if (b == idec.interfaces[k])
goto Lcontinue;
}
// Copy vtbl[] from base class
if (b.sym.vtblOffset())
{
size_t d = b.sym.vtbl.length;
if (d > 1)
{
idec.vtbl.pushSlice(b.sym.vtbl[1 .. d]);
}
}
else
{
idec.vtbl.append(&b.sym.vtbl);
}
Lcontinue:
}
}
idec.members.foreachDsymbol( s => s.addMember(sc, idec) );
auto sc2 = idec.newScope(sc);
/* Set scope so if there are forward references, we still might be able to
* resolve individual members like enums.
*/
idec.members.foreachDsymbol( s => s.setScope(sc2) );
idec.members.foreachDsymbol( s => s.importAll(sc2) );
idec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
idec.semanticRun = PASS.semanticdone;
//printf("-InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
sc2.pop();
if (global.errors != errors)
{
// The type is no good.
idec.type = Type.terror;
}
version (none)
{
if (type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
{
printf("this = %p %s\n", idec, idec.toChars());
printf("type = %d sym = %p\n", idec.type.ty, (cast(TypeClass)idec.type).sym);
}
}
assert(idec.type.ty != Tclass || (cast(TypeClass)idec.type).sym == idec);
version (none)
{
// @@@DEPRECATED_2.120@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
// Deprecated in 2.087
// Made an error in 2.100, but removal depends on `scope class` being removed too
// Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
if (idec.storage_class & STC.scope_)
error(idec.loc, "`scope` as a type constraint is obsolete. Use `scope` at the usage site.");
}
}
}
/*
Adds dsym as a member of scope sds.
Params:
dsym = dsymbol to inserted
sc = scope where the dsymbol is declared
sds = ScopeDsymbol where dsym is inserted
*/
void addMember(Dsymbol dsym, Scope* sc, ScopeDsymbol sds)
{
auto addMemberVisitor = new AddMemberVisitor(sc, sds);
dsym.accept(addMemberVisitor);
}
private void attribAddMember(AttribDeclaration atb, Scope* sc, ScopeDsymbol sds)
{
Dsymbols* d = atb.include(sc);
if (d)
{
Scope* sc2 = atb.newScope(sc);
d.foreachDsymbol( s => s.addMember(sc2, sds) );
if (sc2 != sc)
sc2.pop();
}
}
private extern(C++) class AddMemberVisitor : Visitor
{
alias visit = Visitor.visit;
Scope* sc;
ScopeDsymbol sds;
this(Scope* sc, ScopeDsymbol sds) @safe
{
this.sc = sc;
this.sds = sds;
}
override void visit(Dsymbol dsym)
{
//printf("Dsymbol::addMember('%s')\n", toChars());
//printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds.toChars());
//printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds.symtab = %p)\n", this, toChars(), sds, sds.symtab);
dsym.parent = sds;
if (dsym.isAnonymous()) // no name, so can't add it to symbol table
return;
if (!sds.symtabInsert(dsym)) // if name is already defined
{
if (dsym.isAliasDeclaration() && !dsym._scope)
dsym.setScope(sc);
Dsymbol s2 = sds.symtabLookup(dsym, dsym.ident);
/* https://issues.dlang.org/show_bug.cgi?id=17434
*
* If we are trying to add an import to the symbol table
* that has already been introduced, then keep the one with
* larger visibility. This is fine for imports because if
* we have multiple imports of the same file, if a single one
* is public then the symbol is reachable.
*/
if (auto i1 = dsym.isImport())
{
if (auto i2 = s2.isImport())
{
if (sc.explicitVisibility && sc.visibility > i2.visibility)
sds.symtab.update(dsym);
}
}
// If using C tag/prototype/forward declaration rules
if (sc && sc.inCfile && !dsym.isImport())
// When merging master, replace with: if (sc && sc.inCfile && !dsym.isImport())
{
if (handleTagSymbols(*sc, dsym, s2, sds))
return;
if (handleSymbolRedeclarations(*sc, dsym, s2, sds))
return;
sds.multiplyDefined(Loc.initial, dsym, s2); // ImportC doesn't allow overloading
dsym.errors = true;
return;
}
if (!s2.overloadInsert(dsym))
{
sds.multiplyDefined(Loc.initial, dsym, s2);
dsym.errors = true;
}
}
if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
{
if (dsym.ident == Id.__sizeof ||
!(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof))
{
.error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars());
dsym.errors = true;
}
}
}
override void visit(StaticAssert _)
{
// we didn't add anything
}
/*****************************
* Add import to sd's symbol table.
*/
override void visit(Import imp)
{
//printf("Import.addMember(this=%s, sds=%s, sc=%p)\n", imp.toChars(), sds.toChars(), sc);
if (imp.names.length == 0)
return visit(cast(Dsymbol)imp);
if (imp.aliasId)
visit(cast(Dsymbol)imp);
/* Instead of adding the import to sds's symbol table,
* add each of the alias=name pairs
*/
for (size_t i = 0; i < imp.names.length; i++)
{
Identifier name = imp.names[i];
Identifier _alias = imp.aliases[i];
if (!_alias)
_alias = name;
auto tname = new TypeIdentifier(imp.loc, name);
auto ad = new AliasDeclaration(imp.loc, _alias, tname);
ad._import = imp;
addMember(ad, sc, sds);
imp.aliasdecls.push(ad);
}
}
override void visit(AttribDeclaration atb)
{
attribAddMember(atb, sc, sds);
}
override void visit(StorageClassDeclaration stcd)
{
Dsymbols* d = stcd.include(sc);
if (d)
{
Scope* sc2 = stcd.newScope(sc);
d.foreachDsymbol( (s)
{
//printf("\taddMember %s to %s\n", s.toChars(), sds.toChars());
// STC.local needs to be attached before the member is added to the scope (because it influences the parent symbol)
if (auto decl = s.isDeclaration())
{
decl.storage_class |= stcd.stc & STC.local;
if (auto sdecl = s.isStorageClassDeclaration()) // TODO: why is this not enough to deal with the nested case?
{
sdecl.stc |= stcd.stc & STC.local;
}
}
s.addMember(sc2, sds);
});
if (sc2 != sc)
sc2.pop();
}
}
override void visit(VisibilityDeclaration visd)
{
if (visd.pkg_identifiers)
{
Dsymbol tmp;
Package.resolve(visd.pkg_identifiers, &tmp, null);
visd.visibility.pkg = tmp ? tmp.isPackage() : null;
visd.pkg_identifiers = null;
}
if (visd.visibility.kind == Visibility.Kind.package_ && visd.visibility.pkg && sc._module)
{
Module m = sc._module;
// https://issues.dlang.org/show_bug.cgi?id=17441
// While isAncestorPackageOf does an equality check, the fix for the issue adds a check to see if
// each package's .isModule() properites are equal.
//
// Properties generated from `package(foo)` i.e. visibility.pkg have .isModule() == null.
// This breaks package declarations of the package in question if they are declared in
// the same package.d file, which _do_ have a module associated with them, and hence a non-null
// isModule()
if (!m.isPackage() || !visd.visibility.pkg.ident.equals(m.isPackage().ident))
{
Package pkg = m.parent ? m.parent.isPackage() : null;
if (!pkg || !visd.visibility.pkg.isAncestorPackageOf(pkg))
.error(visd.loc, "%s `%s` does not bind to one of ancestor packages of module `%s`", visd.kind(), visd.toPrettyChars(false), m.toPrettyChars(true));
}
}
attribAddMember(visd, sc, sds);
}
override void visit(StaticIfDeclaration sid)
{
//printf("StaticIfDeclaration::addMember() '%s'\n", sid.toChars());
/* This is deferred until the condition evaluated later (by the include() call),
* so that expressions in the condition can refer to declarations
* in the same scope, such as:
*
* template Foo(int i)
* {
* const int j = i + 1;
* static if (j == 3)
* const int k;
* }
*/
sid.scopesym = sds;
}
override void visit(StaticForeachDeclaration sfd)
{
// used only for caching the enclosing symbol
sfd.scopesym = sds;
}
/***************************************
* Lazily initializes the scope to forward to.
*/
override void visit(ForwardingAttribDeclaration fad)
{
fad.sym.parent = sds;
sds = fad.sym;
attribAddMember(fad, sc, fad.sym);
}
override void visit(MixinDeclaration md)
{
//printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, md.memnum);
md.scopesym = sds;
}
override void visit(DebugSymbol ds)
{
//printf("DebugSymbol::addMember('%s') %s\n", sds.toChars(), ds.toChars());
Module m = sds.isModule();
// Do not add the member to the symbol table,
// just make sure subsequent debug declarations work.
if (!m)
{
.error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars);
ds.errors = true;
}
else
{
if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident))
{
.error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars);
ds.errors = true;
}
if (!m.debugids)
m.debugids = new Identifiers();
m.debugids.push(ds.ident);
}
}
override void visit(VersionSymbol vs)
{
//printf("VersionSymbol::addMember('%s') %s\n", sds.toChars(), vs.toChars());
Module m = sds.isModule();
// Do not add the member to the symbol table,
// just make sure subsequent debug declarations work.
VersionCondition.checkReserved(vs.loc, vs.ident.toString());
if (!m)
{
.error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars);
vs.errors = true;
}
else
{
if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident))
{
.error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars);
vs.errors = true;
}
if (!m.versionids)
m.versionids = new Identifiers();
m.versionids.push(vs.ident);
}
}
override void visit(Nspace ns)
{
visit(cast(Dsymbol)ns);
if (ns.members)
{
if (!ns.symtab)
ns.symtab = new DsymbolTable();
// The namespace becomes 'imported' into the enclosing scope
for (Scope* sce = sc; 1; sce = sce.enclosing)
{
ScopeDsymbol sds2 = sce.scopesym;
if (sds2)
{
sds2.importScope(ns, Visibility(Visibility.Kind.public_));
break;
}
}
assert(sc);
sc = sc.push(ns);
sc.linkage = LINK.cpp; // namespaces default to C++ linkage
sc.parent = ns;
ns.members.foreachDsymbol(s => s.addMember(sc, ns));
sc.pop();
}
}
override void visit(EnumDeclaration ed)
{
version (none)
{
printf("EnumDeclaration::addMember() %s\n", ed.toChars());
for (size_t i = 0; i < ed.members.length; i++)
{
EnumMember em = (*ed.members)[i].isEnumMember();
printf(" member %s\n", em.toChars());
}
}
if (!ed.isAnonymous())
{
visit(cast(Dsymbol)ed);
}
addEnumMembersToSymtab(ed, sc, sds);
}
}
/*******************************************
* Add members of EnumDeclaration to the symbol table(s).
* Params:
* ed = EnumDeclaration
* sc = context of `ed`
* sds = symbol table that `ed` resides in
*/
void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
{
const bool isCEnum = sc.inCfile; // it's an ImportC enum
//printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum);
if (ed.added)
return;
ed.added = true;
if (!ed.members)
return;
const bool isAnon = ed.isAnonymous();
if ((isCEnum || isAnon) && !sds.symtab)
sds.symtab = new DsymbolTable();
if ((isCEnum || !isAnon) && !ed.symtab)
ed.symtab = new DsymbolTable();
ed.members.foreachDsymbol( (s)
{
if (EnumMember em = s.isEnumMember())
{
//printf("adding EnumMember %s to %s %d\n", em.toChars(), ed.toChars(), isCEnum);
em.ed = ed;
if (isCEnum)
{
/* C doesn't add the enum member to the symbol table of the enum tag, it adds
* it to the symbol table that the tag is in. This is in contrast to D, where enum
* members become members of the enum tag. To accommodate this, we add
* the enum members to both symbol tables.
*/
em.addMember(sc, ed); // add em to ed's symbol table
if (em.errors)
return;
em.addMember(sc, sds); // add em to symbol table that ed is in
em.parent = ed; // restore it after previous addMember() changed it
}
else
{
em.addMember(sc, isAnon ? sds : ed);
}
}
});
}
/******************************************************
* Verifies if the given Identifier is a DRuntime hook. It uses the hooks
* defined in `id.d`.
*
* Params:
* id = Identifier to verify
* Returns:
* true if `id` is a DRuntime hook
* false otherwise
*/
private bool isDRuntimeHook(Identifier id)
{
return id == Id._d_HookTraceImpl ||
id == Id._d_newclassT || id == Id._d_newclassTTrace ||
id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace ||
id == Id._d_newThrowable || id == Id._d_delThrowable ||
id == Id._d_arrayassign_l || id == Id._d_arrayassign_r ||
id == Id._d_arraysetassign || id == Id._d_arraysetctor ||
id == Id._d_arrayctor ||
id == Id._d_arraysetlengthTImpl || id == Id._d_arraysetlengthT ||
id == Id._d_arraysetlengthTTrace ||
id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace ||
id == Id._d_arrayappendcTX;
}
void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList)
{
//printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc);
version (none)
{
for (Dsymbol s = tempinst; s; s = s.parent)
{
printf("\t%s\n", s.toChars());
}
printf("Scope\n");
for (Scope* scx = sc; scx; scx = scx.enclosing)
{
printf("\t%s parent %s\n", scx._module ? scx._module.toChars() : "null", scx.parent ? scx.parent.toChars() : "null");
}
}
static if (LOG)
{
printf("\n+TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
}
if (tempinst.inst) // if semantic() was already run
{
static if (LOG)
{
printf("-TemplateInstance.dsymbolSemantic('%s', this=%p) already run\n",
tempinst.inst.toChars(), tempinst.inst);
}
return;
}
if (tempinst.semanticRun != PASS.initial)
{
static if (LOG)
{
printf("Recursive template expansion\n");
}
auto ungag = Ungag(global.gag);
if (!tempinst.gagged)
global.gag = 0;
.error(tempinst.loc, "%s `%s` recursive template expansion", tempinst.kind, tempinst.toPrettyChars);
if (tempinst.gagged)
tempinst.semanticRun = PASS.initial;
else
tempinst.inst = tempinst;
tempinst.errors = true;
return;
}
// Get the enclosing template instance from the scope tinst
tempinst.tinst = sc.tinst;
// Get the instantiating module from the scope minst
tempinst.minst = sc.minst;
// https://issues.dlang.org/show_bug.cgi?id=10920
// If the enclosing function is non-root symbol,
// this instance should be speculative.
if (!tempinst.tinst && sc.func && sc.func.inNonRoot())
{
tempinst.minst = null;
}
tempinst.gagged = (global.gag > 0);
tempinst.semanticRun = PASS.semantic;
static if (LOG)
{
printf("\tdo semantic\n");
}
/* Find template declaration first,
* then run semantic on each argument (place results in tiargs[]),
* last find most specialized template from overload list/set.
*/
if (!tempinst.findTempDecl(sc, null) || !tempinst.semanticTiargs(sc) || !tempinst.findBestMatch(sc, argumentList))
{
Lerror:
if (tempinst.gagged)
{
// https://issues.dlang.org/show_bug.cgi?id=13220
// Roll back status for later semantic re-running
tempinst.semanticRun = PASS.initial;
}
else
tempinst.inst = tempinst;
tempinst.errors = true;
return;
}
TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
assert(tempdecl);
if (global.params.v.templates)
TemplateStats.incInstance(tempdecl, tempinst, global.params.v.templatesListInstances);
tempdecl.checkDeprecated(tempinst.loc, sc);
// If tempdecl is a mixin, disallow it
if (tempdecl.ismixin)
{
.error(tempinst.loc, "%s `%s` mixin templates are not regular templates", tempinst.kind, tempinst.toPrettyChars);
goto Lerror;
}
tempinst.hasNestedArgs(tempinst.tiargs, tempdecl.isstatic);
if (tempinst.errors)
goto Lerror;
// Copy the tempdecl namespace (not the scope one)
tempinst.cppnamespace = tempdecl.cppnamespace;
if (tempinst.cppnamespace)
tempinst.cppnamespace.dsymbolSemantic(sc);
/* Greatly simplified semantic processing for AliasSeq templates
*/
if (tempdecl.isTrivialAliasSeq)
{
tempinst.inst = tempinst;
return aliasSeqInstanceSemantic(tempinst, sc, tempdecl);
}
/* Greatly simplified semantic processing for Alias templates
*/
else if (tempdecl.isTrivialAlias)
{
tempinst.inst = tempinst;
return aliasInstanceSemantic(tempinst, sc, tempdecl);
}
/* See if there is an existing TemplateInstantiation that already
* implements the typeargs. If so, just refer to that one instead.
*/
tempinst.inst = tempdecl.findExistingInstance(tempinst, argumentList);
TemplateInstance errinst = null;
if (!tempinst.inst)
{
// So, we need to implement 'this' instance.
}
else if (tempinst.inst.gagged && !tempinst.gagged && tempinst.inst.errors)
{
// If the first instantiation had failed, re-run semantic,
// so that error messages are shown.
errinst = tempinst.inst;
}
else
{
// It's a match
tempinst.parent = tempinst.inst.parent;
tempinst.errors = tempinst.inst.errors;
// If both this and the previous instantiation were gagged,
// use the number of errors that happened last time.
global.errors += tempinst.errors;
global.gaggedErrors += tempinst.errors;
// If the first instantiation was gagged, but this is not:
if (tempinst.inst.gagged)
{
// It had succeeded, mark it is a non-gagged instantiation,
// and reuse it.
tempinst.inst.gagged = tempinst.gagged;
}
tempinst.tnext = tempinst.inst.tnext;
tempinst.inst.tnext = tempinst;
/* A module can have explicit template instance and its alias
* in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`).
* If the first instantiation 'inst' had happened in non-root module,
* compiler can assume that its instantiated code would be included
* in the separately compiled obj/lib file (e.g. phobos.lib).
*
* However, if 'this' second instantiation happened in root module,
* compiler might need to invoke its codegen
* (https://issues.dlang.org/show_bug.cgi?id=2500 & https://issues.dlang.org/show_bug.cgi?id=2644).
* But whole import graph is not determined until all semantic pass finished,
* so 'inst' should conservatively finish the semantic3 pass for the codegen.
*/
if (tempinst.minst && tempinst.minst.isRoot() && !(tempinst.inst.minst && tempinst.inst.minst.isRoot()))
{
/* Swap the position of 'inst' and 'this' in the instantiation graph.
* Then, the primary instance `inst` will be changed to a root instance,
* along with all members of `inst` having their scopes updated.
*
* Before:
* non-root -> A!() -> B!()[inst] -> C!() { members[non-root] }
* |
* root -> D!() -> B!()[this]
*
* After:
* non-root -> A!() -> B!()[this]
* |
* root -> D!() -> B!()[inst] -> C!() { members[root] }
*/
Module mi = tempinst.minst;
TemplateInstance ti = tempinst.tinst;
tempinst.minst = tempinst.inst.minst;
tempinst.tinst = tempinst.inst.tinst;
tempinst.inst.minst = mi;
tempinst.inst.tinst = ti;
/* https://issues.dlang.org/show_bug.cgi?id=21299
`minst` has been updated on the primary instance `inst` so it is
now coming from a root module, however all Dsymbol `inst.members`
of the instance still have their `_scope.minst` pointing at the
original non-root module. We must now propagate `minst` to all
members so that forward referenced dependencies that get
instantiated will also be appended to the root module, otherwise
there will be undefined references at link-time. */
extern (C++) final class InstMemberWalker : Visitor
{
alias visit = Visitor.visit;
TemplateInstance inst;
extern (D) this(TemplateInstance inst) scope @safe
{
this.inst = inst;
}
override void visit(Dsymbol d)
{
if (d._scope)
d._scope.minst = inst.minst;
}
override void visit(ScopeDsymbol sds)
{
sds.members.foreachDsymbol( s => s.accept(this) );
visit(cast(Dsymbol)sds);
}
override void visit(StructDeclaration sd)
{
// need to visit auto-generated methods as well
if (sd.xeq) visit(sd.xeq);
if (sd.xcmp) visit(sd.xcmp);
if (sd.xhash) visit(sd.xhash);
visit(cast(ScopeDsymbol)sd);
}
override void visit(AttribDeclaration ad)
{
ad.include(null).foreachDsymbol( s => s.accept(this) );
visit(cast(Dsymbol)ad);
}
override void visit(ConditionalDeclaration cd)
{
if (cd.condition.inc)
visit(cast(AttribDeclaration)cd);
else
visit(cast(Dsymbol)cd);
}
}
scope v = new InstMemberWalker(tempinst.inst);
tempinst.inst.accept(v);
if (!global.params.allInst &&
tempinst.minst) // if inst was not speculative...
{
assert(!tempinst.minst.isRoot()); // ... it was previously appended to a non-root module
// Append again to the root module members[], so that the instance will
// get codegen chances (depending on `tempinst.inst.needsCodegen()`).
tempinst.inst.appendToModuleMember();
}
assert(tempinst.inst.memberOf && tempinst.inst.memberOf.isRoot(), "no codegen chances");
}
// modules imported by an existing instance should be added to the module
// that instantiates the instance.
if (tempinst.minst)
foreach(imp; tempinst.inst.importedModules)
if (!tempinst.minst.aimports.contains(imp))
tempinst.minst.aimports.push(imp);
static if (LOG)
{
printf("\tit's a match with instance %p, %d\n", tempinst.inst, tempinst.inst.semanticRun);
}
return;
}
static if (LOG)
{
printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars());
printf("\ttempdecl %s\n", tempdecl.toChars());
}
const errorsave = global.errors;
tempinst.inst = tempinst;
tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
//printf("parent = '%s'\n", parent.kind());
if (global.params.v.templates)
TemplateStats.incUnique(tempdecl, tempinst);
TemplateInstance tempdecl_instance_idx = tempdecl.addInstance(tempinst);
//getIdent();
// Store the place we added it to in target_symbol_list(_idx) so we can
// remove it later if we encounter an error.
Dsymbols* target_symbol_list = tempinst.appendToModuleMember();
size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list.length - 1 : 0;
// Copy the syntax trees from the TemplateDeclaration
tempinst.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
// resolve TemplateThisParameter
for (size_t i = 0; i < tempdecl.parameters.length; i++)
{
if ((*tempdecl.parameters)[i].isTemplateThisParameter() is null)
continue;
Type t = isType((*tempinst.tiargs)[i]);
assert(t);
if (STC stc = ModToStc(t.mod))
{
//printf("t = %s, stc = x%llx\n", t.toChars(), stc);
auto s = new Dsymbols();
s.push(new StorageClassDeclaration(stc, tempinst.members));
tempinst.members = s;
}
break;
}
// Create our own scope for the template parameters
Scope* _scope = tempdecl._scope;
if (tempdecl.semanticRun == PASS.initial)
{
.error(tempinst.loc, "%s `%s` template instantiation `%s` forward references template declaration `%s`",
tempinst.kind, tempinst.toPrettyChars, tempinst.toChars(), tempdecl.toChars());
return;
}
static if (LOG)
{
printf("\tcreate scope for template parameters '%s'\n", tempinst.toChars());
}
tempinst.argsym = new ScopeDsymbol();
tempinst.argsym.parent = _scope.parent;
_scope = _scope.push(tempinst.argsym);
_scope.tinst = tempinst;
_scope.minst = tempinst.minst;
//scope.stc = STC.none;
// Declare each template parameter as an alias for the argument type
Scope* paramscope = _scope.push();
paramscope.stc = STC.none;
paramscope.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=14169
// template parameters should be public
tempinst.declareParameters(paramscope);
paramscope.pop();
// Add members of template instance to template instance symbol table
//parent = scope.scopesym;
tempinst.symtab = new DsymbolTable();
tempinst.members.foreachDsymbol( (s)
{
static if (LOG)
{
printf("\t adding member '%s' %p kind %s to '%s'\n", s.toChars(), s, s.kind(), tempinst.toChars());
}
s.addMember(_scope, tempinst);
});
static if (LOG)
{
printf("adding members done\n");
}
/* See if there is only one member of template instance, and that
* member has the same name as the template instance.
* If so, this template instance becomes an alias for that member.
*/
//printf("members.length = %d\n", tempinst.members.length);
if (tempinst.members.length)
{
Dsymbol s;
if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
{
//printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
//printf("setting aliasdecl\n");
tempinst.aliasdecl = s;
}
}
/* If function template declaration
*/
if (argumentList.length > 0 && tempinst.aliasdecl)
{
if (auto fd = tempinst.aliasdecl.isFuncDeclaration())
{
/* Transmit fargs to type so that TypeFunction.dsymbolSemantic() can
* resolve any "auto ref" storage classes.
*/
if (fd.type)
if (auto tf = fd.type.isTypeFunction())
tf.inferenceArguments = argumentList;
}
}
// Do semantic() analysis on template instance members
static if (LOG)
{
printf("\tdo semantic() on template instance members '%s'\n", tempinst.toChars());
}
Scope* sc2;
sc2 = _scope.push(tempinst);
//printf("enclosing = %d, sc.parent = %s\n", tempinst.enclosing, sc.parent.toChars());
sc2.parent = tempinst;
sc2.tinst = tempinst;
sc2.minst = tempinst.minst;
sc2.stc &= ~STC.deprecated_;
tempinst.tryExpandMembers(sc2);
tempinst.semanticRun = PASS.semanticdone;
/* ConditionalDeclaration may introduce eponymous declaration,
* so we should find it once again after semantic.
*/
if (tempinst.members.length)
{
Dsymbol s;
if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
{
if (!tempinst.aliasdecl || tempinst.aliasdecl != s)
{
//printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
//printf("setting aliasdecl 2\n");
tempinst.aliasdecl = s;
}
}
}
if (global.errors != errorsave)
goto Laftersemantic;
/* If any of the instantiation members didn't get semantic() run
* on them due to forward references, we cannot run semantic2()
* or semantic3() yet.
*/
{
bool found_deferred_ad = false;
for (size_t i = 0; i < Module.deferred.length; i++)
{
Dsymbol sd = Module.deferred[i];
AggregateDeclaration ad = sd.isAggregateDeclaration();
if (ad && ad.parent && ad.parent.isTemplateInstance())
{
//printf("deferred template aggregate: %s %s\n",
// sd.parent.toChars(), sd.toChars());
found_deferred_ad = true;
if (ad.parent == tempinst)
{
ad.deferred = tempinst;
break;
}
}
}
if (found_deferred_ad || Module.deferred.length)
goto Laftersemantic;
}
/* The problem is when to parse the initializer for a variable.
* Perhaps VarDeclaration.dsymbolSemantic() should do it like it does
* for initializers inside a function.
*/
//if (sc.parent.isFuncDeclaration())
{
/* https://issues.dlang.org/show_bug.cgi?id=782
* this has problems if the classes this depends on
* are forward referenced. Find a way to defer semantic()
* on this template.
*/
tempinst.semantic2(sc2);
}
if (global.errors != errorsave)
goto Laftersemantic;
if ((sc.func || sc.fullinst) && !tempinst.tinst)
{
/* If a template is instantiated inside function, the whole instantiation
* should be done at that position. But, immediate running semantic3 of
* dependent templates may cause unresolved forward reference.
* https://issues.dlang.org/show_bug.cgi?id=9050
* To avoid the issue, don't run semantic3 until semantic and semantic2 done.
*/
TemplateInstances deferred;
tempinst.deferred = &deferred;
//printf("Run semantic3 on %s\n", toChars());
/* https://issues.dlang.org/show_bug.cgi?id=23965
* DRuntime hooks are not deprecated, but may be used for deprecated
* types. Deprecations are disabled while analysing hooks to avoid
* spurious error messages.
*/
auto saveUseDeprecated = global.params.useDeprecated;
if (sc.isDeprecated() && isDRuntimeHook(tempinst.name))
global.params.useDeprecated = DiagnosticReporting.off;
tempinst.trySemantic3(sc2);
global.params.useDeprecated = saveUseDeprecated;
for (size_t i = 0; i < deferred.length; i++)
{
//printf("+ run deferred semantic3 on %s\n", deferred[i].toChars());
deferred[i].semantic3(null);
}
tempinst.deferred = null;
}
else if (tempinst.tinst)
{
bool doSemantic3 = false;
FuncDeclaration fd;
if (tempinst.aliasdecl)
fd = tempinst.aliasdecl.toAlias2().isFuncDeclaration();
if (fd)
{
/* Template function instantiation should run semantic3 immediately
* for attribute inference.
*/
scope fld = fd.isFuncLiteralDeclaration();
if (fld && fld.tok == TOK.reserved)
doSemantic3 = true;
else if (sc.func)
doSemantic3 = true;
}
else if (sc.func)
{
/* A lambda function in template arguments might capture the
* instantiated scope context. For the correct context inference,
* all instantiated functions should run the semantic3 immediately.
* See also compilable/test14973.d
*/
foreach (oarg; tempinst.tdtypes)
{
auto s = getDsymbol(oarg);
if (!s)
continue;
if (auto td = s.isTemplateDeclaration())
{
if (!td.literal)
continue;
assert(td.members && td.members.length == 1);
s = (*td.members)[0];
}
if (auto fld = s.isFuncLiteralDeclaration())
{
if (fld.tok == TOK.reserved)
{
doSemantic3 = true;
break;
}
}
}
//printf("[%s] %s doSemantic3 = %d\n", tempinst.tinst.loc.toChars(), tempinst.tinst.toChars(), doSemantic3);
}
if (doSemantic3)
tempinst.trySemantic3(sc2);
TemplateInstance ti = tempinst.tinst;
int nest = 0;
while (ti && !ti.deferred && ti.tinst)
{
ti = ti.tinst;
if (++nest > global.recursionLimit)
{
global.gag = 0; // ensure error message gets printed
.error(tempinst.loc, "%s `%s` recursive expansion", tempinst.kind, tempinst.toPrettyChars);
fatal();
}
}
if (ti && ti.deferred)
{
//printf("deferred semantic3 of %p %s, ti = %s, ti.deferred = %p\n", this, toChars(), ti.toChars());
for (size_t i = 0;; i++)
{
if (i == ti.deferred.length)
{
ti.deferred.push(tempinst);
break;
}
if ((*ti.deferred)[i] == tempinst)
break;
}
}
}
if (tempinst.aliasdecl)
{
/* https://issues.dlang.org/show_bug.cgi?id=13816
* AliasDeclaration tries to resolve forward reference
* twice (See inuse check in AliasDeclaration.toAlias()). It's
* necessary to resolve mutual references of instantiated symbols, but
* it will left a true recursive alias in tuple declaration - an
* AliasDeclaration A refers TupleDeclaration B, and B contains A
* in its elements. To correctly make it an error, we strictly need to
* resolve the alias of eponymous member.
*/
tempinst.aliasdecl = tempinst.aliasdecl.toAlias2();
// stop AliasAssign tuple building
if (auto td = tempinst.aliasdecl.isTupleDeclaration())
td.building = false;
}
Laftersemantic:
sc2.pop();
_scope.pop();
// Give additional context info if error occurred during instantiation
if (global.errors != errorsave)
{
if (!tempinst.errors)
{
if (!tempdecl.literal)
.error(tempinst.loc, "%s `%s` error instantiating", tempinst.kind, tempinst.toPrettyChars);
if (tempinst.tinst)
tempinst.tinst.printInstantiationTrace();
}
tempinst.errors = true;
if (tempinst.gagged)
{
// Errors are gagged, so remove the template instance from the
// instance/symbol lists we added it to and reset our state to
// finish clean and so we can try to instantiate it again later
// (see https://issues.dlang.org/show_bug.cgi?id=4302 and https://issues.dlang.org/show_bug.cgi?id=6602).
tempdecl.removeInstance(tempdecl_instance_idx);
if (target_symbol_list)
{
// Because we added 'this' in the last position above, we
// should be able to remove it without messing other indices up.
assert((*target_symbol_list)[target_symbol_list_idx] == tempinst);
target_symbol_list.remove(target_symbol_list_idx);
tempinst.memberOf = null; // no longer a member
}
tempinst.semanticRun = PASS.initial;
tempinst.inst = null;
tempinst.symtab = null;
}
}
else if (errinst)
{
/* https://issues.dlang.org/show_bug.cgi?id=14541
* If the previous gagged instance had failed by
* circular references, currrent "error reproduction instantiation"
* might succeed, because of the difference of instantiated context.
* On such case, the cached error instance needs to be overridden by the
* succeeded instance.
*/
//printf("replaceInstance()\n");
assert(errinst.errors);
auto ti1 = TemplateInstanceBox(errinst);
tempdecl.instances.remove(ti1);
auto ti2 = TemplateInstanceBox(tempinst);
tempdecl.instances[ti2] = tempinst;
}
static if (LOG)
{
printf("-TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
}
}
/******************************************************
* Do template instance semantic for isAliasSeq templates.
* This is a greatly simplified version of templateInstanceSemantic().
*/
private
void aliasSeqInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
{
//printf("[%s] aliasSeqInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
Scope* paramscope = sc.push();
paramscope.stc = STC.none;
paramscope.visibility = Visibility(Visibility.Kind.public_);
TemplateTupleParameter ttp = (*tempdecl.parameters)[0].isTemplateTupleParameter();
Tuple va = tempinst.tdtypes[0].isTuple();
Declaration d = new TupleDeclaration(tempinst.loc, ttp.ident, &va.objects);
d.storage_class |= STC.templateparameter;
d.dsymbolSemantic(sc);
paramscope.pop();
tempinst.aliasdecl = d;
tempinst.semanticRun = PASS.semanticdone;
}
/******************************************************
* Do template instance semantic for isAlias templates.
* This is a greatly simplified version of templateInstanceSemantic().
*/
private
void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
{
//printf("[%s] aliasInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
Scope* paramscope = sc.push();
paramscope.stc = STC.none;
paramscope.visibility = Visibility(Visibility.Kind.public_);
TemplateTypeParameter ttp = (*tempdecl.parameters)[0].isTemplateTypeParameter();
Type ta = tempinst.tdtypes[0].isType();
auto ad = tempdecl.onemember.isAliasDeclaration();
// Note: qualifiers can be in both 'ad.type.mod' and 'ad.storage_class'
Declaration d = new AliasDeclaration(tempinst.loc, ttp.ident, ta.addMod(ad.type.mod));
d.storage_class |= STC.templateparameter | ad.storage_class;
d.dsymbolSemantic(sc);
paramscope.pop();
tempinst.aliasdecl = d;
tempinst.semanticRun = PASS.semanticdone;
}
// function used to perform semantic on AliasDeclaration
void aliasSemantic(AliasDeclaration ds, Scope* sc)
{
//printf("AliasDeclaration::semantic() %s %p\n", ds.toChars(), ds.aliassym);
// as DsymbolSemanticVisitor::visit(AliasDeclaration), in case we're called first.
// see https://issues.dlang.org/show_bug.cgi?id=21001
ds.storage_class |= sc.stc & STC.deprecated_;
ds.visibility = sc.visibility;
ds.userAttribDecl = sc.userAttribDecl;
void normalRet()
{
ds.inuse = 0;
ds.semanticRun = PASS.semanticdone;
if (auto sx = ds.overnext)
{
ds.overnext = null;
if (!ds.overloadInsert(sx))
ScopeDsymbol.multiplyDefined(Loc.initial, sx, ds);
}
}
void errorRet()
{
ds.aliassym = null;
ds.type = Type.terror;
ds.inuse = 0;
normalRet();
}
// preserve the original type
if (!ds.originalType && ds.type)
ds.originalType = ds.type.syntaxCopy();
if (ds.aliassym)
{
auto fd = ds.aliassym.isFuncLiteralDeclaration();
auto td = ds.aliassym.isTemplateDeclaration();
if (fd || td && td.literal)
{
if (fd && fd.semanticRun >= PASS.semanticdone)
return normalRet();
Expression e = new FuncExp(ds.loc, ds.aliassym);
e = e.expressionSemantic(sc);
if (auto fe = e.isFuncExp())
{
ds.aliassym = fe.td ? cast(Dsymbol)fe.td : fe.fd;
return normalRet();
}
else
return errorRet();
}
if (ds.aliassym.isTemplateInstance())
ds.aliassym.dsymbolSemantic(sc);
return normalRet();
}
ds.inuse = 1;
// Given:
// alias foo.bar.abc def;
// it is not knowable from the syntax whether `def` is an alias
// for type `foo.bar.abc` or an alias for symbol `foo.bar.abc`. It is up to the semantic()
// pass to distinguish.
// If it is a type, then `.type` is set and getType() will return that
// type. If it is a symbol, then `.aliassym` is set and type is `null` -
// toAlias() will return `.aliassym`
const errors = global.errors;
Type oldtype = ds.type;
// Ungag errors when not instantiated DeclDefs scope alias
auto ungag = Ungag(global.gag);
//printf("%s parent = %s, gag = %d, instantiated = %d\n", ds.toChars(), ds.parent.toChars(), global.gag, ds.isInstantiated() !is null);
if (ds.parent && global.gag && !ds.isInstantiated() && !ds.toParent2().isFuncDeclaration() && (sc.minst || sc.tinst))
{
//printf("%s type = %s\n", ds.toPrettyChars(), ds.type.toChars());
global.gag = 0;
}
// https://issues.dlang.org/show_bug.cgi?id=18480
// Detect `alias sym = sym;` to prevent creating loops in overload overnext lists.
if (auto tident = ds.type.isTypeIdentifier())
{
if (sc.hasEdition(Edition.v2024) && tident.idents.length)
{
alias mt = tident;
Dsymbol pscopesym;
Dsymbol s = sc.search(ds.loc, mt.ident, pscopesym);
// detect `alias a = var1.member_var;` which confusingly resolves to
// `typeof(var1).member_var`, which can be valid inside the aggregate type
if (s && s.isVarDeclaration() &&
mt.ident != Id.This && mt.ident != Id._super)
{
s = tident.toDsymbol(sc);
// don't error for `var1.static_symbol`
if (s && s.needThis())
{
error(ds.loc, "cannot alias %s member `%s` of variable `%s`",
s.kind(), s.toChars(), mt.ident.toChars());
errorSupplemental(ds.loc, "Use `typeof(%s)` instead to preserve behaviour",
mt.ident.toChars());
}
}
}
// Selective imports are allowed to alias to the same name `import mod : sym=sym`.
if (!ds._import)
{
if (tident.ident is ds.ident && !tident.idents.length)
{
error(ds.loc, "`alias %s = %s;` cannot alias itself, use a qualified name to create an overload set",
ds.ident.toChars(), tident.ident.toChars());
ds.type = Type.terror;
}
}
}
/* This section is needed because Type.resolve() will:
* const x = 3;
* alias y = x;
* try to convert identifier x to 3.
*/
auto s = ds.type.toDsymbol(sc);
if (errors != global.errors)
return errorRet();
if (s == ds)
{
.error(ds.loc, "%s `%s` cannot resolve", ds.kind, ds.toPrettyChars);
return errorRet();
}
if (!s || !s.isEnumMember())
{
Type t;
Expression e;
Scope* sc2 = sc;
if (ds.storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.disable))
{
// For 'ref' to be attached to function types, and picked
// up by Type.resolve(), it has to go into sc.
sc2 = sc.push();
sc2.stc |= ds.storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable);
}
ds.type = ds.type.addSTC(ds.storage_class);
ds.type.resolve(ds.loc, sc2, e, t, s);
if (sc2 != sc)
sc2.pop();
if (e) // Try to convert Expression to Dsymbol
{
// TupleExp is naturally converted to a TupleDeclaration
if (auto te = e.isTupleExp())
s = new TupleDeclaration(te.loc, ds.ident, cast(Objects*)te.exps);
else
{
s = getDsymbol(e);
if (!s)
{
if (e.op != EXP.error)
.error(ds.loc, "%s `%s` cannot alias an expression `%s`", ds.kind, ds.toPrettyChars, e.toChars());
return errorRet();
}
}
}
ds.type = t;
}
if (s == ds)
{
assert(global.errors);
return errorRet();
}
if (s) // it's a symbolic alias
{
//printf("alias %s resolved to %s %s\n", ds.toChars(), s.kind(), s.toChars());
ds.type = null;
ds.aliassym = s;
}
else // it's a type alias
{
//printf("alias %s resolved to type %s\n", ds.toChars(), ds.type.toChars());
ds.type = ds.type.typeSemantic(ds.loc, sc);
ds.aliassym = null;
}
if (global.gag && errors != global.errors)
return errorRet();
normalRet();
}
/********************
* Perform semantic on AliasAssignment.
* Has a lot of similarities to aliasSemantic(). Perhaps they should share code.
*/
private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
{
//printf("AliasAssign::semantic() %p, %s\n", ds, ds.ident.toChars());
void errorRet()
{
ds.errors = true;
ds.type = Type.terror;
ds.semanticRun = PASS.semanticdone;
return;
}
/* Find the AliasDeclaration corresponding to ds.
* Returns: AliasDeclaration if found, null if error
*/
AliasDeclaration findAliasDeclaration(AliasAssign ds, Scope* sc)
{
Dsymbol scopesym;
Dsymbol as = sc.search(ds.loc, ds.ident, scopesym);
if (!as)
{
.error(ds.loc, "%s `%s` undefined identifier `%s`", ds.kind, ds.toPrettyChars, ds.ident.toChars());
return null;
}
if (as.errors)
return null;
auto ad = as.isAliasDeclaration();
if (!ad)
{
.error(ds.loc, "%s `%s` identifier `%s` must be an alias declaration", ds.kind, ds.toPrettyChars, as.toChars());
return null;
}
if (ad.overnext)
{
error(ds.loc, "%s `%s` cannot reassign overloaded alias", ds.kind, ds.toPrettyChars);
return null;
}
// Check constraints on the parent
auto adParent = ad.toParent();
if (adParent != ds.toParent())
{
if (!adParent)
adParent = ds.toParent();
.error(ds.loc, "`%s` must have same parent `%s` as alias `%s`", ds.ident.toChars(), adParent.toChars(), ad.toChars());
return null;
}
if (!adParent.isTemplateInstance())
{
.error(ds.loc, "%s `%s` must be a member of a template", ds.kind, ds.toPrettyChars);
return null;
}
return ad;
}
auto aliassym = findAliasDeclaration(ds, sc);
if (!aliassym)
return errorRet();
if (aliassym.wasRead)
{
if (!aliassym.errors)
error(ds.loc, "%s was read, so cannot reassign", aliassym.toChars());
aliassym.errors = true;
return errorRet();
}
aliassym.ignoreRead = true; // temporarilly allow reads of aliassym
const storage_class = sc.stc & (STC.deprecated_ | STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable);
if (ds.aliassym)
{
auto fd = ds.aliassym.isFuncLiteralDeclaration();
auto td = ds.aliassym.isTemplateDeclaration();
if (fd && fd.semanticRun >= PASS.semanticdone)
{
}
else if (fd || td && td.literal)
{
Expression e = new FuncExp(ds.loc, ds.aliassym);
e = e.expressionSemantic(sc);
auto fe = e.isFuncExp();
if (!fe)
return errorRet();
ds.aliassym = fe.td ? cast(Dsymbol)fe.td : fe.fd;
}
else if (ds.aliassym.isTemplateInstance())
ds.aliassym.dsymbolSemantic(sc);
aliassym.type = null;
aliassym.aliassym = ds.aliassym;
return;
}
/* Given:
* abc = def;
* it is not knownable from the syntax whether `def` is a type or a symbol.
* It appears here as `ds.type`. Do semantic analysis on `def` to disambiguate.
*/
const errors = global.errors;
Dsymbol s;
// Try AliasSeq optimization
if (auto ti = ds.type.isTypeInstance())
{
if (!ti.tempinst.findTempDecl(sc, null))
return errorRet();
if (auto tempinst = isAliasSeq(sc, ti))
{
s = aliasAssignInPlace(sc, tempinst, aliassym);
if (!s)
return errorRet();
goto Lsymdone;
}
}
/* This section is needed because Type.resolve() will:
* const x = 3;
* alias y = x;
* try to convert identifier x to 3.
*/
s = ds.type.toDsymbol(sc);
if (errors != global.errors)
return errorRet();
if (s == aliassym)
{
.error(ds.loc, "%s `%s` cannot resolve", ds.kind, ds.toPrettyChars);
return errorRet();
}
if (!s || !s.isEnumMember())
{
Type t;
Expression e;
Scope* sc2 = sc;
if (storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable))
{
// For 'ref' to be attached to function types, and picked
// up by Type.resolve(), it has to go into sc.
sc2 = sc.push();
sc2.stc |= storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable);
}
ds.type = ds.type.addSTC(storage_class);
ds.type.resolve(ds.loc, sc2, e, t, s);
if (sc2 != sc)
sc2.pop();
if (e) // Try to convert Expression to Dsymbol
{
// TupleExp is naturally converted to a TupleDeclaration
if (auto te = e.isTupleExp())
s = new TupleDeclaration(te.loc, ds.ident, cast(Objects*)te.exps);
else
{
s = getDsymbol(e);
if (!s)
{
if (e.op != EXP.error)
.error(ds.loc, "%s `%s` cannot alias an expression `%s`", ds.kind, ds.toPrettyChars, e.toChars());
return errorRet();
}
}
}
ds.type = t;
}
if (s == aliassym)
{
assert(global.errors);
return errorRet();
}
if (s) // it's a symbolic alias
{
Lsymdone:
//printf("alias %s resolved to %s %s\n", toChars(), s.kind(), s.toChars());
aliassym.type = null;
aliassym.aliassym = s;
aliassym.storage_class |= sc.stc & STC.deprecated_;
aliassym.visibility = sc.visibility;
aliassym.userAttribDecl = sc.userAttribDecl;
}
else // it's a type alias
{
//printf("alias %s resolved to type %s\n", toChars(), type.toChars());
aliassym.type = ds.type.typeSemantic(ds.loc, sc);
aliassym.aliassym = null;
}
aliassym.ignoreRead = false;
if (aliassym.type && aliassym.type.ty == Terror ||
global.gag && errors != global.errors)
{
aliassym.type = Type.terror;
aliassym.aliassym = null;
return errorRet();
}
ds.semanticRun = PASS.semanticdone;
}
/***************************************
* Expands template instance arguments inside 'alias assign' target declaration (aliassym),
* instead of inside 'tempinst.tiargs' every time.
* Params:
* tempinst = AliasSeq instance
* aliassym = the AliasDeclaration corresponding to AliasAssign
* Returns:
* null.
*/
private TupleDeclaration aliasAssignInPlace(Scope* sc, TemplateInstance tempinst,
AliasDeclaration aliassym)
{
// Mark instance with semantic done, not needed but just in case.
tempinst.inst = tempinst;
tempinst.semanticRun = PASS.semanticdone;
TupleDeclaration td;
if (aliassym.type)
{
// Convert TypeTuple to TupleDeclaration to avoid back and forth allocations
// in the assignment process
if (auto tt = aliassym.type.isTypeTuple())
{
auto objs = new Objects(tt.arguments.length);
foreach (i, p; *tt.arguments)
(*objs)[i] = p.type;
td = new TupleDeclaration(tempinst.loc, aliassym.ident, objs);
td.storage_class |= STC.templateparameter;
td.building = true;
aliassym.type = null;
}
else if (aliassym.type.isTypeError())
return null;
}
else if (auto otd = aliassym.aliassym.isTupleDeclaration())
{
if (otd.building)
td = otd;
else
{
td = new TupleDeclaration(tempinst.loc, aliassym.ident, otd.objects.copy());
td.storage_class |= STC.templateparameter;
td.building = true;
}
}
// If starting from single element in aliassym (td == null) we need to build the tuple
// after semanticTiargs to keep same semantics (for example a FuncLiteraldeclaration
// template argument is converted to FuncExp)
if (td)
aliassym.aliassym = td;
aliassym.semanticRun = PASS.semanticdone;
if (!TemplateInstance.semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td))
{
tempinst.errors = true;
return null;
}
// The alias will stop tuple 'building' mode when used (in AliasDeclaration.toAlias(),
// then TupleDeclaration.getType() will work again)
aliassym.semanticRun = PASS.initial;
if (!td)
{
td = new TupleDeclaration(tempinst.loc, aliassym.ident, tempinst.tiargs);
td.storage_class |= STC.templateparameter;
td.building = true;
return td;
}
auto tiargs = tempinst.tiargs;
size_t oldlen = td.objects.length;
size_t origstart;
size_t insertidx;
size_t insertlen;
foreach (i, o; *tiargs)
{
if (o !is td)
{
++insertlen;
continue;
}
// tuple contains itself (tuple = AliasSeq!(..., tuple, ...))
if (insertlen) // insert any left element before
{
td.objects.insert(insertidx, (*tiargs)[i - insertlen .. i]);
if (insertidx == 0) // reset original tuple start point
origstart = insertlen;
insertlen = 0;
}
if (insertidx) // insert tuple if found more than one time
{
td.objects.reserve(oldlen); // reserve first to assert a valid slice
td.objects.pushSlice((*td.objects)[origstart .. origstart + oldlen]);
}
insertidx = td.objects.length;
}
if (insertlen)
{
if (insertlen != tiargs.length) // insert any left element
td.objects.pushSlice((*tiargs)[$ - insertlen .. $]);
else
// just assign tiargs if tuple = AliasSeq!(nottuple, nottuple...)
td.objects = tempinst.tiargs;
}
return td;
}
/***************************************
* Check if a template instance is a trivial AliasSeq but without other overloads.
* We can only be 100% sure of being AliasSeq after running semanticTiargs()
* and findBestMatch() but this optimization must happen before that.
*/
private TemplateInstance isAliasSeq(Scope* sc, TypeInstance ti)
{
auto tovers = ti.tempinst.tempdecl.isOverloadSet();
foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
{
Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempinst.tempdecl;
int r = overloadApply(dstart, (Dsymbol s)
{
auto td = s.isTemplateDeclaration();
if (!td || !td.isTrivialAliasSeq)
return 1;
return 0;
});
if (r)
return null;
}
return ti.tempinst;
}
/***************************************
* Find all instance fields in `ad`, then push them into `fields`.
*
* Runs semantic() for all instance field variables, but also
* the field types can remain yet not resolved forward references,
* except direct recursive definitions.
* After the process sizeok is set to Sizeok.fwd.
*
* Params:
* ad = the AggregateDeclaration to examine
* Returns:
* false if any errors occur.
*/
bool determineFields(AggregateDeclaration ad)
{
if (ad._scope)
dsymbolSemantic(ad, null);
if (ad.sizeok != Sizeok.none)
return true;
//printf("determineFields() %s, fields.length = %d\n", toChars(), fields.length);
// determineFields can be called recursively from one of the fields's v.semantic
ad.fields.setDim(0);
static int func(Dsymbol s, void* ctx)
{
auto ad = cast(AggregateDeclaration)ctx;
auto v = s.isVarDeclaration();
if (!v)
return 0;
if (v.storage_class & STC.manifest)
return 0;
if (v.semanticRun < PASS.semanticdone)
v.dsymbolSemantic(null);
// Return in case a recursive determineFields triggered by v.semantic already finished
if (ad.sizeok != Sizeok.none)
return 1;
if (v.aliasTuple)
{
// If this variable was really a tuple, process each element.
return v.aliasTuple.foreachVar(tv => tv.apply(&func, cast(void*) ad));
}
if (v.storage_class & (STC.static_ | STC.extern_ | STC.tls | STC.gshared | STC.manifest | STC.ctfe | STC.templateparameter))
return 0;
if (!v.isField() || v.semanticRun < PASS.semanticdone)
return 1; // unresolvable forward reference
ad.fields.push(v);
if (v.storage_class & STC.ref_)
return 0;
auto tv = v.type.baseElemOf();
if (auto tvs = tv.isTypeStruct())
{
if (ad == tvs.sym)
{
const(char)* psz = (v.type.toBasetype().ty == Tsarray) ? "static array of " : "";
.error(ad.loc, "%s `%s` cannot have field `%s` with %ssame struct type", ad.kind, ad.toPrettyChars, v.toChars(), psz);
ad.type = Type.terror;
ad.errors = true;
return 1;
}
}
return 0;
}
if (ad.members)
{
for (size_t i = 0; i < ad.members.length; i++)
{
auto s = (*ad.members)[i];
if (s.apply(&func, cast(void *)ad))
{
if (ad.sizeok != Sizeok.none)
{
// recursive determineFields already finished
return true;
}
return false;
}
}
}
if (ad.sizeok != Sizeok.done)
ad.sizeok = Sizeok.fwd;
return true;
}
/// Do an atomic operation (currently tailored to [shared] static ctors|dtors) needs
private CallExp doAtomicOp (string op, Identifier var, Expression arg)
{
assert(op == "-=" || op == "+=");
Module mod = Module.loadCoreAtomic();
if (!mod)
return null; // core.atomic couldn't be loaded
const loc = Loc.initial;
Objects* tiargs = new Objects(1);
(*tiargs)[0] = new StringExp(loc, op);
Expressions* args = new Expressions(2);
(*args)[0] = new IdentifierExp(loc, var);
(*args)[1] = arg;
auto sc = new ScopeExp(loc, mod);
auto dti = new DotTemplateInstanceExp(
loc, sc, Id.atomicOp, tiargs);
return CallExp.create(loc, dti, args);
}
/***************************************************
* Set up loc for a parse of a mixin. Append the input text to the mixin.
* Params:
* input = mixin text
* loc = location of expansion
* baseLoc = location to adjust
* mixinOut = sink for mixin text data
* Returns:
* adjusted loc suitable for Parser
*/
void adjustLocForMixin(const(char)[] input, Loc loc, ref BaseLoc baseLoc, ref Output mixinOut)
{
if (mixinOut.doOutput)
{
const lines = mixinOut.bufferLines;
writeMixin(input, loc, mixinOut.bufferLines, *mixinOut.buffer);
baseLoc.startLine = lines + 2;
baseLoc.filename = mixinOut.name;
return;
}
SourceLoc sl = SourceLoc(loc);
if (sl.filename.length == 0)
{
// Rare case of compiler-generated mixin exp, e.g. __xtoHash
baseLoc.filename = "";
return;
}
/* Create a pseudo-filename for the mixin string, as it may not even exist
* in the source file.
*/
auto len = sl.filename.length + 7 + (sl.linnum).sizeof * 3 + 1;
char* filename = cast(char*) mem.xmalloc(len);
snprintf(filename, len, "%.*s-mixin-%d", cast(int) sl.filename.length, sl.filename.ptr, cast(int) sl.linnum);
baseLoc.startLine = sl.line;
baseLoc.filename = filename.toDString;
}
/**************************************
* Append source code text to output for better debugging.
* Canonicalize line endings.
* Params:
* s = source code text
* loc = location of source code text
* lines = line count to update
* output = sink for output
*/
private void writeMixin(const(char)[] s, Loc loc, ref int lines, ref OutBuffer buf)
{
buf.writestring("// expansion at ");
buf.writestring(loc.toChars());
buf.writenl();
++lines;
// write by line to create consistent line endings
size_t lastpos = 0;
for (size_t i = 0; i < s.length; ++i)
{
// detect LF and CRLF
const c = s[i];
if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
{
buf.writestring(s[lastpos .. i]);
buf.writenl();
++lines;
if (c == '\r')
++i;
lastpos = i + 1;
}
}
if(lastpos < s.length)
buf.writestring(s[lastpos .. $]);
if (s.length == 0 || s[$-1] != '\n')
{
buf.writenl(); // ensure empty line after expansion
++lines;
}
buf.writenl();
++lines;
}
/*********************************************
* Search for ident as member of d.
* Params:
* d = dsymbol where ident is searched for
* loc = location to print for error messages
* ident = identifier to search for
* flags = search options
* Returns:
* null if not found
*/
Dsymbol search(Dsymbol d, Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all)
{
scope v = new SearchVisitor(loc, ident, flags);
d.accept(v);
return v.result;
}
Dsymbol search_correct(Dsymbol d, Identifier ident)
{
/***************************************************
* Search for symbol with correct spelling.
*/
Dsymbol symbol_search_fp(const(char)[] seed, out int cost)
{
/* If not in the lexer's string table, it certainly isn't in the symbol table.
* Doing this first is a lot faster.
*/
if (!seed.length)
return null;
Identifier id = Identifier.lookup(seed);
if (!id)
return null;
cost = 0; // all the same cost
Dsymbol s = d;
Module.clearCache();
return s.search(Loc.initial, id, SearchOpt.ignoreErrors);
}
if (global.gag)
return null; // don't do it for speculative compiles; too time consuming
// search for exact name first
if (auto s = d.search(Loc.initial, ident, SearchOpt.ignoreErrors))
return s;
import dmd.root.speller : speller;
return speller!symbol_search_fp(ident.toString());
}
private extern(C++) class SearchVisitor : Visitor
{
alias visit = Visitor.visit;
const Loc loc;
Identifier ident;
SearchOptFlags flags;
Dsymbol result;
this(Loc loc, Identifier ident, SearchOptFlags flags) @safe
{
this.loc = loc;
this.ident = ident;
this.flags = flags;
}
void setResult(Dsymbol d)
{
result = d;
}
override void visit(Dsymbol d)
{
//printf("Dsymbol::search(this=%p,%s, ident='%s')\n", d, d.toChars(), ident.toChars());
return setResult(null);
}
override void visit(ScopeDsymbol sds)
{
//printf("%s.ScopeDsymbol::search(ident='%s', flags=x%x)\n", sds.toChars(), ident.toChars(), flags);
//if (strcmp(ident.toChars(),"c") == 0) *(char*)0=0;
// Look in symbols declared in this module
if (sds.symtab && !(flags & SearchOpt.importsOnly))
{
//printf(" look in locals\n");
auto s1 = sds.symtab.lookup(ident);
if (s1)
{
//printf("\tfound in locals = '%s.%s'\n",toChars(),s1.toChars());
return setResult(s1);
}
}
//printf(" not found in locals\n");
// Look in imported scopes
if (!sds.importedScopes)
return setResult(null);
//printf(" look in imports\n");
Dsymbol s = null;
OverloadSet a = null;
// Look in imported modules
for (size_t i = 0; i < sds.importedScopes.length; i++)
{
// If private import, don't search it
if ((flags & SearchOpt.ignorePrivateImports) && sds.visibilities[i] == Visibility.Kind.private_)
continue;
SearchOptFlags sflags = flags & (SearchOpt.ignoreErrors | SearchOpt.ignoreAmbiguous); // remember these in recursive searches
Dsymbol ss = (*sds.importedScopes)[i];
//printf("\tscanning import '%s', visibilities = %d, isModule = %p, isImport = %p\n", ss.toChars(), visibilities[i], ss.isModule(), ss.isImport());
if (ss.isModule())
{
if (flags & SearchOpt.localsOnly)
continue;
}
else // mixin template
{
if (flags & SearchOpt.importsOnly)
continue;
sflags |= SearchOpt.localsOnly;
}
/* Don't find private members if ss is a module
*/
Dsymbol s2 = ss.search(loc, ident, sflags | (ss.isModule() ? SearchOpt.ignorePrivateImports : SearchOpt.all));
import dmd.access : symbolIsVisible;
if (!s2 || !(flags & SearchOpt.ignoreVisibility) && !symbolIsVisible(sds, s2))
continue;
if (!s)
{
s = s2;
if (s && s.isOverloadSet())
a = sds.mergeOverloadSet(ident, a, s);
}
else if (s2 && s != s2)
{
if (s.toAlias() == s2.toAlias() || s.getType() == s2.getType() && s.getType())
{
/* After following aliases, we found the same
* symbol, so it's not an ambiguity. But if one
* alias is deprecated or less accessible, prefer
* the other.
*/
if (s.isDeprecated() || s.visible() < s2.visible() && s2.visible().kind != Visibility.Kind.none)
s = s2;
}
else
{
/* Two imports of the same module should be regarded as
* the same.
*/
Import i1 = s.isImport();
Import i2 = s2.isImport();
if (!(i1 && i2 && (i1.mod == i2.mod || (!i1.parent.isImport() && !i2.parent.isImport() && i1.ident.equals(i2.ident)))))
{
/* https://issues.dlang.org/show_bug.cgi?id=8668
* Public selective import adds AliasDeclaration in module.
* To make an overload set, resolve aliases in here and
* get actual overload roots which accessible via s and s2.
*/
s = s.toAlias();
s2 = s2.toAlias();
/* If both s2 and s are overloadable (though we only
* need to check s once)
*/
auto so2 = s2.isOverloadSet();
if ((so2 || s2.isOverloadable()) && (a || s.isOverloadable()))
{
if (symbolIsVisible(sds, s2))
{
a = sds.mergeOverloadSet(ident, a, s2);
}
if (!symbolIsVisible(sds, s))
s = s2;
continue;
}
/* Two different overflow sets can have the same members
* https://issues.dlang.org/show_bug.cgi?id=16709
*/
auto so = s.isOverloadSet();
if (so && so2)
{
if (so.a.length == so2.a.length)
{
foreach (j; 0 .. so.a.length)
{
if (so.a[j] !is so2.a[j])
goto L1;
}
continue; // the same
L1:
{ } // different
}
}
if (flags & SearchOpt.ignoreAmbiguous) // if return NULL on ambiguity
return setResult(null);
/* If two imports from C import files, pick first one, as C has global name space
*/
if (s.isCsymbol() && s2.isCsymbol())
continue;
if (!(flags & SearchOpt.ignoreErrors))
ScopeDsymbol.multiplyDefined(loc, s, s2);
break;
}
}
}
}
if (s)
{
/* Build special symbol if we had multiple finds
*/
if (a)
{
if (!s.isOverloadSet())
a = sds.mergeOverloadSet(ident, a, s);
s = a;
}
//printf("\tfound in imports %s.%s\n", toChars(), s.toChars());
return setResult(s);
}
//printf(" not found in imports\n");
return setResult(null);
}
override void visit(WithScopeSymbol ws)
{
//printf("WithScopeSymbol.search(%s)\n", ident.toChars());
if (flags & SearchOpt.importsOnly)
return setResult(null);
// Acts as proxy to the with class declaration
Dsymbol s = null;
Expression eold = null;
for (Expression e = ws.withstate.exp; e && e != eold; e = resolveAliasThis(ws._scope, e, true))
{
if (auto se = e.isScopeExp())
{
s = se.sds;
}
else if (e.isTypeExp())
{
s = e.type.toDsymbol(null);
}
else
{
Type t = e.type.toBasetype();
s = t.toDsymbol(null);
}
if (s)
{
s = s.search(loc, ident, flags);
if (s)
return setResult(s);
}
eold = e;
}
return setResult(null);
}
override void visit(ArrayScopeSymbol ass)
{
//printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags);
if (ident != Id.dollar)
return setResult(null);
VarDeclaration* pvar;
Expression ce;
static Dsymbol dollarFromTypeTuple(Loc loc, TypeTuple tt, Scope* sc)
{
/* $ gives the number of type entries in the type tuple
*/
auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null);
Expression e = new IntegerExp(Loc.initial, tt.arguments.length, Type.tsize_t);
v._init = new ExpInitializer(Loc.initial, e);
v.storage_class |= STC.temp | STC.static_ | STC.const_;
v.dsymbolSemantic(sc);
return v;
}
const DYNCAST kind = ass.arrayContent.dyncast();
switch (kind) with (DYNCAST)
{
case dsymbol:
TupleDeclaration td = cast(TupleDeclaration) ass.arrayContent;
/* $ gives the number of elements in the tuple
*/
auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null);
Expression e = new IntegerExp(Loc.initial, td.objects.length, Type.tsize_t);
v._init = new ExpInitializer(Loc.initial, e);
v.storage_class |= STC.temp | STC.static_ | STC.const_;
v.dsymbolSemantic(ass._scope);
return setResult(v);
case type:
return setResult(dollarFromTypeTuple(loc, cast(TypeTuple) ass.arrayContent, ass._scope));
default:
break;
}
Expression exp = cast(Expression) ass.arrayContent;
if (auto ie = exp.isIndexExp())
{
/* array[index] where index is some function of $
*/
pvar = &ie.lengthVar;
ce = ie.e1;
}
else if (auto se = exp.isSliceExp())
{
/* array[lwr .. upr] where lwr or upr is some function of $
*/
pvar = &se.lengthVar;
ce = se.e1;
}
else if (auto ae = exp.isArrayExp())
{
/* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $
* $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...)
*/
pvar = &ae.lengthVar;
ce = ae.e1;
}
else
{
/* Didn't find $, look in enclosing scope(s).
*/
return setResult(null);
}
ce = ce.lastComma();
/* If we are indexing into an array that is really a type
* tuple, rewrite this as an index into a type tuple and
* try again.
*/
if (auto te = ce.isTypeExp())
{
if (auto ttp = te.type.isTypeTuple())
return setResult(dollarFromTypeTuple(loc, ttp, ass._scope));
}
/* *pvar is lazily initialized, so if we refer to $
* multiple times, it gets set only once.
*/
if (!*pvar) // if not already initialized
{
/* Create variable v and set it to the value of $
*/
VarDeclaration v;
Type t;
if (auto tupexp = ce.isTupleExp())
{
/* It is for an expression tuple, so the
* length will be a const.
*/
Expression e = new IntegerExp(Loc.initial, tupexp.exps.length, Type.tsize_t);
v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, new ExpInitializer(Loc.initial, e));
v.storage_class |= STC.temp | STC.static_ | STC.const_;
}
else if (ce.type && (t = ce.type.toBasetype()) !is null && (t.ty == Tstruct || t.ty == Tclass))
{
// Look for opDollar
assert(exp.op == EXP.array || exp.op == EXP.slice);
AggregateDeclaration ad = isAggregate(t);
assert(ad);
Dsymbol s = ad.search(loc, Id.opDollar);
if (!s) // no dollar exists -- search in higher scope
return setResult(null);
s = s.toAlias();
Expression e = null;
// Check for multi-dimensional opDollar(dim) template.
if (TemplateDeclaration td = s.isTemplateDeclaration())
{
dinteger_t dim = 0;
if (auto ae = exp.isArrayExp())
{
dim = ae.currentDimension;
}
else if (exp.isSliceExp())
{
dim = 0; // slices are currently always one-dimensional
}
else
{
assert(0);
}
auto tiargs = new Objects();
Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t);
edim = edim.expressionSemantic(ass._scope);
tiargs.push(edim);
e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs);
}
else
{
/* opDollar exists, but it's not a template.
* This is acceptable ONLY for single-dimension indexing.
* Note that it's impossible to have both template & function opDollar,
* because both take no arguments.
*/
auto ae = exp.isArrayExp();
if (ae && ae.arguments.length != 1)
{
error(exp.loc, "`%s` only defines opDollar for one dimension", ad.toChars());
return setResult(null);
}
Declaration d = s.isDeclaration();
assert(d);
e = new DotVarExp(loc, ce, d);
}
e = e.expressionSemantic(ass._scope);
if (!e.type)
error(exp.loc, "`%s` has no value", e.toChars());
t = e.type.toBasetype();
if (t && t.ty == Tfunction)
e = new CallExp(e.loc, e);
v = new VarDeclaration(loc, null, Id.dollar, new ExpInitializer(Loc.initial, e));
v.storage_class |= STC.temp | STC.ctfe | STC.rvalue;
}
else
{
/* For arrays, $ will either be a compile-time constant
* (in which case its value in set during constant-folding),
* or a variable (in which case an expression is created in
* toir.c).
*/
// https://issues.dlang.org/show_bug.cgi?id=16213
// For static arrays $ is known at compile time,
// so declare it as a manifest constant.
auto tsa = ce.type ? ce.type.isTypeSArray() : null;
if (tsa)
{
auto e = new ExpInitializer(loc, tsa.dim);
v = new VarDeclaration(loc, tsa.dim.type, Id.dollar, e, STC.manifest);
}
else
{
auto e = new VoidInitializer(Loc.initial);
e.type = Type.tsize_t;
v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, e);
v.storage_class |= STC.temp | STC.ctfe; // it's never a true static variable
}
}
*pvar = v;
}
(*pvar).dsymbolSemantic(ass._scope);
return setResult((*pvar));
}
override void visit(Import imp)
{
//printf("%s.Import.search(ident = '%s', flags = x%x)\n", imp.toChars(), ident.toChars(), flags);
if (!imp.pkg)
{
imp.load(null);
imp.mod.importAll(null);
imp.mod.dsymbolSemantic(null);
}
// Forward it to the package/module
return setResult(imp.pkg.search(loc, ident, flags));
}
override void visit(Nspace ns)
{
//printf("%s.Nspace.search('%s')\n", toChars(), ident.toChars());
if (ns._scope && !ns.symtab)
dsymbolSemantic(ns, ns._scope);
if (!ns.members || !ns.symtab) // opaque or semantic() is not yet called
{
if (!(flags & SearchOpt.ignoreErrors))
.error(loc, "%s `%s` is forward referenced when looking for `%s`", ns.kind, ns.toPrettyChars, ident.toChars());
return setResult(null);
}
visit(cast(ScopeDsymbol)ns);
}
override void visit(EnumDeclaration em)
{
//printf("%s.EnumDeclaration::search('%s')\n", em.toChars(), ident.toChars());
if (em._scope)
{
// Try one last time to resolve this enum
dsymbolSemantic(em, em._scope);
}
visit(cast(ScopeDsymbol)em);
}
override void visit(Package pkg)
{
//printf("%s Package.search('%s', flags = x%x)\n", pkg.toChars(), ident.toChars(), flags);
flags &= ~cast(int)SearchOpt.localsOnly; // searching an import is always transitive
if (!pkg.isModule() && pkg.mod)
{
// Prefer full package name.
Dsymbol s = pkg.symtab ? pkg.symtab.lookup(ident) : null;
if (s)
return setResult(s);
//printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars());
return setResult(pkg.mod.search(loc, ident, flags));
}
visit(cast(ScopeDsymbol)pkg);
}
override void visit(Module m)
{
/* Since modules can be circularly referenced,
* need to stop infinite recursive searches.
* This is done with the cache.
*/
//printf("%s Module.search('%s', flags = x%x) insearch = %d\n", m.toChars(), ident.toChars(), flags, m.insearch);
if (m.insearch)
return setResult(null);
/* Qualified module searches always search their imports,
* even if SearchLocalsOnly
*/
if (!(flags & SearchOpt.unqualifiedModule))
flags &= ~(SearchOpt.unqualifiedModule | SearchOpt.localsOnly);
if (m.searchCacheIdent == ident && m.searchCacheFlags == flags)
{
//printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n",
// toChars(), ident.toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol.toChars() : "null");
return setResult(m.searchCacheSymbol);
}
const errors = global.errors;
m.insearch = true;
visit(cast(ScopeDsymbol)m);
Dsymbol s = result;
m.insearch = false;
if (errors == global.errors)
{
// https://issues.dlang.org/show_bug.cgi?id=10752
// Can cache the result only when it does not cause
// access error so the side-effect should be reproduced in later search.
m.searchCacheIdent = ident;
m.searchCacheSymbol = s;
m.searchCacheFlags = flags;
}
return setResult(s);
}
override void visit(Declaration decl)
{
Dsymbol s = null;
if (decl.type)
{
s = decl.type.toDsymbol(decl._scope);
if (s)
s = s.search(loc, ident, flags);
}
return setResult(s);
}
override void visit(StructDeclaration sd)
{
//printf("%s.StructDeclaration::search('%s', flags = x%x)\n", sd.toChars(), ident.toChars(), flags);
if (sd._scope && !sd.symtab)
dsymbolSemantic(sd, sd._scope);
if (!sd.members || !sd.symtab) // opaque or semantic() is not yet called
{
// .stringof is always defined (but may be hidden by some other symbol)
if(ident != Id.stringof && !(flags & SearchOpt.ignoreErrors) && sd.semanticRun < PASS.semanticdone)
.error(loc, "%s `%s` is forward referenced when looking for `%s`", sd.kind, sd.toPrettyChars, ident.toChars());
return setResult(null);
}
visit(cast(ScopeDsymbol)sd);
}
override void visit(ClassDeclaration cd)
{
//printf("%s.ClassDeclaration.search('%s', flags=x%x)\n", cd.toChars(), ident.toChars(), flags);
//if (_scope) printf("%s baseok = %d\n", toChars(), baseok);
if (cd._scope && cd.baseok < Baseok.semanticdone)
{
if (!cd.inuse)
{
// must semantic on base class/interfaces
cd.inuse = true;
dsymbolSemantic(cd, null);
cd.inuse = false;
}
}
if (!cd.members || !cd.symtab) // opaque or addMember is not yet done
{
// .stringof is always defined (but may be hidden by some other symbol)
if (ident != Id.stringof && !(flags & SearchOpt.ignoreErrors) && cd.semanticRun < PASS.semanticdone)
cd.classError("%s `%s` is forward referenced when looking for `%s`", ident.toChars());
//*(char*)0=0;
return setResult(null);
}
visit(cast(ScopeDsymbol)cd);
auto s = result;
// don't search imports of base classes
if (flags & SearchOpt.importsOnly)
return setResult(s);
if (s)
return setResult(s);
// Search bases classes in depth-first, left to right order
foreach (b; (*cd.baseclasses)[])
{
if (!b.sym)
continue;
if (!b.sym.symtab)
{
cd.classError("%s `%s` base `%s` is forward referenced", b.sym.ident.toChars());
continue;
}
import dmd.access : symbolIsVisible;
s = b.sym.search(loc, ident, flags);
if (!s)
continue;
if (s == cd) // happens if s is nested in this and derives from this
s = null;
else if (!(flags & SearchOpt.ignoreVisibility) && !(s.visible().kind == Visibility.Kind.protected_) && !symbolIsVisible(cd, s))
s = null;
else
break;
}
return setResult(s);
}
}
/*************************************
* Set scope for future semantic analysis so we can
* deal better with forward references.
*
* Params:
* d = dsymbol for which the scope is set
* sc = scope that is used to set the value
*/
void setScope(Dsymbol d, Scope* sc)
{
scope setScopeVisitor = new SetScopeVisitor(sc);
d.accept(setScopeVisitor);
}
private extern(C++) class SetScopeVisitor : Visitor
{
alias visit = typeof(super).visit;
Scope* sc;
this(Scope* sc) @safe
{
this.sc = sc;
}
override void visit(Dsymbol d)
{
//printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", d, d.toChars(), sc, sc.stc);
if (!sc.nofree)
sc.setNoFree(); // may need it even after semantic() finishes
d._scope = sc;
if (sc.depdecl)
d.depdecl = sc.depdecl;
if (!d.userAttribDecl)
d.userAttribDecl = sc.userAttribDecl;
}
override void visit(Import i)
{
visit(cast(Dsymbol)i);
if (i.aliasdecls.length)
{
if (!i.mod)
i.importAll(sc);
sc = sc.push(i.mod);
sc.visibility = i.visibility;
foreach (ad; i.aliasdecls)
ad.setScope(sc);
sc = sc.pop();
}
}
override void visit(Nspace ns)
{
visit(cast(Dsymbol)ns);
if (ns.members)
{
assert(sc);
sc = sc.push(ns);
sc.linkage = LINK.cpp; // namespaces default to C++ linkage
sc.parent = ns;
ns.members.foreachDsymbol(s => s.setScope(sc));
sc.pop();
}
}
override void visit(EnumDeclaration ed)
{
if (ed.semanticRun > PASS.initial)
return;
visit(cast(Dsymbol)ed);
}
override void visit(AggregateDeclaration ad)
{
// Might need a scope to resolve forward references. The check for
// semanticRun prevents unnecessary setting of _scope during deferred
// setScope phases for aggregates which already finished semantic().
// See https://issues.dlang.org/show_bug.cgi?id=16607
if (ad.semanticRun < PASS.semanticdone)
visit(cast(Dsymbol)ad);
}
override void visit(AttribDeclaration atr)
{
Dsymbols* d = atr.include(sc);
//printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d);
if (d)
{
Scope* sc2 = atr.newScope(sc);
d.foreachDsymbol( s => s.setScope(sc2) );
if (sc2 != sc)
sc2.pop();
}
}
override void visit(DeprecatedDeclaration dd)
{
//printf("DeprecatedDeclaration::setScope() %p\n", this);
if (dd.decl)
visit(cast(Dsymbol)dd); // for forward reference
visit(cast(AttribDeclaration)dd);
}
override void visit(CPPMangleDeclaration cppmd)
{
if (cppmd.decl)
visit(cast(Dsymbol)cppmd); // for forward reference
visit(cast(AttribDeclaration)cppmd);
}
override void visit(AnonDeclaration anond)
{
if (anond.decl)
visit(cast(Dsymbol)anond); // for forward reference
visit(cast(AttribDeclaration)anond);
}
override void visit(ConditionalDeclaration condd)
{
condd.include(sc).foreachDsymbol( s => s.setScope(sc) );
}
override void visit(StaticIfDeclaration sid)
{
// do not evaluate condition before semantic pass
// But do set the scope, in case we need it for forward referencing
visit(cast(Dsymbol)sid); // for forward reference
}
override void visit(StaticForeachDeclaration sfd)
{
// do not evaluate condition before semantic pass
// But do set the scope, in case we need it for forward referencing
visit(cast(Dsymbol)sfd); // for forward reference
}
override void visit(MixinDeclaration md)
{
visit(cast(Dsymbol)md);
}
override void visit(UserAttributeDeclaration uad)
{
//printf("UserAttributeDeclaration::setScope() %p\n", this);
if (uad.decl)
visit(cast(Dsymbol)uad);
visit(cast(AttribDeclaration)uad);
}
}
void importAll(Dsymbol d, Scope* sc)
{
scope iav = new ImportAllVisitor(sc);
d.accept(iav);
}
extern(C++) class ImportAllVisitor : Visitor
{
alias visit = typeof(super).visit;
Scope* sc;
this(Scope* sc) @safe
{
this.sc = sc;
}
override void visit(Dsymbol d) {}
override void visit(Import imp)
{
if (imp.mod) return; // Already done
/*
* https://issues.dlang.org/show_bug.cgi?id=15525
*
* Loading the import has failed,
* most likely because of parsing errors.
* Therefore we cannot trust the resulting AST.
*/
if (imp.load(sc))
{
// https://issues.dlang.org/show_bug.cgi?id=23873
// For imports that are not at module or function level,
// e.g. aggregate level, the import symbol is added to the
// symbol table and later semantic is performed on it.
// This leads to semantic analysis on an malformed AST
// which causes all kinds of segfaults.
// The fix is to note that the module has errors and avoid
// semantic analysis on it.
if(imp.mod)
imp.mod.errors = true;
return;
}
if (!imp.mod) return; // Failed
if (sc.stc & STC.static_)
imp.isstatic = true;
imp.mod.importAll(null);
imp.mod.checkImportDeprecation(imp.loc, sc);
if (sc.explicitVisibility)
imp.visibility = sc.visibility;
if (!imp.isstatic && !imp.aliasId && !imp.names.length)
sc.scopesym.importScope(imp.mod, imp.visibility);
// Enable access to pkgs/mod as soon as posible, because compiler
// can traverse them before the import gets semantic (Issue: 21501)
if (!imp.aliasId && !imp.names.length)
imp.addPackageAccess(sc.scopesym);
}
override void visit(Module m)
{
//printf("+Module::importAll(this = %p, '%s'): parent = %p\n", m, m.toChars(), m.parent);
if (m._scope)
return; // already done
if (m.filetype == FileType.ddoc)
{
error(m.loc, "%s `%s` is a Ddoc file, cannot import it", m.kind, m.toPrettyChars);
return;
}
/* Note that modules get their own scope, from scratch.
* This is so regardless of where in the syntax a module
* gets imported, it is unaffected by context.
* Ignore prevsc.
*/
Scope* sc = Scope.createGlobal(m, global.errorSink); // create root scope
if (m.md && m.md.msg)
m.md.msg = semanticString(sc, m.md.msg, "deprecation message");
// Add import of "object", even for the "object" module.
// If it isn't there, some compiler rewrites, like
// classinst == classinst -> .object.opEquals(classinst, classinst)
// would fail inside object.d.
if (m.filetype != FileType.c &&
(m.members.length == 0 ||
(*m.members)[0].ident != Id.object ||
(*m.members)[0].isImport() is null))
{
auto im = new Import(m.loc, null, Id.object, null, 0);
m.members.shift(im);
}
if (!m.symtab)
{
// Add all symbols into module's symbol table
m.symtab = new DsymbolTable();
for (size_t i = 0; i < m.members.length; i++)
{
Dsymbol s = (*m.members)[i];
s.addMember(sc, sc.scopesym);
}
}
// anything else should be run after addMember, so version/debug symbols are defined
/* Set scope for the symbols so that if we forward reference
* a symbol, it can possibly be resolved on the spot.
* If this works out well, it can be extended to all modules
* before any semantic() on any of them.
*/
m.setScope(sc); // remember module scope for semantic
for (size_t i = 0; i < m.members.length; i++)
{
Dsymbol s = (*m.members)[i];
s.setScope(sc);
}
for (size_t i = 0; i < m.members.length; i++)
{
Dsymbol s = (*m.members)[i];
s.importAll(sc);
}
sc = sc.pop();
sc.pop(); // 2 pops because Scope.createGlobal() created 2
}
override void visit(AttribDeclaration atb)
{
Dsymbols* d = atb.include(sc);
//printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d);
if (d)
{
Scope* sc2 = atb.newScope(sc);
d.foreachDsymbol( s => s.importAll(sc2) );
if (sc2 != sc)
sc2.pop();
}
}
// do not evaluate condition before semantic pass
override void visit(StaticIfDeclaration _) {}
// do not evaluate aggregate before semantic pass
override void visit(StaticForeachDeclaration _) {}
}
/*******************************
* Load module.
* Returns:
* true for errors, false for success
*/
extern (D) bool load(Import imp, Scope* sc)
{
// See if existing module
const errors = global.errors;
DsymbolTable dst = Package.resolve(imp.packages, null, &imp.pkg);
version (none)
{
if (pkg && pkg.isModule())
{
.error(loc, "can only import from a module, not from a member of module `%s`. Did you mean `import %s : %s`?", pkg.toChars(), pkg.toPrettyChars(), id.toChars());
mod = pkg.isModule(); // Error recovery - treat as import of that module
return true;
}
}
Dsymbol s = dst.lookup(imp.id);
if (s)
{
if (s.isModule())
imp.mod = cast(Module)s;
else
{
if (s.isAliasDeclaration())
{
.error(imp.loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), imp.id.toChars());
}
else if (Package p = s.isPackage())
{
if (p.isPkgMod == PKG.unknown)
{
const preverrors = global.errors;
imp.mod = Module.load(imp.loc, imp.packages, imp.id);
if (!imp.mod)
p.isPkgMod = PKG.package_;
else
{
// imp.mod is a package.d, or a normal module which conflicts with the package name.
if (imp.mod.isPackageFile)
imp.mod.tag = p.tag; // reuse the same package tag
else
{
// show error if Module.load does not
if (preverrors == global.errors)
.error(imp.loc, "%s `%s` from file %s conflicts with %s `%s`", imp.mod.kind(), imp.mod.toPrettyChars(), imp.mod.srcfile.toChars, p.kind(), p.toPrettyChars());
return true;
}
}
}
else
{
imp.mod = p.isPackageMod();
}
if (!imp.mod)
{
.error(imp.loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), imp.id.toChars());
}
}
else if (imp.pkg)
{
.error(imp.loc, "can only import from a module, not from package `%s.%s`", imp.pkg.toPrettyChars(), imp.id.toChars());
}
else
{
.error(imp.loc, "can only import from a module, not from package `%s`", imp.id.toChars());
}
}
}
if (!imp.mod)
{
// Load module
imp.mod = Module.load(imp.loc, imp.packages, imp.id);
if (imp.mod)
{
// imp.id may be different from mod.ident, if so then insert alias
dst.insert(imp.id, imp.mod);
}
}
if (imp.mod && !imp.mod.importedFrom)
imp.mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule;
if (!imp.pkg)
{
if (imp.mod && imp.mod.isPackageFile)
{
// one level depth package.d file (import pkg; ./pkg/package.d)
// it's necessary to use the wrapping Package already created
imp.pkg = imp.mod.pkg;
}
else
imp.pkg = imp.mod;
}
return global.errors != errors;
}
void setFieldOffset(Dsymbol d, AggregateDeclaration ad, FieldState* fieldState, bool isunion)
{
scope v = new SetFieldOffsetVisitor(ad, fieldState, isunion);
d.accept(v);
}
private extern(C++) class SetFieldOffsetVisitor : Visitor
{
alias visit = Visitor.visit;
AggregateDeclaration ad;
FieldState* fieldState;
bool isunion;
this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) @safe
{
this.ad = ad;
this.fieldState = fieldState;
this.isunion = isunion;
}
override void visit(Dsymbol d) {}
override void visit(Nspace ns)
{
//printf("Nspace::setFieldOffset() %s\n", toChars());
if (ns._scope) // if fwd reference
dsymbolSemantic(ns, null); // try to resolve it
ns.members.foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) );
}
override void visit(VarDeclaration vd)
{
//printf("VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), vd.toChars());
if (vd.aliasTuple)
{
// If this variable was really a tuple, set the offsets for the tuple fields
vd.aliasTuple.foreachVar((s) { s.setFieldOffset(ad, fieldState, isunion); });
return;
}
if (!vd.isField())
return;
assert(!(vd.storage_class & (STC.static_ | STC.extern_ | STC.parameter)));
//printf("+VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars());
/* Fields that are tuples appear both as part of TupleDeclarations and
* as members. That means ignore them if they are already a field.
*/
if (vd.offset)
{
// already a field
fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613
return;
}
for (size_t i = 0; i < ad.fields.length; i++)
{
if (ad.fields[i] == vd)
{
// already a field
fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613
return;
}
}
// Check for forward referenced types which will fail the size() call
Type t = vd.type.toBasetype();
if (vd.storage_class & STC.ref_)
{
// References are the size of a pointer
t = Type.tvoidptr;
}
Type tv = t.baseElemOf();
if (tv.ty == Tstruct)
{
auto ts = cast(TypeStruct)tv;
assert(ts.sym != ad); // already checked in ad.determineFields()
if (!ts.sym.determineSize(vd.loc))
{
vd.type = Type.terror;
vd.errors = true;
return;
}
}
// List in ad.fields. Even if the type is error, it's necessary to avoid
// pointless error diagnostic "more initializers than fields" on struct literal.
ad.fields.push(vd);
if (t.ty == Terror)
return;
/* If coming after a bit field in progress,
* advance past the field
*/
fieldState.inFlight = false;
const sz = t.size(vd.loc);
assert(sz != SIZE_INVALID && sz < uint.max);
uint memsize = cast(uint)sz; // size of member
uint memalignsize = target.fieldalign(t); // size of member for alignment purposes
vd.offset = placeField(vd.loc,
fieldState.offset,
memsize, memalignsize, vd.alignment,
ad.structsize, ad.alignsize,
isunion);
//printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
//printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize);
}
override void visit(BitFieldDeclaration bfd)
{
enum log = false;
static if (log)
{
printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), bfd.toChars());
void print(const FieldState* fieldState)
{
fieldState.print();
printf(" fieldWidth = %d bits\n", bfd.fieldWidth);
}
print(fieldState);
}
Type t = bfd.type.toBasetype();
const bool anon = bfd.isAnonymous();
// List in ad.fields. Even if the type is error, it's necessary to avoid
// pointless error diagnostic "more initializers than fields" on struct literal.
if (!anon)
ad.fields.push(bfd);
if (t.ty == Terror)
return;
const sz = t.size(bfd.loc);
assert(sz != SIZE_INVALID && sz < uint.max);
uint memsize = cast(uint)sz; // size of member
uint memalignsize = target.fieldalign(t); // size of member for alignment purposes
if (log) printf(" memsize: %u memalignsize: %u\n", memsize, memalignsize);
if (bfd.fieldWidth == 0 && !anon)
error(bfd.loc, "named bit fields cannot have 0 width");
if (bfd.fieldWidth > memsize * 8)
error(bfd.loc, "bit field width %d is larger than type", bfd.fieldWidth);
const style = target.c.bitFieldStyle;
if (style != TargetC.BitFieldStyle.MS && style != TargetC.BitFieldStyle.Gcc_Clang)
assert(0, "unsupported bit-field style");
const isMicrosoftStyle = style == TargetC.BitFieldStyle.MS;
const contributesToAggregateAlignment = target.c.contributesToAggregateAlignment(bfd);
void startNewField()
{
if (log) printf("startNewField()\n");
uint alignsize;
if (isMicrosoftStyle)
alignsize = memsize; // not memalignsize
else
{
if (bfd.fieldWidth > 32)
alignsize = memalignsize;
else if (bfd.fieldWidth > 16)
alignsize = 4;
else if (bfd.fieldWidth > 8)
alignsize = 2;
else
alignsize = 1;
}
uint dummy;
bfd.offset = placeField(bfd.loc,
fieldState.offset,
memsize, alignsize, bfd.alignment,
ad.structsize,
contributesToAggregateAlignment ? ad.alignsize : dummy,
isunion);
fieldState.inFlight = true;
fieldState.fieldOffset = bfd.offset;
fieldState.bitOffset = 0;
fieldState.fieldSize = memsize;
}
if (ad.alignsize == 0)
ad.alignsize = 1;
if (!isMicrosoftStyle && contributesToAggregateAlignment && ad.alignsize < memalignsize)
ad.alignsize = memalignsize;
if (bfd.fieldWidth == 0)
{
if (!isMicrosoftStyle && !isunion)
{
// Use type of zero width field to align to next field
fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1);
ad.structsize = fieldState.offset;
}
else if (isMicrosoftStyle && fieldState.inFlight && !isunion)
{
// documentation says align to next int
//const alsz = cast(uint)Type.tint32.size();
const alsz = memsize; // but it really does this
fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1);
ad.structsize = fieldState.offset;
}
fieldState.inFlight = false;
return;
}
if (!fieldState.inFlight)
{
//printf("not in flight\n");
startNewField();
}
else if (!isMicrosoftStyle)
{
// If the bit-field spans more units of alignment than its type
// and is at the alignment boundary, start a new field at the
// next alignment boundary. This affects when offsetof reports
// a higher number and bitoffsetof starts at zero again.
if (fieldState.bitOffset % (memalignsize * 8) == 0 &&
fieldState.bitOffset + bfd.fieldWidth > memsize * 8)
{
if (log) printf("more units of alignment than its type\n");
startNewField(); // the bit field is full
}
else
{
// if alignment boundary is crossed
uint start = (fieldState.fieldOffset * 8 + fieldState.bitOffset) % (memalignsize * 8);
uint end = start + bfd.fieldWidth;
//printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize);
if (start / (memsize * 8) != (end - 1) / (memsize * 8))
{
if (log) printf("alignment is crossed\n");
startNewField();
}
}
}
else
{
if (memsize != fieldState.fieldSize ||
fieldState.bitOffset + bfd.fieldWidth > fieldState.fieldSize * 8)
{
//printf("new field\n");
startNewField();
}
}
bfd.offset = fieldState.fieldOffset;
bfd.bitOffset = fieldState.bitOffset;
const pastField = bfd.bitOffset + bfd.fieldWidth;
if (isMicrosoftStyle)
fieldState.fieldSize = memsize;
else
{
const size = (pastField + 7) / 8;
fieldState.fieldSize = size;
//printf(" offset: %d, size: %d\n", offset, size);
if (isunion)
{
const newstructsize = bfd.offset + size;
if (newstructsize > ad.structsize)
ad.structsize = newstructsize;
}
else
ad.structsize = bfd.offset + size;
}
//printf("at end: ad.structsize = %d\n", cast(int)ad.structsize);
//print(fieldState);
if (!isunion)
{
fieldState.offset = bfd.offset + fieldState.fieldSize;
fieldState.bitOffset = pastField;
}
//printf("\t%s: offset = %d bitOffset = %d fieldWidth = %d memsize = %d\n", toChars(), offset, bitOffset, fieldWidth, memsize);
//printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
//printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize);
}
override void visit(TemplateMixin tm)
{
//printf("TemplateMixin.setFieldOffset() %s\n", tm.toChars());
if (tm._scope) // if fwd reference
dsymbolSemantic(tm, null); // try to resolve it
tm.members.foreachDsymbol( (s) { s.setFieldOffset(ad, fieldState, isunion); } );
}
override void visit(AttribDeclaration atd)
{
atd.include(null).foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) );
}
override void visit(AnonDeclaration anond)
{
//printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", anond);
if (anond.decl)
{
/* This works by treating an AnonDeclaration as an aggregate 'member',
* so in order to place that member we need to compute the member's
* size and alignment.
*/
size_t fieldstart = ad.fields.length;
/* Hackishly hijack ad's structsize and alignsize fields
* for use in our fake anon aggregate member.
*/
uint savestructsize = ad.structsize;
uint savealignsize = ad.alignsize;
ad.structsize = 0;
ad.alignsize = 0;
FieldState fs;
anond.decl.foreachDsymbol( (s)
{
s.setFieldOffset(ad, &fs, anond.isunion);
if (anond.isunion)
fs.offset = 0;
});
/* https://issues.dlang.org/show_bug.cgi?id=13613
* If the fields in this.members had been already
* added in ad.fields, just update *poffset for the subsequent
* field offset calculation.
*/
if (fieldstart == ad.fields.length)
{
ad.structsize = savestructsize;
ad.alignsize = savealignsize;
fieldState.offset = ad.structsize;
return;
}
anond.anonstructsize = ad.structsize;
anond.anonalignsize = ad.alignsize;
ad.structsize = savestructsize;
ad.alignsize = savealignsize;
// 0 sized structs are set to 1 byte
if (anond.anonstructsize == 0)
{
anond.anonstructsize = 1;
anond.anonalignsize = 1;
}
assert(anond._scope);
auto alignment = anond._scope.alignment();
/* Given the anon 'member's size and alignment,
* go ahead and place it.
*/
anond.anonoffset = placeField(anond.loc,
fieldState.offset,
anond.anonstructsize, anond.anonalignsize, alignment,
ad.structsize, ad.alignsize,
isunion);
// Add to the anon fields the base offset of this anonymous aggregate
//printf("anon fields, anonoffset = %d\n", anonoffset);
foreach (const i; fieldstart .. ad.fields.length)
{
VarDeclaration v = ad.fields[i];
//printf("\t[%d] %s %d\n", i, v.toChars(), v.offset);
v.offset += anond.anonoffset;
}
}
}
}
extern(D) Scope* newScope(Dsymbol d, Scope* sc)
{
scope nsv = new NewScopeVisitor(sc);
d.accept(nsv);
return nsv.sc;
}
private extern(C++) class NewScopeVisitor : Visitor
{
alias visit = typeof(super).visit;
Scope* sc;
this(Scope* sc)
{
this.sc = sc;
}
/****************************************
* A hook point to supply scope for members.
* addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this.
*/
override void visit(AttribDeclaration dc){}
override void visit(StorageClassDeclaration swt)
{
STC scstc = sc.stc;
/* These sets of storage classes are mutually exclusive,
* so choose the innermost or most recent one.
*/
if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest))
scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest);
if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared))
scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared);
if (swt.stc & (STC.const_ | STC.immutable_ | STC.manifest))
scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest);
if (swt.stc & (STC.gshared | STC.shared_))
scstc &= ~(STC.gshared | STC.shared_);
if (swt.stc & (STC.safe | STC.trusted | STC.system))
scstc &= ~(STC.safe | STC.trusted | STC.system);
scstc |= swt.stc;
//printf("scstc = x%llx\n", scstc);
sc = swt.createNewScope(sc, scstc, sc.linkage, sc.cppmangle,
sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining);
}
/**
* Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set
*
* Calls `StorageClassDeclaration.newScope` (as it must be called or copied
* in any function overriding `newScope`), then set the `Scope`'s depdecl.
*
* Returns:
* Always a new scope, to use for this `DeprecatedDeclaration`'s members.
*/
override void visit(DeprecatedDeclaration dpd)
{
auto oldsc = sc;
visit((cast(StorageClassDeclaration)dpd));
auto scx = sc;
sc = oldsc;
// The enclosing scope is deprecated as well
if (scx == sc)
scx = sc.push();
scx.depdecl = dpd;
sc = scx;
}
override void visit(LinkDeclaration lid)
{
sc= lid.createNewScope(sc, sc.stc, lid.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility,
sc.aligndecl, sc.inlining);
}
override void visit(CPPMangleDeclaration cpmd)
{
sc = cpmd.createNewScope(sc, sc.stc, LINK.cpp, cpmd.cppmangle, sc.visibility, sc.explicitVisibility,
sc.aligndecl, sc.inlining);
}
/**
* Returns:
* A copy of the parent scope, with `this` as `namespace` and C++ linkage
*///override Scope* visit(Scope* sc)
override void visit(CPPNamespaceDeclaration scd)
{
auto scx = sc.copy();
scx.linkage = LINK.cpp;
scx.namespace = scd;
sc = scx;
}
override void visit(VisibilityDeclaration atbd)
{
if (atbd.pkg_identifiers)
dsymbolSemantic(atbd, sc);
sc = atbd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, atbd.visibility, 1, sc.aligndecl, sc.inlining);
}
override void visit(AlignDeclaration visd)
{
sc = visd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility,
sc.explicitVisibility, visd, sc.inlining);
}
override void visit(PragmaDeclaration prd)
{
if (prd.ident == Id.Pinline)
{
// We keep track of this pragma inside scopes,
// then it's evaluated on demand in function semantic
sc = prd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, prd); // @suppress(dscanner.style.long_line)
}
}
/**************************************
* Use the ForwardingScopeDsymbol as the parent symbol for members.
*/
override void visit(ForwardingAttribDeclaration fad)
{
sc = sc.push(fad.sym);
}
override void visit(UserAttributeDeclaration uac)
{
Scope* sc2 = sc;
if (uac.atts && uac.atts.length)
{
// create new one for changes
sc2 = sc.copy();
sc2.userAttribDecl = uac;
}
sc = sc2;
}
}
Dsymbols* include(Dsymbol d, Scope* sc)
{
scope icv = new ConditionIncludeVisitor(sc);
d.accept(icv);
return icv.symbols;
}
extern(C++) class ConditionIncludeVisitor : Visitor
{
alias visit = typeof(super).visit;
Scope* sc;
Dsymbols* symbols;
this(Scope* sc)
{
this.sc = sc;
}
override void visit(AttribDeclaration ad)
{
if (ad.errors)
{
symbols = null;
return;
}
symbols = ad.decl;
return;
}
// Decide if 'then' or 'else' code should be included
override void visit(ConditionalDeclaration cdc)
{
//printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
if (cdc.errors)
{
symbols = null;
return;
}
assert(cdc.condition);
symbols = dmd.expressionsem.include(cdc.condition, cdc._scope ? cdc._scope : sc) ? cdc.decl : cdc.elsedecl;
}
override void visit(StaticIfDeclaration sif)
{
/****************************************
* Different from other AttribDeclaration subclasses, include() call requires
* the completion of addMember and setScope phases.
*/
//printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
if (sif.errors || sif.onStack)
{
symbols = null;
return;
}
sif.onStack = true;
scope(exit) sif.onStack = false;
if (sc && sif.condition.inc == Include.notComputed)
{
assert(sif.scopesym); // addMember is already done
assert(sif._scope); // setScope is already done
Scope* saved_scope = sc;
sc = sif._scope;
visit(cast(ConditionalDeclaration) sif);
Dsymbols* d = symbols;
sc = saved_scope;
if (d && !sif.addisdone)
{
// Add members lazily.
d.foreachDsymbol( s => s.addMember(sif._scope, sif.scopesym) );
// Set the member scopes lazily.
d.foreachDsymbol( s => s.setScope(sif._scope) );
sif.addisdone = true;
}
symbols = d;
return;
}
else
{
visit(cast(ConditionalDeclaration)sif);
}
}
override void visit(StaticForeachDeclaration sfd)
{
if (sfd.errors || sfd.onStack)
{
symbols = null;
return;
}
if (sfd.cached)
{
assert(!sfd.onStack);
symbols = sfd.cache;
return;
}
sfd.onStack = true;
scope(exit) sfd.onStack = false;
if (sfd._scope)
{
sfd.sfe.prepare(sfd._scope); // lower static foreach aggregate
}
if (!sfd.sfe.ready())
{
symbols = null;// TODO: ok?
return;
}
// expand static foreach
import dmd.statementsem: makeTupleForeach;
Dsymbols* d = makeTupleForeach(sfd._scope, true, true, sfd.sfe.aggrfe, sfd.decl, sfd.sfe.needExpansion).decl;
if (d) // process generated declarations
{
// Add members lazily.
d.foreachDsymbol( s => s.addMember(sfd._scope, sfd.scopesym) );
// Set the member scopes lazily.
d.foreachDsymbol( s => s.setScope(sfd._scope) );
}
sfd.cached = true;
sfd.cache = d;
symbols = d;
}
}
/**
* Called from a symbol's semantic to check if `gnuAbiTag` UDA
* can be applied to them
*
* Directly emits an error if the UDA doesn't work with this symbol
*
* Params:
* sym = symbol to check for `gnuAbiTag`
* linkage = Linkage of the symbol (Declaration.link or sc.link)
*/
void checkGNUABITag(Dsymbol sym, LINK linkage)
{
if (global.params.cplusplus < CppStdRevision.cpp11)
return;
foreachUdaNoSemantic(sym, (exp) {
if (!isGNUABITag(exp))
return 0; // continue
if (sym.isCPPNamespaceDeclaration() || sym.isNspace())
{
.error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars());
sym.errors = true;
}
else if (linkage != LINK.cpp)
{
.error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars());
sym.errors = true;
}
// Only one `@gnuAbiTag` is allowed by semantic2
return 1; // break
});
}
/**
* Check if the provided expression references `core.attribute.gnuAbiTag`
*
* This should be called after semantic has been run on the expression.
* Semantic on UDA happens in semantic2 (see `dmd.semantic2`).
*
* Params:
* e = Expression to check (usually from `UserAttributeDeclaration.atts`)
*
* Returns:
* `true` if the expression references the compiler-recognized `gnuAbiTag`
*/
bool isGNUABITag(Expression e)
{
if (global.params.cplusplus < CppStdRevision.cpp11)
return false;
auto ts = e.type ? e.type.isTypeStruct() : null;
if (!ts)
return false;
if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent)
return false;
// Can only be defined in druntime
Module m = ts.sym.parent.isModule();
if (!m || !m.isCoreModule(Id.attribute))
return false;
return true;
}
/******************************************
* If a variable has a scope destructor call, return call for it.
* Otherwise, return NULL.
*/
private Expression callScopeDtor(VarDeclaration vd, Scope* sc)
{
//printf("VarDeclaration::callScopeDtor() %s\n", toChars());
// Destruction of STC.field's is handled by buildDtor()
if (vd.storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field))
{
return null;
}
if (vd.iscatchvar)
return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here
Expression e = null;
// Destructors for structs and arrays of structs
Type tv = vd.type.baseElemOf();
if (tv.ty == Tstruct)
{
StructDeclaration sd = (cast(TypeStruct)tv).sym;
if (!sd.dtor || sd.errors)
return null;
const sz = vd.type.size();
assert(sz != SIZE_INVALID);
if (!sz)
return null;
if (vd.type.toBasetype().ty == Tstruct)
{
// v.__xdtor()
e = new VarExp(vd.loc, vd);
/* This is a hack so we can call destructors on const/immutable objects.
* Need to add things like "const ~this()" and "immutable ~this()" to
* fix properly.
*/
e.type = e.type.mutableOf();
// Enable calling destructors on shared objects.
// The destructor is always a single, non-overloaded function,
// and must serve both shared and non-shared objects.
e.type = e.type.unSharedOf;
e = new DotVarExp(vd.loc, e, sd.dtor, false);
e = new CallExp(vd.loc, e);
}
else
{
// __ArrayDtor(v[0 .. n])
e = new VarExp(vd.loc, vd);
const sdsz = sd.type.size();
assert(sdsz != SIZE_INVALID && sdsz != 0);
const n = sz / sdsz;
SliceExp se = new SliceExp(vd.loc, e, new IntegerExp(vd.loc, 0, Type.tsize_t),
new IntegerExp(vd.loc, n, Type.tsize_t));
// Prevent redundant bounds check
se.upperIsInBounds = true;
se.lowerIsLessThanUpper = true;
// This is a hack so we can call destructors on const/immutable objects.
se.type = sd.type.arrayOf();
e = new CallExp(vd.loc, new IdentifierExp(vd.loc, Id.__ArrayDtor), se);
}
return e;
}
// Destructors for classes
if (!(vd.storage_class & (STC.auto_ | STC.scope_) && !(vd.storage_class & STC.parameter)))
return null;
for (ClassDeclaration cd = vd.type.isClassHandle(); cd; cd = cd.baseClass)
{
/* We can do better if there's a way with onstack
* classes to determine if there's no way the monitor
* could be set.
*/
//if (cd.isInterfaceDeclaration())
// error("interface `%s` cannot be scope", cd.toChars());
if (!vd.onstack) // if any destructors
continue;
// delete'ing C++ classes crashes (and delete is deprecated anyway)
if (cd.classKind == ClassKind.cpp)
{
// Don't call non-existant dtor
if (!cd.dtor)
break;
e = new VarExp(vd.loc, vd);
e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances
e = new DotVarExp(vd.loc, e, cd.dtor, false);
e = new CallExp(vd.loc, e);
break;
}
// delete this;
Expression ec;
ec = new VarExp(vd.loc, vd);
e = new DeleteExp(vd.loc, ec, true);
break;
}
return e;
}
/***************************************
* Collect all instance fields, then determine instance size.
* Returns:
* false if failed to determine the size.
*/
bool determineSize(AggregateDeclaration ad, Loc loc)
{
//printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
// The previous instance size finalizing had:
if (ad.type.ty == Terror || ad.errors)
return false; // failed already
if (ad.sizeok == Sizeok.done)
return true; // succeeded
if (!ad.members)
{
.error(loc, "%s `%s` unknown size", ad.kind, ad.toPrettyChars);
return false;
}
if (ad._scope)
dsymbolSemantic(ad, null);
// Determine the instance size of base class first.
if (auto cd = ad.isClassDeclaration())
{
cd = cd.baseClass;
if (cd && !cd.determineSize(loc))
goto Lfail;
}
// Determine instance fields when sizeok == Sizeok.none
if (!ad.determineFields())
goto Lfail;
if (ad.sizeok != Sizeok.done)
ad.finalizeSize();
// this aggregate type has:
if (ad.type.ty == Terror)
return false; // marked as invalid during the finalizing.
if (ad.sizeok == Sizeok.done)
return true; // succeeded to calculate instance size.
Lfail:
// There's unresolvable forward reference.
if (ad.type != Type.terror)
error(loc, "%s `%s` no size because of forward reference", ad.kind, ad.toPrettyChars);
// Don't cache errors from speculative semantic, might be resolvable later.
// https://issues.dlang.org/show_bug.cgi?id=16574
if (!global.gag)
{
ad.type = Type.terror;
ad.errors = true;
}
return false;
}
void addComment(Dsymbol d, const(char)* comment)
{
scope v = new AddCommentVisitor(comment);
d.accept(v);
}
extern (C++) class AddCommentVisitor: Visitor
{
alias visit = Visitor.visit;
const(char)* comment;
this(const(char)* comment)
{
this.comment = comment;
}
override void visit(Dsymbol d)
{
if (!comment || !*comment)
return;
//printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars());
void* h = cast(void*)d; // just the pointer is the key
auto p = h in d.commentHashTable;
if (!p)
{
d.commentHashTable[h] = comment;
return;
}
if (strcmp(*p, comment) != 0)
{
// Concatenate the two
*p = Lexer.combineComments((*p).toDString(), comment.toDString(), true);
}
}
override void visit(AttribDeclaration atd)
{
if (comment)
{
atd.include(null).foreachDsymbol( s => s.addComment(comment) );
}
}
override void visit(ConditionalDeclaration cd)
{
if (comment)
{
cd.decl .foreachDsymbol( s => s.addComment(comment) );
cd.elsedecl.foreachDsymbol( s => s.addComment(comment) );
}
}
override void visit(StaticForeachDeclaration sfd) {}
}
void checkCtorConstInit(Dsymbol d)
{
scope v = new CheckCtorConstInitVisitor();
d.accept(v);
}
private extern(C++) class CheckCtorConstInitVisitor : Visitor
{
alias visit = Visitor.visit;
override void visit(AttribDeclaration ad)
{
ad.include(null).foreachDsymbol( s => s.checkCtorConstInit() );
}
override void visit(VarDeclaration vd)
{
version (none)
{
/* doesn't work if more than one static ctor */
if (vd.ctorinit == 0 && vd.isCtorinit() && !vd.isField())
error("missing initializer in static constructor for const variable");
}
}
override void visit(Dsymbol d){}
}
/**************************************
* Determine if this symbol is only one.
* Returns:
* false, ps = null: There are 2 or more symbols
* true, ps = null: There are zero symbols
* true, ps = symbol: The one and only one symbol
*/
bool oneMember(Dsymbol d, out Dsymbol ps, Identifier ident)
{
scope v = new OneMemberVisitor(ps, ident);
d.accept(v);
return v.result;
}
private extern(C++) class OneMemberVisitor : Visitor
{
alias visit = Visitor.visit;
Dsymbol* ps;
Identifier ident;
bool result;
this(out Dsymbol ps, Identifier ident)
{
this.ps = &ps;
this.ident = ident;
}
override void visit(AttribDeclaration atb)
{
Dsymbols* d = atb.include(null);
result = oneMembers(d, *ps, ident);
}
override void visit(StaticForeachDeclaration sfd)
{
// Required to support IFTI on a template that contains a
// `static foreach` declaration. `super.oneMember` calls
// include with a `null` scope. As `static foreach` requires
// the scope for expansion, `oneMember` can only return a
// precise result once `static foreach` has been expanded.
if (sfd.cached)
{
this.visit(cast(AttribDeclaration) sfd);
}
else
{
*ps = null; // a `static foreach` declaration may in general expand to multiple symbols
result = false;
}
}
override void visit(StorageClassDeclaration scd)
{
bool t = oneMembers(scd.decl, *ps, ident);
if (t && *ps)
{
/* This is to deal with the following case:
* struct Tick {
* template to(T) { const T to() { ... } }
* }
* For eponymous function templates, the 'const' needs to get attached to 'to'
* before the semantic analysis of 'to', so that template overloading based on the
* 'this' pointer can be successful.
*/
if (FuncDeclaration fd = (*ps).isFuncDeclaration())
{
/* Use storage_class2 instead of storage_class otherwise when we do .di generation
* we'll wind up with 'const const' rather than 'const'.
*/
/* Don't think we need to worry about mutually exclusive storage classes here
*/
fd.storage_class2 |= scd.stc;
}
}
result = t;
}
override void visit(ConditionalDeclaration cd)
{
//printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition.inc);
if (cd.condition.inc != Include.notComputed)
{
Dsymbols* d = dmd.expressionsem.include(cd.condition, null) ? cd.decl : cd.elsedecl;
result = oneMembers(d, *ps, ident);
}
else
{
bool res = (oneMembers(cd.decl, *ps, ident) && *ps is null && oneMembers(cd.elsedecl, *ps, ident) && *ps is null);
*ps = null;
result = res;
}
}
override void visit(ScopeDsymbol sd)
{
if (sd.isAnonymous())
result = oneMembers(sd.members, *ps, ident);
else {
// visit(Dsymbol dsym)
*ps = sd;
result = true;
}
}
override void visit(StaticAssert sa)
{
//printf("StaticAssert::oneMember())\n");
*ps = null;
result = true;
}
override void visit(TemplateInstance ti)
{
*ps = null;
result = true;
}
override void visit(TemplateMixin tm)
{
*ps = tm;
result = true;
}
override void visit(Dsymbol dsym)
{
*ps = dsym;
result = true;
}
}
/****************************************
* Return true if any of the members are static ctors or static dtors, or if
* any members have members that are.
*/
bool hasStaticCtorOrDtor(Dsymbol d)
{
scope v = new HasStaticCtorOrDtor();
d.accept(v);
return v.result;
}
private extern(C++) class HasStaticCtorOrDtor : Visitor
{
import dmd.mtype : Type;
alias visit = Visitor.visit;
bool result;
// attrib.d
override void visit(AttribDeclaration ad){
result = ad.include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0;
}
// dsymbol.d
override void visit(Dsymbol d){
//printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars());
result = false;
}
override void visit(ScopeDsymbol sd) {
if (sd.members)
{
for (size_t i = 0; i < sd.members.length; i++)
{
Dsymbol member = (*(sd.members))[i];
if (member.hasStaticCtorOrDtor())
result = true;
return;
}
}
result = false;
}
// dtemplate.d
override void visit(TemplateDeclaration td) {
result = false; // don't scan uninstantiated templates
}
// func.d
override void visit(StaticCtorDeclaration scd) {
result = true;
}
override void visit(StaticDtorDeclaration sdd) @nogc nothrow pure @safe {
result = true;
}
}
bool isFuncHidden(ClassDeclaration cd, FuncDeclaration fd)
{
import dmd.funcsem : overloadApply;
//printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars());
Dsymbol s = cd.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors);
if (!s)
{
//printf("not found\n");
/* Because, due to a hack, if there are multiple definitions
* of fd.ident, NULL is returned.
*/
return false;
}
s = s.toAlias();
if (auto os = s.isOverloadSet())
{
foreach (sm; os.a)
{
auto fm = sm.isFuncDeclaration();
if (overloadApply(fm, s => fd == s.isFuncDeclaration()))
return false;
}
return true;
}
else
{
auto f = s.isFuncDeclaration();
//printf("%s fdstart = %p\n", s.kind(), fdstart);
if (overloadApply(f, s => fd == s.isFuncDeclaration()))
return false;
return !fd.parent.isTemplateMixin();
}
}
Dsymbol vtblSymbol(ClassDeclaration cd)
{
if (!cd.vtblsym)
{
auto vtype = Type.tvoidptr.immutableOf().sarrayOf(cd.vtbl.length);
auto var = new VarDeclaration(cd.loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_);
var.addMember(null, cd);
var.isdataseg = 1;
var._linkage = LINK.d;
var.semanticRun = PASS.semanticdone; // no more semantic wanted
cd.vtblsym = var;
}
return cd.vtblsym;
}
bool isAbstract(ClassDeclaration cd)
{
enum log = false;
if (cd.isabstract != ThreeState.none)
return cd.isabstract == ThreeState.yes;
if (log) printf("isAbstract(%s)\n", cd.toChars());
bool no() { if (log) printf("no\n"); cd.isabstract = ThreeState.no; return false; }
bool yes() { if (log) printf("yes\n"); cd.isabstract = ThreeState.yes; return true; }
if (cd.storage_class & STC.abstract_ || cd._scope && cd._scope.stc & STC.abstract_)
return yes();
if (cd.errors)
return no();
/* https://issues.dlang.org/show_bug.cgi?id=11169
* Resolve forward references to all class member functions,
* and determine whether this class is abstract.
*/
static int func(Dsymbol s, void*)
{
auto fd = s.isFuncDeclaration();
if (!fd)
return 0;
if (fd.storage_class & STC.static_)
return 0;
if (fd.isAbstract())
return 1;
return 0;
}
// opaque class is not abstract if it is not declared abstract
if (!(cd.members))
return no();
for (size_t i = 0; i < cd.members.length; i++)
{
auto s = (*(cd.members))[i];
if (s.apply(&func, null))
{
return yes();
}
}
/* If the base class is not abstract, then this class cannot
* be abstract.
*/
if (!cd.isInterfaceDeclaration() && (!cd.baseClass || !cd.baseClass.isAbstract()))
return no();
/* If any abstract functions are inherited, but not overridden,
* then the class is abstract. Do this by checking the vtbl[].
* Need to do semantic() on class to fill the vtbl[].
*/
cd.dsymbolSemantic(null);
/* The next line should work, but does not because when ClassDeclaration.dsymbolSemantic()
* is called recursively it can set PASS.semanticdone without finishing it.
*/
//if (semanticRun < PASS.semanticdone)
{
/* Could not complete semantic(). Try running semantic() on
* each of the virtual functions,
* which will fill in the vtbl[] overrides.
*/
static int virtualSemantic(Dsymbol s, void*)
{
auto fd = s.isFuncDeclaration();
if (fd && !(fd.storage_class & STC.static_) && !fd.isUnitTestDeclaration())
fd.dsymbolSemantic(null);
return 0;
}
for (size_t i = 0; i < cd.members.length; i++)
{
auto s = (*(cd.members))[i];
s.apply(&virtualSemantic,null);
}
}
/* Finally, check the vtbl[]
*/
foreach (i; 1 .. cd.vtbl.length)
{
auto fd = cd.vtbl[i].isFuncDeclaration();
//if (fd) printf("\tvtbl[%d] = [%s] %s\n", i, fd.loc.toChars(), fd.toPrettyChars());
if (!fd || fd.isAbstract())
{
return yes();
}
}
return no();
}
void finalizeSize(AggregateDeclaration ad)
{
scope v = new FinalizeSizeVisitor();
ad.accept(v);
}
/**********************************
* Determine if exp is all binary zeros.
* Params:
* exp = expression to check
* Returns:
* true if it's all binary 0
*/
bool _isZeroInit(Expression exp)
{
switch (exp.op)
{
case EXP.int64:
return exp.toInteger() == 0;
case EXP.null_:
return true;
case EXP.structLiteral:
{
auto sle = exp.isStructLiteralExp();
if (sle.sd.isNested())
return false;
const isCstruct = sle.sd.isCsymbol(); // C structs are default initialized to all zeros
foreach (i; 0 .. sle.sd.fields.length)
{
auto field = sle.sd.fields[i];
if (field.type.size(field.loc))
{
auto e = sle.elements && i < sle.elements.length ? (*sle.elements)[i] : null;
if (e ? !_isZeroInit(e)
: !isCstruct && !field.type.isZeroInit(field.loc))
return false;
}
}
return true;
}
case EXP.arrayLiteral:
{
auto ale = cast(ArrayLiteralExp)exp;
const dim = ale.elements ? ale.elements.length : 0;
if (ale.type.toBasetype().ty == Tarray) // if initializing a dynamic array
return dim == 0;
foreach (i; 0 .. dim)
{
if (!_isZeroInit(ale[i]))
return false;
}
/* Note that true is returned for all T[0]
*/
return true;
}
case EXP.string_:
{
auto se = cast(StringExp)exp;
if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array
return se.len == 0;
foreach (i; 0 .. se.len)
{
if (se.getIndex(i) != 0)
return false;
}
return true;
}
case EXP.vector:
{
auto ve = cast(VectorExp) exp;
return _isZeroInit(ve.e1);
}
case EXP.float64:
case EXP.complex80:
{
import dmd.root.ctfloat : CTFloat;
return (exp.toReal() is CTFloat.zero) &&
(exp.toImaginary() is CTFloat.zero);
}
default:
return false;
}
}
private extern(C++) class FinalizeSizeVisitor : Visitor
{
alias visit = Visitor.visit;
override void visit(ClassDeclaration outerCd)
{
assert(outerCd.sizeok != Sizeok.done);
// Set the offsets of the fields and determine the size of the class
if (outerCd.baseClass)
{
assert(outerCd.baseClass.sizeok == Sizeok.done);
outerCd.alignsize = outerCd.baseClass.alignsize;
if (outerCd.classKind == ClassKind.cpp)
outerCd.structsize = target.cpp.derivedClassOffset(outerCd.baseClass);
else
outerCd.structsize = outerCd.baseClass.structsize;
}
else if (outerCd.classKind == ClassKind.objc)
outerCd.structsize = 0; // no hidden member for an Objective-C class
else if (outerCd.isInterfaceDeclaration())
{
if (outerCd.interfaces.length == 0)
{
outerCd.alignsize = target.ptrsize;
outerCd.structsize = target.ptrsize; // allow room for __vptr
}
}
else
{
outerCd.alignsize = target.ptrsize;
outerCd.structsize = target.ptrsize; // allow room for __vptr
if (outerCd.hasMonitor())
outerCd.structsize += target.ptrsize; // allow room for __monitor
}
//printf("finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
size_t bi = 0; // index into vtblInterfaces[]
/****
* Runs through the inheritance graph to set the BaseClass.offset fields.
* Recursive in order to account for the size of the interface classes, if they are
* more than just interfaces.
* Params:
* cd = interface to look at
* baseOffset = offset of where cd will be placed
* Returns:
* subset of instantiated size used by cd for interfaces
*/
uint membersPlace(ClassDeclaration cd, uint baseOffset)
{
//printf(" membersPlace(%s, %d)\n", cd.toChars(), baseOffset);
uint offset = baseOffset;
foreach (BaseClass* b; cd.interfaces)
{
if (b.sym.sizeok != Sizeok.done)
b.sym.finalizeSize();
assert(b.sym.sizeok == Sizeok.done);
if (!b.sym.alignsize)
b.sym.alignsize = target.ptrsize;
offset = alignmember(structalign_t(cast(ushort)b.sym.alignsize), b.sym.alignsize, offset);
assert(bi < outerCd.vtblInterfaces.length);
BaseClass* bv = (*(outerCd.vtblInterfaces))[bi];
if (b.sym.interfaces.length == 0)
{
//printf("\tvtblInterfaces[%d] b=%p b.sym = %s, offset = %d\n", bi, bv, bv.sym.toChars(), offset);
bv.offset = offset;
++bi;
// All the base interfaces down the left side share the same offset
for (BaseClass* b2 = bv; b2.baseInterfaces.length; )
{
b2 = &b2.baseInterfaces[0];
b2.offset = offset;
//printf("\tvtblInterfaces[%d] b=%p sym = %s, offset = %d\n", bi, b2, b2.sym.toChars(), b2.offset);
}
}
membersPlace(b.sym, offset);
//printf(" %s size = %d\n", b.sym.toChars(), b.sym.structsize);
offset += b.sym.structsize;
if (outerCd.alignsize < b.sym.alignsize)
outerCd.alignsize = b.sym.alignsize;
}
return offset - baseOffset;
}
outerCd.structsize += membersPlace(outerCd, outerCd.structsize);
if (outerCd.isInterfaceDeclaration())
{
outerCd.sizeok = Sizeok.done;
return;
}
// FIXME: Currently setFieldOffset functions need to increase fields
// to calculate each variable offsets. It can be improved later.
outerCd.fields.setDim(0);
FieldState fieldState;
fieldState.offset = outerCd.structsize;
foreach (s; *(outerCd.members))
{
s.setFieldOffset(outerCd, &fieldState, false);
}
outerCd.sizeok = Sizeok.done;
// Calculate fields[i].overlapped
outerCd.checkOverlappedFields();
}
override void visit(StructDeclaration sd)
{
//printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
assert(sd.sizeok != Sizeok.done);
if (sd.sizeok == Sizeok.inProcess)
{
return;
}
sd.sizeok = Sizeok.inProcess;
//printf("+StructDeclaration::finalizeSize() %s, fields.length = %d, sizeok = %d\n", toChars(), fields.length, sizeok);
sd.fields.setDim(0); // workaround
// Set the offsets of the fields and determine the size of the struct
FieldState fieldState;
bool isunion = sd.isUnionDeclaration() !is null;
for (size_t i = 0; i < sd.members.length; i++)
{
Dsymbol s = (*sd.members)[i];
s.setFieldOffset(sd, &fieldState, isunion);
if (sd.type.ty == Terror)
{
errorSupplemental(s.loc, "error on member `%s`", s.toPrettyChars);
sd.errors = true;
return;
}
}
if (sd.structsize == 0)
{
sd.hasNoFields = true;
sd.alignsize = 1;
// A fine mess of what size a zero sized struct should be
final switch (sd.classKind)
{
case ClassKind.d:
case ClassKind.cpp:
sd.structsize = 1;
break;
case ClassKind.c:
case ClassKind.objc:
if (target.c.bitFieldStyle == TargetC.BitFieldStyle.MS)
{
/* Undocumented MS behavior for:
* struct S { int :0; };
*/
sd.structsize = 4;
}
else
sd.structsize = 0;
break;
}
}
// Round struct size up to next alignsize boundary.
// This will ensure that arrays of structs will get their internals
// aligned properly.
if (sd.alignment.isDefault() || sd.alignment.isPack())
sd.structsize = (sd.structsize + sd.alignsize - 1) & ~(sd.alignsize - 1);
else
sd.structsize = (sd.structsize + sd.alignment.get() - 1) & ~(sd.alignment.get() - 1);
sd.sizeok = Sizeok.done;
//printf("-StructDeclaration::finalizeSize() %s, fields.length = %d, structsize = %d\n", toChars(), cast(int)fields.length, cast(int)structsize);
if (sd.errors)
return;
// Calculate fields[i].overlapped
if (sd.checkOverlappedFields())
{
sd.errors = true;
return;
}
// Determine if struct is all zeros or not
sd.zeroInit = true;
auto lastOffset = -1;
foreach (vd; sd.fields)
{
// First skip zero sized fields
if (vd.type.size(vd.loc) == 0)
continue;
// only consider first sized member of an (anonymous) union
if (vd.overlapped && vd.offset == lastOffset)
continue;
lastOffset = vd.offset;
if (vd._init)
{
if (vd._init.isVoidInitializer())
/* Treat as 0 for the purposes of putting the initializer
* in the BSS segment, or doing a mass set to 0
*/
continue;
// Examine init to see if it is all 0s.
auto exp = vd.getConstInitializer();
if (!exp || !_isZeroInit(exp))
{
sd.zeroInit = false;
break;
}
}
else if (!vd.type.isZeroInit(sd.loc))
{
sd.zeroInit = false;
break;
}
}
sd.argTypes = target.toArgTypes(sd.type);
}
}
/*****************************************
* Is Dsymbol a variable that contains pointers?
*/
bool hasPointers(Dsymbol d)
{
scope v = new HasPointersVisitor();
d.accept(v);
return v.result;
}
private extern(C++) class HasPointersVisitor : Visitor
{
import dmd.mtype : Type;
alias visit = Visitor.visit;
bool result;
override void visit(AttribDeclaration ad)
{
result = ad.include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
}
override void visit(VarDeclaration vd)
{
import dmd.typesem : hasPointers;
result = (!vd.isDataseg() && vd.type.hasPointers());
}
override void visit(Dsymbol d)
{
//printf("Dsymbol::hasPointers() %s\n", toChars());
result = false;
}
override void visit(TemplateMixin tm)
{
//printf("TemplateMixin.hasPointers() %s\n", toChars());
result = tm.members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
}
override void visit(Nspace ns)
{
//printf("Nspace::hasPointers() %s\n", toChars());
result = ns.members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
}
}
/***************************************
* Expands attribute declarations in members in depth first
* order. Calls dg(size_t symidx, Dsymbol *sym) for each
* member.
* If dg returns !=0, stops and returns that value else returns 0.
* Use this function to avoid the O(N + N^2/2) complexity of
* calculating dim and calling N times getNth.
* Returns:
* last value returned by dg()
*/
int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null)
{
assert(dg);
if (!members)
return 0;
size_t n = pn ? *pn : 0; // take over index
int result = 0;
foreach (size_t i; 0 .. members.length)
{
import dmd.attrib : AttribDeclaration;
import dmd.dtemplate : TemplateMixin;
Dsymbol s = (*members)[i];
if (AttribDeclaration a = s.isAttribDeclaration())
result = _foreach(sc, a.include(sc), dg, &n);
else if (TemplateMixin tm = s.isTemplateMixin())
result = _foreach(sc, tm.members, dg, &n);
else if (s.isTemplateInstance())
{
}
else if (s.isUnitTestDeclaration())
{
}
else
result = dg(n++, s);
if (result)
break;
}
if (pn)
*pn = n; // update index
return result;
}
/****************************************
* Create array of the local classes in the Module, suitable
* for inclusion in ModuleInfo
* Params:
* mod = the Module
* aclasses = array to fill in
* Returns: array of local classes
*/
void getLocalClasses(Module mod, ref ClassDeclarations aclasses)
{
//printf("members.length = %d\n", mod.members.length);
int pushAddClassDg(size_t n, Dsymbol sm)
{
if (!sm)
return 0;
if (auto cd = sm.isClassDeclaration())
{
// compatibility with previous algorithm
if (cd.parent && cd.parent.isTemplateMixin())
return 0;
if (cd.classKind != ClassKind.objc)
aclasses.push(cd);
}
return 0;
}
_foreach(null, mod.members, &pushAddClassDg);
}
/*****************************************
* Same as Dsymbol::oneMember(), but look at an array of Dsymbols.
*/
extern (D) bool oneMembers(Dsymbols* members, out Dsymbol ps, Identifier ident)
{
//printf("Dsymbol::oneMembers() %d\n", members ? members.length : 0);
Dsymbol s = null;
if (!members)
{
ps = null;
return true;
}
for (size_t i = 0; i < members.length; i++)
{
Dsymbol sx = (*members)[i];
bool x = sx.oneMember(ps, ident); //MYTODO: this temporarily creates a new dependency to dsymbolsem, will need to extract oneMembers() later
//printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps);
if (!x)
{
//printf("\tfalse 1\n");
assert(ps is null);
return false;
}
if (ps)
{
assert(ident);
if (!ps.ident || !ps.ident.equals(ident))
continue;
if (!s)
s = ps;
else if (s.isOverloadable() && ps.isOverloadable())
{
// keep head of overload set
FuncDeclaration f1 = s.isFuncDeclaration();
FuncDeclaration f2 = ps.isFuncDeclaration();
if (f1 && f2)
{
assert(!f1.isFuncAliasDeclaration());
assert(!f2.isFuncAliasDeclaration());
for (; f1 != f2; f1 = f1.overnext0)
{
if (f1.overnext0 is null)
{
f1.overnext0 = f2;
break;
}
}
}
}
else // more than one symbol
{
ps = null;
//printf("\tfalse 2\n");
return false;
}
}
}
ps = s; // s is the one symbol, null if none
//printf("\ttrue\n");
return true;
}