blob: c9906363cc8cd396be353b831a120f6c1fc33686 [file] [log] [blame]
/**
* Does the semantic 1 pass on the AST, which looks at symbol declarations but not initializers
* or function bodies.
*
* Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d, _dsymbolsem.d)
* Documentation: https://dlang.org/phobos/dmd_dsymbolsem.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbolsem.d
*/
module dmd.dsymbolsem;
import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
import dmd.aliasthis;
import dmd.apply;
import dmd.arraytypes;
import dmd.astcodegen;
import dmd.astenums;
import dmd.attrib;
import dmd.blockexit;
import dmd.clone;
import dmd.compiler;
import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.dimport;
import dmd.dinterpret;
import dmd.dmangle;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.dversion;
import dmd.errors;
import dmd.escape;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.importc;
import dmd.init;
import dmd.initsem;
import dmd.hdrgen;
import dmd.mtype;
import dmd.nogc;
import dmd.nspace;
import dmd.objc;
import dmd.opover;
import dmd.parse;
import dmd.root.filename;
import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.root.utf;
import dmd.semantic2;
import dmd.semantic3;
import dmd.sideeffect;
import dmd.statementsem;
import dmd.staticassert;
import dmd.tokens;
import dmd.utils;
import dmd.statement;
import dmd.target;
import dmd.templateparamsem;
import dmd.typesem;
import dmd.visitor;
enum LOG = false;
private uint setMangleOverride(Dsymbol s, const(char)[] sym)
{
if (s.isFuncDeclaration() || s.isVarDeclaration())
{
s.isDeclaration().mangleOverride = sym;
return 1;
}
if (auto ad = s.isAttribDeclaration())
{
uint nestedCount = 0;
ad.include(null).foreachDsymbol( (s) { nestedCount += setMangleOverride(s, sym); } );
return nestedCount;
}
return 0;
}
/*************************************
* Does semantic analysis on the public face of declarations.
*/
extern(C++) 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.flags & SCOPE.Cfile && 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
dd.msg.error("compile time constant expected, not `%s`", dd.msg.toChars());
}
return dd.msgstr;
}
// 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;
}
private extern(C++) final class DsymbolSemanticVisitor : Visitor
{
alias visit = Visitor.visit;
Scope* sc;
this(Scope* sc)
{
this.sc = sc;
}
// Save the scope and defer semantic analysis on the Dsymbol.
private void deferDsymbolSemantic(Dsymbol s, Scope *scx)
{
s._scope = scx ? scx : sc.copy();
s._scope.setNoFree();
Module.addDeferredSemantic(s);
}
override void visit(Dsymbol dsym)
{
dsym.error("%p has no semantic routine", 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)
{
s = sc.search(dsym.loc, dsym.ident, null);
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);
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++;
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)
dsym.error("extern symbols cannot have initializers");
AggregateDeclaration ad = dsym.isThis();
if (ad)
dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
/* 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;
if (needctfe)
{
sc.flags |= SCOPE.condition;
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.flags & SCOPE.Cfile) != 0).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 (sc.func && !sc.intypeof)
{
if (dsym.storage_class & STC.gshared && !dsym.isMember())
{
if (sc.func.setUnsafe())
dsym.error("__gshared not allowed in safe functions; use 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)
{
dsym.error("type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`", dsym.type.toChars(), dsym._init.toChars());
}
else
dsym.error("variables cannot be of type `void`");
dsym.type = Type.terror;
tb = dsym.type;
}
if (tb.ty == Tfunction)
{
dsym.error("cannot be declared to be a function");
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_)))
{
dsym.error("no definition of struct `%s`", 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());
}
// 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.error("storage class `auto` has no effect if type is not inferred, did you mean `scope`?");
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.flags & SCOPE.Cfile) != 0) : 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.dim; pos++)
{
Lexpand1:
Expression e = (*iexps)[pos];
Parameter arg = Parameter.getNth(tt.arguments, pos);
arg.type = arg.type.typeSemantic(dsym.loc, sc);
//printf("[%d] iexps.dim = %d, ", pos, iexps.dim);
//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.dim > nelems)
goto Lnomatch;
if (e.type.implicitConvTo(arg.type))
continue;
}
if (auto te = e.isTupleExp())
{
if (iexps.dim - 1 + te.exps.dim > 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(0, "__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.dim; 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.dim = %d, ", pos, u, exps.dim);
//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.dim - 1 + exps.dim;
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.dim < 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.dim;
if (tedim != nelems)
{
error(dsym.loc, "tuple of %d elements cannot be assigned to tuple 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;
StorageClass storage_class = STC.temp | STC.local | 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);
if (sc.scopesym)
{
//printf("adding %s to %s\n", v.toChars(), sc.scopesym.toChars());
if (sc.scopesym.members)
// Note this prevents using foreach() over members, because the limits can change
sc.scopesym.members.push(v);
}
Expression e = new DsymbolExp(dsym.loc, v);
(*exps)[i] = e;
}
auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
v2.parent = dsym.parent;
v2.isexp = true;
dsym.aliassym = 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 (StorageClass stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_))
{
if (stc == STC.final_)
dsym.error("cannot be `final`, perhaps you meant `const`?");
else
{
OutBuffer buf;
stcToBuffer(&buf, stc);
dsym.error("cannot be `%s`", 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.in_ && global.params.previewIn)
dsym.storage_class |= STC.scope_;
if (dsym.storage_class & STC.scope_)
{
StorageClass stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared);
if (stc)
{
OutBuffer buf;
stcToBuffer(&buf, stc);
dsym.error("cannot be `scope` and `%s`", buf.peekChars());
}
else if (dsym.isMember())
{
dsym.error("field cannot be `scope`");
}
else if (!dsym.type.hasPointers())
{
dsym.storage_class &= ~STC.scope_; // silently ignore; may occur in generic code
}
}
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.vfield && 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)
{
dsym.error("field not allowed in interface");
}
else if (aad && aad.sizeok == Sizeok.done)
{
dsym.error("cannot be further field because it will change the determined %s size", 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.undefined_)
{
dsym.error("cannot use template to add field to aggregate `%s`", ad2.toChars());
}
}
}
if ((dsym.storage_class & (STC.ref_ | STC.parameter | STC.foreach_ | STC.temp | STC.result)) == STC.ref_ && dsym.ident != Id.This)
{
dsym.error("only parameters or `foreach` declarations can be `ref`");
}
if (dsym.type.hasWild())
{
if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field) || dsym.isDataseg())
{
dsym.error("only parameters or stack based variables can be `inout`");
}
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)
{
dsym.error("`inout` variables can only be declared inside `inout` functions");
}
}
}
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
dsym.error("default construction is disabled for type `%s`", dsym.type.toChars());
}
}
FuncDeclaration fd = parent.isFuncDeclaration();
if (dsym.type.isscope() && !(dsym.storage_class & STC.nodtor))
{
if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd)
{
dsym.error("globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`");
}
// @@@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)
dsym.error("reference to `scope class` must be `scope`");
}
}
// Calculate type size + safety checks
if (sc.func && !sc.intypeof)
{
if (dsym._init && dsym._init.isVoidInitializer() &&
(dsym.type.hasPointers() || dsym.type.hasInvariant())) // also computes type size
{
if (sc.func.setUnsafe())
{
if (dsym.type.hasPointers())
dsym.error("`void` initializers for pointers not allowed in safe functions");
else
dsym.error("`void` initializers for structs with invariants are not allowed in safe functions");
}
}
else if (!dsym._init &&
!(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&
dsym.type.hasVoidInitPointers())
{
if (sc.func.setUnsafe())
dsym.error("`void` initializers for pointers not allowed in safe functions");
}
}
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)
dsym.error("manifest constants must have initializers");
bool isBlit = false;
uinteger_t sz;
if (sc.flags & SCOPE.Cfile && !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)
dsym.error("size of type `%s` is invalid", 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)
{
dsym.error("`%s` does not have a default initializer", 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.flags & SCOPE.Cfile &&
dsym.type.isTypeSArray() &&
dsym.type.isTypeSArray().isIncomplete() &&
dsym._init.isVoidInitializer() &&
!(dsym.storage_class & STC.field))
{
dsym.error("incomplete array type must have initializer");
}
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(), 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.flags & SCOPE.Cfile) != 0);
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.flags & SCOPE.Cfile) != 0);
if (!e)
{
dsym.error("is not a static and cannot have static initializer");
e = ErrorExp.get();
}
}
ei = new ExpInitializer(dsym._init.loc, e);
dsym._init = ei;
}
else if (sc.flags & SCOPE.Cfile && 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);
}
Expression exp = ei.exp;
Expression e1 = new VarExp(dsym.loc, dsym);
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--;
exp = exp.optimize(WANTvalue);
if (exp.op == EXP.error)
{
dsym._init = new ErrorInitializer();
ei = null;
}
else
ei.exp = exp;
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())
{
// See if initializer is a NewExp that can be allocated on the stack
if (dsym.type.toBasetype().ty == Tclass)
{
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
{
// 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);
const init_err = dsym._init.isExpInitializer();
if (init_err && init_err.exp.op == EXP.showCtfeContext)
{
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.flags & SCOPE.Cfile)
{
/* 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)
{
uint 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();
exp = exp.expressionSemantic(sc);
exp = resolveProperties(sc, exp);
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())
dsym.error("of type struct `%s` uses `this(this)`, which is not allowed in static initialization", 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_))
dsym.error("static storage variables cannot have destructors");
}
}
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(BitFieldDeclaration dsym)
{
//printf("BitField::semantic('%s') %s\n", toPrettyChars(), id.toChars());
if (dsym.semanticRun >= PASS.semanticdone)
return;
visit(cast(VarDeclaration)dsym);
if (dsym.errors)
return;
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
width.error("bit-field type `%s` is not an integer type", dsym.type.toChars());
dsym.errors = true;
}
if (!width.isIntegerExp())
{
width.error("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())
{
width.error("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)
{
width.error("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)
{
//printf("Import::semantic('%s') %s\n", toPrettyChars(), id.toChars());
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
bool loadErrored = false;
if (!imp.mod)
{
loadErrored = imp.load(sc);
if (imp.mod)
{
imp.mod.importAll(null);
imp.mod.checkImportDeprecation(imp.loc, sc);
}
}
if (imp.mod)
{
// 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.dim) // neither a selective nor a renamed import
{
ScopeDsymbol scopesym = sc.getScopesym();
if (!imp.isstatic)
{
scopesym.importScope(imp.mod, imp.visibility);
}
imp.addPackageAccess(scopesym);
}
if (!loadErrored)
{
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.dim; 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], IgnorePrivateImports);
if (sym)
{
import dmd.access : symbolIsVisible;
if (!symbolIsVisible(sc, sym))
imp.mod.error(imp.loc, "member `%s` is not visible from module `%s`",
imp.names[i].toChars(), sc._module.toChars());
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]);
if (s)
imp.mod.error(imp.loc, "import `%s` not found, did you mean %s `%s`?", imp.names[i].toChars(), s.kind(), s.toPrettyChars());
else
imp.mod.error(imp.loc, "import `%s` not found", imp.names[i].toChars());
ad.type = Type.terror;
}
}
sc = sc.pop();
}
imp.semanticRun = PASS.semanticdone;
// object self-imports itself, so skip that
// https://issues.dlang.org/show_bug.cgi?id=7547
// don't list pseudo modules __entrypoint.d, __main.d
// https://issues.dlang.org/show_bug.cgi?id=11117
// https://issues.dlang.org/show_bug.cgi?id=11164
if (global.params.moduleDeps !is null && !(imp.id == Id.object && sc._module.ident == Id.object) &&
strcmp(sc._module.ident.toChars(), "__main") != 0)
{
/* The grammar of the file is:
* ImportDeclaration
* ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
* ModuleAliasIdentifier ] "\n"
*
* BasicImportDeclaration
* ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
* " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
*
* FilePath
* - any string with '(', ')' and '\' escaped with the '\' character
*/
OutBuffer* ob = global.params.moduleDeps;
Module imod = sc._module;
if (!global.params.moduleDepsFile)
ob.writestring("depsImport ");
ob.writestring(imod.toPrettyChars());
ob.writestring(" (");
escapePath(ob, imod.srcfile.toChars());
ob.writestring(") : ");
// use visibility instead of sc.visibility because it couldn't be
// resolved yet, see the comment above
visibilityToBuffer(ob, imp.visibility);
ob.writeByte(' ');
if (imp.isstatic)
{
stcToBuffer(ob, STC.static_);
ob.writeByte(' ');
}
ob.writestring(": ");
foreach (pid; imp.packages)
{
ob.printf("%s.", pid.toChars());
}
ob.writestring(imp.id.toString());
ob.writestring(" (");
if (imp.mod)
escapePath(ob, imp.mod.srcfile.toChars());
else
ob.writestring("???");
ob.writeByte(')');
foreach (i, name; imp.names)
{
if (i == 0)
ob.writeByte(':');
else
ob.writeByte(',');
Identifier _alias = imp.aliases[i];
if (!_alias)
{
ob.printf("%s", name.toChars());
_alias = name;
}
else
ob.printf("%s=%s", _alias.toChars(), name.toChars());
}
if (imp.aliasId)
ob.printf(" -> %s", imp.aliasId.toChars());
ob.writenl();
}
//printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg);
}
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)
{
Scope* sc2 = ad.newScope(sc);
bool errors;
for (size_t i = 0; i < d.dim; i++)
{
Dsymbol s = (*d)[i];
s.dsymbolSemantic(sc2);
errors |= s.errors;
}
ad.errors |= errors;
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)
{
sc = sc.push();
sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared);
sc.inunion = scd.isunion ? scd : null;
sc.flags = 0;
for (size_t i = 0; i < scd.decl.dim; 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)
{
StringExp verifyMangleString(ref Expression e)
{
auto se = semanticString(sc, e, "mangled name");
if (!se)
return null;
e = se;
if (!se.len)
{
pd.error("zero-length string not allowed for mangled name");
return null;
}
if (se.sz != 1)
{
pd.error("mangled name characters can only be of type `char`");
return null;
}
version (all)
{
/* Note: D language specification should not have any assumption about backend
* implementation. Ideally pragma(mangle) can accept a string of any content.
*
* Therefore, this validation is compiler implementation specific.
*/
auto slice = se.peekString();
for (size_t i = 0; i < se.len;)
{
dchar c = slice[i];
if (c < 0x80)
{
if (c.isValidMangling)
{
++i;
continue;
}
else
{
pd.error("char 0x%02x not allowed in mangled name", c);
break;
}
}
if (const msg = utf_decodeChar(slice, i, c))
{
pd.error("%.*s", cast(int)msg.length, msg.ptr);
break;
}
if (!isUniAlpha(c))
{
pd.error("char `0x%04x` not allowed in mangled name", c);
break;
}
}
}
return se;
}
void declarations()
{
if (!pd.decl)
return;
Scope* sc2 = pd.newScope(sc);
scope(exit)
if (sc2 != sc)
sc2.pop();
foreach (s; (*pd.decl)[])
{
s.dsymbolSemantic(sc2);
if (pd.ident != Id.mangle)
continue;
assert(pd.args);
if (auto ad = s.isAggregateDeclaration())
{
Expression e = (*pd.args)[0];
sc2 = sc2.startCTFE();
e = e.expressionSemantic(sc);
e = resolveProperties(sc2, e);
sc2 = sc2.endCTFE();
AggregateDeclaration agg;
if (auto tc = e.type.isTypeClass())
agg = tc.sym;
else if (auto ts = e.type.isTypeStruct())
agg = ts.sym;
ad.mangleOverride = new MangleOverride;
void setString(ref Expression e)
{
if (auto se = verifyMangleString(e))
{
const name = (cast(const(char)[])se.peekData()).xarraydup;
ad.mangleOverride.id = Identifier.idPool(name);
e = se;
}
else
e.error("must be a string");
}
if (agg)
{
ad.mangleOverride.agg = agg;
if (pd.args.dim == 2)
{
setString((*pd.args)[1]);
}
else
ad.mangleOverride.id = agg.ident;
}
else
setString((*pd.args)[0]);
}
else if (auto td = s.isTemplateDeclaration())
{
pd.error("cannot apply to a template declaration");
errorSupplemental(pd.loc, "use `template Class(Args...){ pragma(mangle, \"other_name\") class Class {} }`");
}
else if (auto se = verifyMangleString((*pd.args)[0]))
{
const name = (cast(const(char)[])se.peekData()).xarraydup;
uint cnt = setMangleOverride(s, name);
if (cnt > 1)
pd.error("can only apply to a single declaration");
}
}
}
void noDeclarations()
{
if (pd.decl)
{
pd.error("is missing a terminating `;`");
declarations();
// do them anyway, to avoid segfaults.
}
}
// Should be merged with PragmaStatement
//printf("\tPragmaDeclaration::semantic '%s'\n", pd.toChars());
if (target.supportsLinkerDirective())
{
if (pd.ident == Id.linkerDirective)
{
if (!pd.args || pd.args.dim != 1)
pd.error("one string argument expected for pragma(linkerDirective)");
else
{
auto se = semanticString(sc, (*pd.args)[0], "linker directive");
if (!se)
return noDeclarations();
(*pd.args)[0] = se;
if (global.params.verbose)
message("linkopt %.*s", cast(int)se.len, se.peekString().ptr);
}
return noDeclarations();
}
}
if (pd.ident == Id.msg)
{
if (pd.args)
{
for (size_t i = 0; i < pd.args.dim; i++)
{
Expression e = (*pd.args)[i];
sc = sc.startCTFE();
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
sc = sc.endCTFE();
e = ctfeInterpretForPragmaMsg(e);
if (e.op == EXP.error)
{
errorSupplemental(pd.loc, "while evaluating `pragma(msg, %s)`", (*pd.args)[i].toChars());
return;
}
StringExp se = e.toStringExp();
if (se)
{
se = se.toUTF8(sc);
fprintf(stderr, "%.*s", cast(int)se.len, se.peekString().ptr);
}
else
fprintf(stderr, "%s", e.toChars());
}
fprintf(stderr, "\n");
}
return noDeclarations();
}
else if (pd.ident == Id.lib)
{
if (!pd.args || pd.args.dim != 1)
pd.error("string expected for library name");
else
{
auto se = semanticString(sc, (*pd.args)[0], "library name");
if (!se)
return noDeclarations();
(*pd.args)[0] = se;
auto name = se.peekString().xarraydup;
if (global.params.verbose)
message("library %s", name.ptr);
if (global.params.moduleDeps && !global.params.moduleDepsFile)
{
OutBuffer* ob = global.params.moduleDeps;
Module imod = sc._module;
ob.writestring("depsLib ");
ob.writestring(imod.toPrettyChars());
ob.writestring(" (");
escapePath(ob, imod.srcfile.toChars());
ob.writestring(") : ");
ob.writestring(name);
ob.writenl();
}
mem.xfree(name.ptr);
}
return noDeclarations();
}
else if (pd.ident == Id.startaddress)
{
if (!pd.args || pd.args.dim != 1)
pd.error("function name expected for start address");
else
{
/* https://issues.dlang.org/show_bug.cgi?id=11980
* resolveProperties and ctfeInterpret call are not necessary.
*/
Expression e = (*pd.args)[0];
sc = sc.startCTFE();
e = e.expressionSemantic(sc);
sc = sc.endCTFE();
(*pd.args)[0] = e;
Dsymbol sa = getDsymbol(e);
if (!sa || !sa.isFuncDeclaration())
pd.error("function name expected for start address, not `%s`", e.toChars());
}
return noDeclarations();
}
else if (pd.ident == Id.Pinline)
{
if (pd.args && pd.args.dim > 1)
{
pd.error("one boolean expression expected for `pragma(inline)`, not %llu", cast(ulong) pd.args.dim);
pd.args.setDim(1);
(*pd.args)[0] = ErrorExp.get();
}
// this pragma now gets evaluated on demand in function semantic
return declarations();
}
else if (pd.ident == Id.mangle)
{
if (!pd.args)
pd.args = new Expressions();
if (pd.args.dim == 0 || pd.args.dim > 2)
{
pd.error(pd.args.dim == 0 ? "string expected for mangled name"
: "expected 1 or 2 arguments");
pd.args.setDim(1);
(*pd.args)[0] = ErrorExp.get(); // error recovery
}
return declarations();
}
else if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor)
{
if (pd.args && pd.args.dim != 0)
pd.error("takes no argument");
else
{
immutable isCtor = pd.ident == Id.crt_constructor;
static uint recurse(Dsymbol s, bool isCtor)
{
if (auto ad = s.isAttribDeclaration())
{
uint nestedCount;
auto decls = ad.include(null);
if (decls)
{
for (size_t i = 0; i < decls.dim; ++i)
nestedCount += recurse((*decls)[i], isCtor);
}
return nestedCount;
}
else if (auto f = s.isFuncDeclaration())
{
f.flags |= isCtor ? FUNCFLAG.CRTCtor : FUNCFLAG.CRTDtor;
return 1;
}
else
return 0;
assert(0);
}
if (recurse(pd, isCtor) > 1)
pd.error("can only apply to a single declaration");
}
return declarations();
}
else if (pd.ident == Id.printf || pd.ident == Id.scanf)
{
if (pd.args && pd.args.dim != 0)
pd.error("takes no argument");
return declarations();
}
else if (!global.params.ignoreUnsupportedPragmas)
{
error(pd.loc, "unrecognized `pragma(%s)`", pd.ident.toChars());
return declarations();
}
if (!global.params.verbose)
return declarations();
/* Print unrecognized pragmas
*/
OutBuffer buf;
buf.writestring(pd.ident.toString());
if (pd.args)
{
const errors_save = global.startGagging();
for (size_t i = 0; i < pd.args.dim; i++)
{
Expression e = (*pd.args)[i];
sc = sc.startCTFE();
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
sc = sc.endCTFE();
e = e.ctfeInterpret();
if (i == 0)
buf.writestring(" (");
else
buf.writeByte(',');
buf.writestring(e.toChars());
}
if (pd.args.dim)
buf.writeByte(')');
global.endGagging(errors_save);
}
message("pragma %s", buf.peekChars());
return declarations();
}
override void visit(StaticIfDeclaration sid)
{
attribSemantic(sid);
}
override void visit(StaticForeachDeclaration sfd)
{
attribSemantic(sfd);
}
private Dsymbols* compileIt(CompileDeclaration cd)
{
//printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
OutBuffer buf;
if (expressionsToString(buf, sc, cd.exps))
return null;
const errors = global.errors;
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false);
p.nextToken();
auto d = p.parseDeclDefs(0);
if (global.errors != errors)
return null;
if (p.token.value != TOK.endOfFile)
{
cd.error("incomplete mixin declaration `%s`", str.ptr);
return null;
}
return d;
}
/***********************************************************
* https://dlang.org/spec/module.html#mixin-declaration
*/
override void visit(CompileDeclaration cd)
{
//printf("CompileDeclaration::semantic()\n");
if (!cd.compiled)
{
cd.decl = compileIt(cd);
cd.AttribDeclaration.addMember(sc, cd.scopesym);
cd.compiled = true;
if (cd._scope && cd.decl)
{
for (size_t i = 0; i < cd.decl.dim; 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))
{
ns.exp.error("expected valid identifier for C++ namespace but got `%.*s`",
cast(int)sident.length, sident.ptr);
return null;
}
else
return Identifier.idPool(sident);
}
if (ns.ident is null)
{
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.dim; ++d)
{
auto exp = (*te.exps)[d];
auto prev = d ? current : ns.cppnamespace;
current = (d + 1) != te.exps.dim
? 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
ns.exp.error("`%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
ns.exp.error("compile time string constant (or tuple) 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, sc, true);
return attribSemantic(uad);
}
override void visit(StaticAssert sa)
{
if (sa.semanticRun < PASS.semanticdone)
sa.semanticRun = PASS.semanticdone;
}
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;
//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); // 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)
{
//printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc.scopesym, sc.scopesym.toChars(), ed.toChars());
//printf("EnumDeclaration::semantic() %p %s\n", this, ed.toChars());
if (ed.semanticRun >= PASS.semanticdone)
return; // semantic() already completed
if (ed.semanticRun == PASS.semantic)
{
assert(ed.memtype);
error(ed.loc, "circular reference to enum base type `%s`", ed.memtype.toChars());
ed.errors = true;
ed.semanticRun = PASS.semanticdone;
return;
}
uint dprogress_save = Module.dprogress;
Scope* scx = null;
if (ed._scope)
{
sc = ed._scope;
scx = ed._scope; // save so we don't make redundant copies
ed._scope = null;
}
if (!sc)
return;
ed.parent = sc.parent;
ed.type = ed.type.typeSemantic(ed.loc, sc);
ed.visibility = sc.visibility;
if (sc.stc & STC.deprecated_)
ed.isdeprecated = true;
ed.userAttribDecl = sc.userAttribDecl;
ed.cppnamespace = sc.namespace;
ed.semanticRun = PASS.semantic;
UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage);
if (!ed.members && !ed.memtype) // enum ident;
{
ed.semanticRun = PASS.semanticdone;
return;
}
if (!ed.symtab)
ed.symtab = new DsymbolTable();
/* The separate, and distinct, cases are:
* 1. enum { ... }
* 2. enum : memtype { ... }
* 3. enum ident { ... }
* 4. enum ident : memtype { ... }
* 5. enum ident : memtype;
* 6. enum ident;
*/
if (ed.memtype)
{
ed.memtype = ed.memtype.typeSemantic(ed.loc, sc);
/* Check to see if memtype is forward referenced
*/
if (auto te = ed.memtype.isTypeEnum())
{
auto sym = te.toDsymbol(sc).isEnumDeclaration();
// Special enums like __c_[u]long[long] are fine to forward reference
// see https://issues.dlang.org/show_bug.cgi?id=20599
if (!sym.isSpecial() && (!sym.memtype || !sym.members || !sym.symtab || sym._scope))
{
// memtype is forward referenced, so try again later
deferDsymbolSemantic(ed, scx);
Module.dprogress = dprogress_save;
//printf("\tdeferring %s\n", toChars());
ed.semanticRun = PASS.initial;
return;
}
else
// Ensure that semantic is run to detect. e.g. invalid forward references
sym.dsymbolSemantic(sc);
}
if (ed.memtype.ty == Tvoid)
{
ed.error("base type must not be `void`");
ed.memtype = Type.terror;
}
if (ed.memtype.ty == Terror)
{
ed.errors = true;
// poison all the members
ed.members.foreachDsymbol( (s) { s.errors = true; } );
ed.semanticRun = PASS.semanticdone;
return;
}
}
if (!ed.members) // enum ident : memtype;
{
ed.semanticRun = PASS.semanticdone;
return;
}
if (ed.members.dim == 0)
{
ed.error("enum `%s` must have at least one member", ed.toChars());
ed.errors = true;
ed.semanticRun = PASS.semanticdone;
return;
}
if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done
ed.semanticRun = PASS.semanticdone;
Module.dprogress++;
// @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
// Deprecated in 2.100
// Make an error in 2.110
if (sc.stc & STC.scope_)
deprecation(ed.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
Scope* sce;
if (ed.isAnonymous())
sce = sc;
else
{
sce = sc.push(ed);
sce.parent = ed;
}
sce = sce.startCTFE();
sce.setNoFree(); // needed for getMaxMinValue()
/* Each enum member gets the sce scope
*/
ed.members.foreachDsymbol( (s)
{
EnumMember em = s.isEnumMember();
if (em)
em._scope = sce;
});
/* addMember() is not called when the EnumDeclaration appears as a function statement,
* so we have to do what addMember() does and install the enum members in the right symbol
* table
*/
addEnumMembers(ed, sc, sc.getScopesym());
if (sc.flags & SCOPE.Cfile)
{
/* C11 6.7.2.2
*/
assert(ed.memtype);
int nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0
void emSemantic(EnumMember em, ref int nextValue)
{
static void errorReturn(EnumMember em)
{
em.errors = true;
em.semanticRun = PASS.semanticdone;
}
em.semanticRun = PASS.semantic;
em.type = Type.tint32;
em.linkage = LINK.c;
em.storage_class |= STC.manifest;
if (em.value)
{
Expression e = em.value;
assert(e.dyncast() == DYNCAST.expression);
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
e = e.integralPromotions(sc);
e = e.ctfeInterpret();
if (e.op == EXP.error)
return errorReturn(em);
auto ie = e.isIntegerExp();
if (!ie)
{
// C11 6.7.2.2-2
em.error("enum member must be an integral constant expression, not `%s` of type `%s`", e.toChars(), e.type.toChars());
return errorReturn(em);
}
const sinteger_t v = ie.toInteger();
if (v < int.min || v > uint.max)
{
// C11 6.7.2.2-2
em.error("enum member value `%s` does not fit in an `int`", e.toChars());
return errorReturn(em);
}
em.value = new IntegerExp(em.loc, cast(int)v, Type.tint32);
nextValue = cast(int)v;
}
else
{
em.value = new IntegerExp(em.loc, nextValue, Type.tint32);
}
++nextValue; // C11 6.7.2.2-3 add 1 to value of previous enumeration constant
em.semanticRun = PASS.semanticdone;
}
ed.members.foreachDsymbol( (s)
{
if (EnumMember em = s.isEnumMember())
emSemantic(em, nextValue);
});
ed.semanticRun = PASS.semanticdone;
return;
}
ed.members.foreachDsymbol( (s)
{
if (EnumMember em = s.isEnumMember())
em.dsymbolSemantic(em._scope);
});
//printf("defaultval = %lld\n", defaultval);
//if (defaultval) printf("defaultval: %s %s\n", defaultval.toChars(), defaultval.type.toChars());
//printf("members = %s\n", members.toChars());
}
override void visit(EnumMember em)
{
//printf("EnumMember::semantic() %s\n", em.toChars());
void errorReturn()
{
em.errors = true;
em.semanticRun = PASS.semanticdone;
}
if (em.errors || em.semanticRun >= PASS.semanticdone)
return;
if (em.semanticRun == PASS.semantic)
{
em.error("circular reference to `enum` member");
return errorReturn();
}
assert(em.ed);
em.ed.dsymbolSemantic(sc);
if (em.ed.errors)
return errorReturn();
if (em.errors || em.semanticRun >= PASS.semanticdone)
return;
if (em._scope)
sc = em._scope;
if (!sc)
return;
em.semanticRun = PASS.semantic;
em.visibility = em.ed.isAnonymous() ? em.ed.visibility : Visibility(Visibility.Kind.public_);
em.linkage = LINK.d;
em.storage_class |= STC.manifest;
// https://issues.dlang.org/show_bug.cgi?id=9701
if (em.ed.isAnonymous())
{
if (em.userAttribDecl)
em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl;
else
em.userAttribDecl = em.ed.userAttribDecl;
}
// Eval UDA in this same scope. Issues 19344, 20835, 21122
if (em.userAttribDecl)
{
// Set scope but avoid extra sc.uda attachment inside setScope()
auto inneruda = em.userAttribDecl.userAttribDecl;
em.userAttribDecl.setScope(sc);
em.userAttribDecl.userAttribDecl = inneruda;
}
// The first enum member is special
bool first = (em == (*em.ed.members)[0]);
if (em.origType)
{
em.origType = em.origType.typeSemantic(em.loc, sc);
em.type = em.origType;
assert(em.value); // "type id;" is not a valid enum member declaration
}
if (em.value)
{
Expression e = em.value;
assert(e.dyncast() == DYNCAST.expression);
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
e = e.ctfeInterpret();
if (e.op == EXP.error)
return errorReturn();
if (first && !em.ed.memtype && !em.ed.isAnonymous())
{
em.ed.memtype = e.type;
if (em.ed.memtype.ty == Terror)
{
em.ed.errors = true;
return errorReturn();
}
if (em.ed.memtype.ty != Terror)
{
/* https://issues.dlang.org/show_bug.cgi?id=11746
* All of named enum members should have same type
* with the first member. If the following members were referenced
* during the first member semantic, their types should be unified.
*/
em.ed.members.foreachDsymbol( (s)
{
EnumMember enm = s.isEnumMember();
if (!enm || enm == em || enm.semanticRun < PASS.semanticdone || enm.origType)
return;
//printf("[%d] em = %s, em.semanticRun = %d\n", i, toChars(), em.semanticRun);
Expression ev = enm.value;
ev = ev.implicitCastTo(sc, em.ed.memtype);
ev = ev.ctfeInterpret();
ev = ev.castTo(sc, em.ed.type);
if (ev.op == EXP.error)
em.ed.errors = true;
enm.value = ev;
});
if (em.ed.errors)
{
em.ed.memtype = Type.terror;
return errorReturn();
}
}
}
if (em.ed.memtype && !em.origType)
{
e = e.implicitCastTo(sc, em.ed.memtype);
e = e.ctfeInterpret();
// save origValue for better json output
em.origValue = e;
if (!em.ed.isAnonymous())
{
e = e.castTo(sc, em.ed.type.addMod(e.type.mod)); // https://issues.dlang.org/show_bug.cgi?id=12385
e = e.ctfeInterpret();
}
}
else if (em.origType)
{
e = e.implicitCastTo(sc, em.origType);
e = e.ctfeInterpret();
assert(em.ed.isAnonymous());
// save origValue for better json output
em.origValue = e;
}
em.value = e;
}
else if (first)
{
Type t;
if (em.ed.memtype)
t = em.ed.memtype;
else
{
t = Type.tint32;
if (!em.ed.isAnonymous())
em.ed.memtype = t;
}
Expression e = new IntegerExp(em.loc, 0, t);
e = e.ctfeInterpret();
// save origValue for better json output
em.origValue = e;
if (!em.ed.isAnonymous())
{
e = e.castTo(sc, em.ed.type);
e = e.ctfeInterpret();
}
em.value = e;
}
else
{
/* Find the previous enum member,
* and set this to be the previous value + 1
*/
EnumMember emprev = null;
em.ed.members.foreachDsymbol( (s)
{
if (auto enm = s.isEnumMember())
{
if (enm == em)
return 1; // found
emprev = enm;
}
return 0; // continue
});
assert(emprev);
if (emprev.semanticRun < PASS.semanticdone) // if forward reference
emprev.dsymbolSemantic(emprev._scope); // resolve it
if (emprev.errors)
return errorReturn();
Expression eprev = emprev.value;
// .toHeadMutable() due to https://issues.dlang.org/show_bug.cgi?id=18645
Type tprev = eprev.type.toHeadMutable().equals(em.ed.type.toHeadMutable())
? em.ed.memtype
: eprev.type;
/*
https://issues.dlang.org/show_bug.cgi?id=20777
Previously this used getProperty, which doesn't consider anything user defined,
this construct does do that and thus fixes the bug.
*/
Expression emax = DotIdExp.create(em.ed.loc, new TypeExp(em.ed.loc, tprev), Id.max);
emax = emax.expressionSemantic(sc);
emax = emax.ctfeInterpret();
// Set value to (eprev + 1).
// But first check that (eprev != emax)
assert(eprev);
Expression e = new EqualExp(EXP.equal, em.loc, eprev, emax);
e = e.expressionSemantic(sc);
e = e.ctfeInterpret();
if (e.toInteger())
{
em.error("initialization with `%s.%s+1` causes overflow for type `%s`",
emprev.ed.toChars(), emprev.toChars(), em.ed.memtype.toChars());
return errorReturn();
}
// Now set e to (eprev + 1)
e = new AddExp(em.loc, eprev, IntegerExp.literal!1);
e = e.expressionSemantic(sc);
e = e.castTo(sc, eprev.type);
e = e.ctfeInterpret();
// save origValue (without cast) for better json output
if (e.op != EXP.error) // avoid duplicate diagnostics
{
assert(emprev.origValue);
em.origValue = new AddExp(em.loc, emprev.origValue, IntegerExp.literal!1);
em.origValue = em.origValue.expressionSemantic(sc);
em.origValue = em.origValue.ctfeInterpret();
}
if (e.op == EXP.error)
return errorReturn();
if (e.type.isfloating())
{
// Check that e != eprev (not always true for floats)
Expression etest = new EqualExp(EXP.equal, em.loc, e, eprev);
etest = etest.expressionSemantic(sc);
etest = etest.ctfeInterpret();
if (etest.toInteger())
{
em.error("has inexact value due to loss of precision");
return errorReturn();
}
}
em.value = e;
}
if (!em.origType)
em.type = em.value.type;
assert(em.origValue);
em.semanticRun = PASS.semanticdone;
}
override void visit(TemplateDeclaration tempdecl)
{
static if (LOG)
{
printf("TemplateDeclaration.dsymbolSemantic(this = %p, id = '%s')\n", this, tempdecl.ident.toChars());
printf("sc.stc = %llx\n", sc.stc);
printf("sc.module = %s\n", sc._module.toChars());
}
if (tempdecl.semanticRun != PASS.initial)
return; // semantic() already run
if (tempdecl._scope)
{
sc = tempdecl._scope;
tempdecl._scope = null;
}
if (!sc)
return;
// Remember templates defined in module object that we need to know about
if (sc._module && sc._module.ident == Id.object)
{
if (tempdecl.ident == Id.RTInfo)
Type.rtinfo = tempdecl;
}
/* Remember Scope for later instantiations, but make
* a copy since attributes can change.
*/
if (!tempdecl._scope)
{
tempdecl._scope = sc.copy();
tempdecl._scope.setNoFree();
}
tempdecl.semanticRun = PASS.semantic;
tempdecl.parent = sc.parent;
tempdecl.visibility = sc.visibility;
tempdecl.userAttribDecl = sc.userAttribDecl;
tempdecl.cppnamespace = sc.namespace;
tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
UserAttributeDeclaration.checkGNUABITag(tempdecl, sc.linkage);
if (!tempdecl.isstatic)
{
if (auto ad = tempdecl.parent.pastMixin().isAggregateDeclaration())
ad.makeNested();
}
// Set up scope for parameters
auto paramsym = new ScopeDsymbol();
paramsym.parent = tempdecl.parent;
Scope* paramscope = sc.push(paramsym);
paramscope.stc = 0;
if (global.params.doDocComments)
{
tempdecl.origParameters = new TemplateParameters(tempdecl.parameters.dim);
for (size_t i = 0; i < tempdecl.parameters.dim; i++)
{
TemplateParameter tp = (*tempdecl.parameters)[i];
(*tempdecl.origParameters)[i] = tp.syntaxCopy();
}
}
for (size_t i = 0; i < tempdecl.parameters.dim; i++)
{
TemplateParameter tp = (*tempdecl.parameters)[i];
if (!tp.declareParameter(paramscope))
{
error(tp.loc, "parameter `%s` multiply defined", tp.ident.toChars());
tempdecl.errors = true;
}
if (!tp.tpsemantic(paramscope, tempdecl.parameters))
{
tempdecl.errors = true;
}
if (i + 1 != tempdecl.parameters.dim && tp.isTemplateTupleParameter())
{
tempdecl.error("template tuple parameter must be last one");
tempdecl.errors = true;
}
}
/* Calculate TemplateParameter.dependent
*/
TemplateParameters tparams = TemplateParameters(1);
for (size_t i = 0; i < tempdecl.parameters.dim; i++)
{
TemplateParameter tp = (*tempdecl.parameters)[i];
tparams[0] = tp;
for (size_t j = 0; j < tempdecl.parameters.dim; j++)
{
// Skip cases like: X(T : T)
if (i == j)
continue;
if (TemplateTypeParameter ttp = (*tempdecl.parameters)[j].isTemplateTypeParameter())
{
if (reliesOnTident(ttp.specType, &tparams))
tp.dependent = true;
}
else if (TemplateAliasParameter tap = (*tempdecl.parameters)[j].isTemplateAliasParameter())
{
if (reliesOnTident(tap.specType, &tparams) ||
reliesOnTident(isType(tap.specAlias), &tparams))
{
tp.dependent = true;
}
}
}
}
paramscope.pop();
// Compute again
tempdecl.onemember = null;
if (tempdecl.members)
{
Dsymbol s;
if (Dsymbol.oneMembers(tempdecl.members, &s, tempdecl.ident) && s)
{
tempdecl.onemember = s;
s.parent = tempdecl;
}
}
/* BUG: should check:
* 1. template functions must not introduce virtual functions, as they
* cannot be accomodated in the vtbl[]
* 2. templates cannot introduce non-static data members (i.e. fields)
* as they would change the instance size of the aggregate.
*/
tempdecl.semanticRun = PASS.semanticdone;
}
override void visit(TemplateInstance ti)
{
templateInstanceSemantic(ti, sc, null);
}
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, null))
{
if (tm.semanticRun == PASS.initial) // forward reference had occurred
{
//printf("forward reference - deferring\n");
return deferDsymbolSemantic(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.dim != tmix.tiargs.dim)
continue;
for (size_t i = 0; i < tm.tiargs.dim; 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);
}
tm.error("recursive mixin instantiation");
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);
uint 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.dim;
__gshared int nest;
//printf("%d\n", nest);
if (++nest > global.recursionLimit)
{
global.gag = 0; // ensure error message gets printed
tm.error("recursive expansion");
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.dim > deferred_dim) {}
AggregateDeclaration ad = tm.toParent().isAggregateDeclaration();
if (sc.func && !ad)
{
tm.semantic2(sc2);
tm.semantic3(sc2);
}
// Give additional context info if error occurred during instantiation
if (global.errors != errorsave)
{
tm.error("error instantiating");
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());
}
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.toTupleExp();
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
UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp);
if (ns.members)
{
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;
static if (LOG)
{
printf("-Nspace::semantic('%s')\n", ns.toChars());
}
}
void funcDeclarationSemantic(FuncDeclaration funcdecl)
{
version (none)
{
printf("FuncDeclaration::semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, funcdecl, funcdecl.toPrettyChars(), sc.linkage);
if (funcdecl.isFuncLiteralDeclaration())
printf("\tFuncLiteralDeclaration()\n");
printf("sc.parent = %s, parent = %s\n", sc.parent.toChars(), funcdecl.parent ? funcdecl.parent.toChars() : "");
printf("type: %p, %s\n", funcdecl.type, funcdecl.type.toChars());
}
if (funcdecl.semanticRun != PASS.initial && funcdecl.isFuncLiteralDeclaration())
{
/* Member functions that have return types that are
* forward references can have semantic() run more than
* once on them.
* See test\interface2.d, test20
*/
return;
}
if (funcdecl.semanticRun >= PASS.semanticdone)
return;
assert(funcdecl.semanticRun <= PASS.semantic);
funcdecl.semanticRun = PASS.semantic;
if (funcdecl._scope)
{
sc = funcdecl._scope;
funcdecl._scope = null;
}
if (!sc || funcdecl.errors)
return;
funcdecl.cppnamespace = sc.namespace;
funcdecl.parent = sc.parent;
Dsymbol parent = funcdecl.toParent();
funcdecl.foverrides.setDim(0); // reset in case semantic() is being retried for this function
funcdecl.storage_class |= sc.stc & ~STC.ref_;
AggregateDeclaration ad = funcdecl.isThis();
// Don't nest structs b/c of generated methods which should not access the outer scopes.
// https://issues.dlang.org/show_bug.cgi?id=16627
if (ad && !funcdecl.isGenerated())
{
funcdecl.storage_class |= ad.storage_class & (STC.TYPECTOR | STC.synchronized_);
ad.makeNested();
}
if (sc.func)
funcdecl.storage_class |= sc.func.storage_class & STC.disable;
// Remove prefix storage classes silently.
if ((funcdecl.storage_class & STC.TYPECTOR) && !(ad || funcdecl.isNested()))
funcdecl.storage_class &= ~STC.TYPECTOR;
//printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration::isFinal());
if (sc.flags & SCOPE.compile)
funcdecl.flags |= FUNCFLAG.compileTimeOnly; // don't emit code for this function
funcdecl.linkage = sc.linkage;
if (auto fld = funcdecl.isFuncLiteralDeclaration())
{
if (fld.treq)
{
Type treq = fld.treq;
assert(treq.nextOf().ty == Tfunction);
if (treq.ty == Tdelegate)
fld.tok = TOK.delegate_;
else if (treq.isPtrToFunction())
fld.tok = TOK.function_;
else
assert(0);
funcdecl.linkage = treq.nextOf().toTypeFunction().linkage;
}
}
// evaluate pragma(inline)
if (auto pragmadecl = sc.inlining)
funcdecl.inlining = pragmadecl.evalPragmaInline(sc);
// check pragma(crt_constructor)
if (funcdecl.flags & (FUNCFLAG.CRTCtor | FUNCFLAG.CRTDtor))
{
if (funcdecl.linkage != LINK.c)
{
funcdecl.error("must be `extern(C)` for `pragma(%s)`",
(funcdecl.flags & FUNCFLAG.CRTCtor) ? "crt_constructor".ptr : "crt_destructor".ptr);
}
}
funcdecl.visibility = sc.visibility;
funcdecl.userAttribDecl = sc.userAttribDecl;
UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl.linkage);
if (!funcdecl.originalType)
funcdecl.originalType = funcdecl.type.syntaxCopy();
static TypeFunction getFunctionType(FuncDeclaration fd)
{
if (auto tf = fd.type.isTypeFunction())
return tf;
if (!fd.type.isTypeError())
{
fd.error("`%s` must be a function instead of `%s`", fd.toChars(), fd.type.toChars());
fd.type = Type.terror;
}
fd.errors = true;
return null;
}
if (sc.flags & SCOPE.Cfile)
{
/* C11 allows a function to be declared with a typedef, D does not.
*/
if (auto ti = funcdecl.type.isTypeIdentifier())
{
auto tj = ti.typeSemantic(funcdecl.loc, sc);
if (auto tjf = tj.isTypeFunction())
{
/* Copy the type instead of just pointing to it,
* as we don't merge function types
*/
auto tjf2 = new TypeFunction(tjf.parameterList, tjf.next, tjf.linkage);
funcdecl.type = tjf2;
funcdecl.originalType = tjf2;
}
}
}
if (!getFunctionType(funcdecl))
return;
if (!funcdecl.type.deco)
{
sc = sc.push();
sc.stc |= funcdecl.storage_class & (STC.disable | STC.deprecated_); // forward to function type
TypeFunction tf = funcdecl.type.toTypeFunction();
if (sc.func)
{
/* If the nesting parent is pure without inference,
* then this function defaults to pure too.
*
* auto foo() pure {
* auto bar() {} // become a weak purity function
* class C { // nested class
* auto baz() {} // become a weak purity function
* }
*
* static auto boo() {} // typed as impure
* // Even though, boo cannot call any impure functions.
* // See also Expression::checkPurity().
* }
*/
if (tf.purity == PURE.impure && (funcdecl.isNested() || funcdecl.isThis()))
{
FuncDeclaration fd = null;
for (Dsymbol p = funcdecl.toParent2(); p; p = p.toParent2())
{
if (AggregateDeclaration adx = p.isAggregateDeclaration())
{
if (adx.isNested())
continue;
break;
}
if ((fd = p.isFuncDeclaration()) !is null)
break;
}
/* If the parent's purity is inferred, then this function's purity needs
* to be inferred first.
*/
if (fd && fd.isPureBypassingInference() >= PURE.weak && !funcdecl.isInstantiated())
{
tf.purity = PURE.fwdref; // default to pure
}
}
}
if (tf.isref)
sc.stc |= STC.ref_;
if (tf.isScopeQual)
sc.stc |= STC.scope_;
if (tf.isnothrow)
sc.stc |= STC.nothrow_;
if (tf.isnogc)
sc.stc |= STC.nogc;
if (tf.isproperty)
sc.stc |= STC.property;
if (tf.purity == PURE.fwdref)
sc.stc |= STC.pure_;
if (tf.trust != TRUST.default_)
{
sc.stc &= ~STC.safeGroup;
if (tf.trust == TRUST.safe)
sc.stc |= STC.safe;
else if (tf.trust == TRUST.system)
sc.stc |= STC.system;
else if (tf.trust == TRUST.trusted)
sc.stc |= STC.trusted;
}
if (funcdecl.isCtorDeclaration())
{
tf.isctor = true;
Type tret = ad.handleType();
assert(tret);
tret = tret.addStorageClass(funcdecl.storage_class | sc.stc);
tret = tret.addMod(funcdecl.type.mod);
tf.next = tret;
if (ad.isStructDeclaration())
sc.stc |= STC.ref_;
}
// 'return' on a non-static class member function implies 'scope' as well
if (ad && ad.isClassDeclaration() && (tf.isreturn || sc.stc & STC.return_) && !(sc.stc & STC.static_))
sc.stc |= STC.scope_;
// If 'this' has no pointers, remove 'scope' as it has no meaning
if (sc.stc & STC.scope_ && ad && ad.isStructDeclaration() && !ad.type.hasPointers())
{
sc.stc &= ~STC.scope_;
tf.isScopeQual = false;
}
sc.linkage = funcdecl.linkage;
if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested()))
{
OutBuffer buf;
MODtoBuffer(&buf, tf.mod);
funcdecl.error("without `this` cannot be `%s`", buf.peekChars());
tf.mod = 0; // remove qualifiers
}
/* Apply const, immutable, wild and shared storage class
* to the function type. Do this before type semantic.
*/
auto stc = funcdecl.storage_class;
if (funcdecl.type.isImmutable())
stc |= STC.immutable_;
if (funcdecl.type.isConst())
stc |= STC.const_;
if (funcdecl.type.isShared() || funcdecl.storage_class & STC.synchronized_)
stc |= STC.shared_;
if (funcdecl.type.isWild())
stc |= STC.wild;
funcdecl.type = funcdecl.type.addSTC(stc);
funcdecl.type = funcdecl.type.typeSemantic(funcdecl.loc, sc);
sc = sc.pop();
}
auto f = getFunctionType(funcdecl);
if (!f)
return; // funcdecl's type is not a function
{
// Merge back function attributes into 'originalType'.
// It's used for mangling, ddoc, and json output.
TypeFunction tfo = funcdecl.originalType.toTypeFunction();
tfo.mod = f.mod;
tfo.isScopeQual = f.isScopeQual;
tfo.isreturninferred = f.isreturninferred;
tfo.isscopeinferred = f.isscopeinferred;
tfo.isref = f.isref;
tfo.isnothrow = f.isnothrow;
tfo.isnogc = f.isnogc;
tfo.isproperty = f.isproperty;
tfo.purity = f.purity;
tfo.trust = f.trust;
funcdecl.storage_class &= ~(STC.TYPECTOR | STC.FUNCATTR);
}
if (funcdecl.overnext && funcdecl.isCsymbol())
{
/* C does not allow function overloading, but it does allow
* redeclarations of the same function. If .overnext points
* to a redeclaration, ok. Error if it is an overload.
*/
auto fnext = funcdecl.overnext.isFuncDeclaration();
funcDeclarationSemantic(fnext);
auto fn = fnext.type.isTypeFunction();
if (!fn || !cFuncEquivalence(f, fn))
{
funcdecl.error("redeclaration with different type");
//printf("t1: %s\n", f.toChars());
//printf("t2: %s\n", fn.toChars());
}
funcdecl.overnext = null; // don't overload the redeclarations
}
if ((funcdecl.storage_class & STC.auto_) && !f.isref && !funcdecl.inferRetType)
funcdecl.error("storage class `auto` has no effect if return type is not inferred");
/* Functions can only be 'scope' if they have a 'this'
*/
if (f.isScopeQual && !funcdecl.isNested() && !ad)
{
funcdecl.error("functions cannot be `scope`");
}
if (f.isreturn && !funcdecl.needThis() && !funcdecl.isNested())
{
/* Non-static nested functions have a hidden 'this' pointer to which
* the 'return' applies
*/
if (sc.scopesym && sc.scopesym.isAggregateDeclaration())
funcdecl.error("`static` member has no `this` to which `return` can apply");
else
error(funcdecl.loc, "top-level function `%s` has no `this` to which `return` can apply", funcdecl.toChars());
}
if (funcdecl.isAbstract() && !funcdecl.isVirtual())
{
const(char)* sfunc;
if (funcdecl.isStatic())
sfunc = "static";
else if (funcdecl.visibility.kind == Visibility.Kind.private_ || funcdecl.visibility.kind == Visibility.Kind.package_)
sfunc = visibilityToChars(funcdecl.visibility.kind);
else
sfunc = "final";
funcdecl.error("`%s` functions cannot be `abstract`", sfunc);
}
if (funcdecl.isOverride() && !funcdecl.isVirtual() && !funcdecl.isFuncLiteralDeclaration())
{
Visibility.Kind kind = funcdecl.visible().kind;
if ((kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_) && funcdecl.isMember())
funcdecl.error("`%s` method is not virtual and cannot override", visibilityToChars(kind));
else
funcdecl.error("cannot override a non-virtual function");
}
if (funcdecl.isAbstract() && funcdecl.isFinalFunc())
funcdecl.error("cannot be both `final` and `abstract`");
version (none)
{
if (funcdecl.isAbstract() && funcdecl.fbody)
funcdecl.error("`abstract` functions cannot have bodies");
}
version (none)
{
if (funcdecl.isStaticConstructor() || funcdecl.isStaticDestructor())
{
if (!funcdecl.isStatic() || funcdecl.type.nextOf().ty != Tvoid)
funcdecl.error("static constructors / destructors must be `static void`");
if (f.arguments && f.arguments.dim)
funcdecl.error("static constructors / destructors must have empty parameter list");
// BUG: check for invalid storage classes
}
}
if (const pors = sc.flags & (SCOPE.printf | SCOPE.scanf))
{
/* printf/scanf-like functions must be of the form:
* extern (C/C++) T printf([parameters...], const(char)* format, ...);
* or:
* extern (C/C++) T vprintf([parameters...], const(char)* format, va_list);
*/
static bool isPointerToChar(Parameter p)
{
if (auto tptr = p.type.isTypePointer())
{
return tptr.next.ty == Tchar;
}
return false;
}
bool isVa_list(Parameter p)
{
return p.type.equals(target.va_listType(funcdecl.loc, sc));
}
const nparams = f.parameterList.length;
if ((f.linkage == LINK.c || f.linkage == LINK.cpp) &&
(f.parameterList.varargs == VarArg.variadic &&
nparams >= 1 &&
isPointerToChar(f.parameterList[nparams - 1]) ||
f.parameterList.varargs == VarArg.none &&
nparams >= 2 &&
isPointerToChar(f.parameterList[nparams - 2]) &&
isVa_list(f.parameterList[nparams - 1])
)
)
{
funcdecl.flags |= (pors == SCOPE.printf) ? FUNCFLAG.printf : FUNCFLAG.scanf;
}
else
{
const p = (pors == SCOPE.printf ? Id.printf : Id.scanf).toChars();
if (f.parameterList.varargs == VarArg.variadic)
{
funcdecl.error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, ...)`"
~ " not `%s`",
p, f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars());
}
else
{
funcdecl.error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, va_list)`",
p, f.next.toChars(), funcdecl.toChars());
}
}
}
if (auto id = parent.isInterfaceDeclaration())
{
funcdecl.storage_class |= STC.abstract_;
if (funcdecl.isCtorDeclaration() || funcdecl.isPostBlitDeclaration() || funcdecl.isDtorDeclaration() || funcdecl.isInvariantDeclaration() || funcdecl.isNewDeclaration() || funcdecl.isDelete())
funcdecl.error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface `%s`", id.toChars());
if (funcdecl.fbody && funcdecl.isVirtual())
funcdecl.error("function body only allowed in `final` functions in interface `%s`", id.toChars());
}
if (UnionDeclaration ud = parent.isUnionDeclaration())
{
if (funcdecl.isPostBlitDeclaration() || funcdecl.isDtorDeclaration() || funcdecl.isInvariantDeclaration())
funcdecl.error("destructors, postblits and invariants are not allowed in union `%s`", ud.toChars());
}
if (StructDeclaration sd = parent.isStructDeclaration())
{
if (funcdecl.isCtorDeclaration())
{
goto Ldone;
}
}
if (ClassDeclaration cd = parent.isClassDeclaration())
{
parent = cd = objc.getParent(funcdecl, cd);
if (funcdecl.isCtorDeclaration())
{
goto Ldone;
}
if (funcdecl.storage_class & STC.abstract_)
cd.isabstract = ThreeState.yes;
// if static function, do not put in vtbl[]
if (!funcdecl.isVirtual())
{
//printf("\tnot virtual\n");
goto Ldone;
}
// Suppress further errors if the return type is an error
if (funcdecl.type.nextOf() == Type.terror)
goto Ldone;
bool may_override = false;
for (size_t i = 0; i < cd.baseclasses.dim; i++)
{
BaseClass* b = (*cd.baseclasses)[i];
ClassDeclaration cbd = b.type.toBasetype().isClassHandle();
if (!cbd)
continue;
for (size_t j = 0; j < cbd.vtbl.dim; j++)
{
FuncDeclaration f2 = cbd.vtbl[j].isFuncDeclaration();
if (!f2 || f2.ident != funcdecl.ident)
continue;
if (cbd.parent && cbd.parent.isTemplateInstance())
{
if (!f2.functionSemantic())
goto Ldone;
}
may_override = true;
}
}
if (may_override && funcdecl.type.nextOf() is null)
{
/* If same name function exists in base class but 'this' is auto return,
* cannot find index of base class's vtbl[] to override.
*/
funcdecl.error("return type inference is not supported if may override base class function");
}
/* Find index of existing function in base class's vtbl[] to override
* (the index will be the same as in cd's current vtbl[])
*/
int vi = cd.baseClass ? funcdecl.findVtblIndex(&cd.baseClass.vtbl, cast(int)cd.baseClass.vtbl.dim) : -1;
bool doesoverride = false;
switch (vi)
{
case -1:
Lintro:
/* Didn't find one, so
* This is an 'introducing' function which gets a new
* slot in the vtbl[].
*/
// Verify this doesn't override previous final function
if (cd.baseClass)
{
Dsymbol s = cd.baseClass.search(funcdecl.loc, funcdecl.ident);
if (s)
{
if (auto f2 = s.isFuncDeclaration())
{
f2 = f2.overloadExactMatch(funcdecl.type);
if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_)
funcdecl.error("cannot override `final` function `%s`", f2.toPrettyChars());
}
}
}
/* These quirky conditions mimic what happens when virtual
inheritance is implemented by producing a virtual base table
with offsets to each of the virtual bases.
*/
if (target.cpp.splitVBasetable && cd.classKind == ClassKind.cpp &&
cd.baseClass && cd.baseClass.vtbl.dim)
{
/* if overriding an interface function, then this is not
* introducing and don't put it in the class vtbl[]
*/
funcdecl.interfaceVirtual = funcdecl.overrideInterface();
if (funcdecl.interfaceVirtual)
{
//printf("\tinterface function %s\n", toChars());
cd.vtblFinal.push(funcdecl);
goto Linterfaces;
}
}
if (funcdecl.isFinalFunc())
{
// Don't check here, as it may override an interface function
//if (isOverride())
// error("is marked as override, but does not override any function");
cd.vtblFinal.push(funcdecl);
}
else
{
//printf("\tintroducing function %s\n", funcdecl.toChars());
funcdecl.flags |= FUNCFLAG.introducing;
if (cd.classKind == ClassKind.cpp && target.cpp.reverseOverloads)
{
/* Overloaded functions with same name are grouped and in reverse order.
* Search for first function of overload group, and insert
* funcdecl into vtbl[] immediately before it.
*/
funcdecl.vtblIndex = cast(int)cd.vtbl.dim;
bool found;
foreach (const i, s; cd.vtbl)
{
if (found)
// the rest get shifted forward
++s.isFuncDeclaration().vtblIndex;
else if (s.ident == funcdecl.ident && s.parent == parent)
{
// found first function of overload group
funcdecl.vtblIndex = cast(int)i;
found = true;
++s.isFuncDeclaration().vtblIndex;
}
}
cd.vtbl.insert(funcdecl.vtblIndex, funcdecl);
debug foreach (const i, s; cd.vtbl)
{
// a C++ dtor gets its vtblIndex later (and might even be added twice to the vtbl),
// e.g. when compiling druntime with a debug compiler, namely with core.stdcpp.exception.
if (auto fd = s.isFuncDeclaration())
assert(fd.vtblIndex == i ||
(cd.classKind == ClassKind.cpp && fd.isDtorDeclaration) ||
funcdecl.parent.isInterfaceDeclaration); // interface functions can be in multiple vtbls
}
}
else
{
// Append to end of vtbl[]
vi = cast(int)cd.vtbl.dim;
cd.vtbl.push(funcdecl);
funcdecl.vtblIndex = vi;
}
}
break;
case -2:
// can't determine because of forward references
funcdecl.errors = true;
return;
default:
{
if (vi >= cd.vtbl.length)
{
/* the derived class cd doesn't have its vtbl[] allocated yet.
* https://issues.dlang.org/show_bug.cgi?id=21008
*/
funcdecl.error("circular reference to class `%s`", cd.toChars());
funcdecl.errors = true;
return;
}
FuncDeclaration fdv = cd.baseClass.vtbl[vi].isFuncDeclaration();
FuncDeclaration fdc = cd.vtbl[vi].isFuncDeclaration();
// This function is covariant with fdv
if (fdc == funcdecl)
{
doesoverride = true;
break;
}
if (fdc.toParent() == parent)
{
//printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n",
// vi, this, this.toChars(), this.type.toChars(), this.loc.toChars(),
// fdc, fdc .toChars(), fdc .type.toChars(), fdc .loc.toChars(),
// fdv, fdv .toChars(), fdv .type.toChars(), fdv .loc.toChars());
// fdc overrides fdv exactly, then this introduces new function.
if (fdc.type.mod == fdv.type.mod && funcdecl.type.mod != fdv.type.mod)
goto Lintro;
}
if (fdv.isDeprecated && !funcdecl.isDeprecated)
deprecation(funcdecl.loc, "`%s` is overriding the deprecated method `%s`",
funcdecl.toPrettyChars, fdv.toPrettyChars);
// This function overrides fdv
if (fdv.isFinalFunc())
funcdecl.error("cannot override `final` function `%s`", fdv.toPrettyChars());
if (!funcdecl.isOverride())
{
if (fdv.isFuture())
{
deprecation(funcdecl.loc, "`@__future` base class method `%s` is being overridden by `%s`; rename the latter", fdv.toPrettyChars(), funcdecl.toPrettyChars());
// Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[]
goto Lintro;
}
else
{
// https://issues.dlang.org/show_bug.cgi?id=17349
error(funcdecl.loc, "cannot implicitly override base class method `%s` with `%s`; add `override` attribute",
fdv.toPrettyChars(), funcdecl.toPrettyChars());
}
}
doesoverride = true;
if (fdc.toParent() == parent)
{
// If both are mixins, or both are not, then error.
// If either is not, the one that is not overrides the other.
bool thismixin = funcdecl.parent.isClassDeclaration() !is null;
bool fdcmixin = fdc.parent.isClassDeclaration() !is null;
if (thismixin == fdcmixin)
{
funcdecl.error("multiple overrides of same function");
}
/*
* https://issues.dlang.org/show_bug.cgi?id=711
*
* If an overriding method is introduced through a mixin,
* we need to update the vtbl so that both methods are
* present.
*/
else if (thismixin)
{
/* if the mixin introduced the overriding method, then reintroduce it
* in the vtbl. The initial entry for the mixined method
* will be updated at the end of the enclosing `if` block
* to point to the current (non-mixined) function.
*/
auto vitmp = cast(int)cd.vtbl.dim;
cd.vtbl.push(fdc);
fdc.vtblIndex = vitmp;
}
else if (fdcmixin)
{
/* if the current overriding function is coming from a
* mixined block, then push the current function in the
* vtbl, but keep the previous (non-mixined) function as
* the overriding one.
*/
auto vitmp = cast(int)cd.vtbl.dim;
cd.vtbl.push(funcdecl);
funcdecl.vtblIndex = vitmp;
break;
}
else // fdc overrides fdv
{
// this doesn't override any function
break;
}
}
cd.vtbl[vi] = funcdecl;
funcdecl.vtblIndex = vi;
/* Remember which functions this overrides
*/
funcdecl.foverrides.push(fdv);
/* This works by whenever this function is called,
* it actually returns tintro, which gets dynamically
* cast to type. But we know that tintro is a base
* of type, so we could optimize it by not doing a
* dynamic cast, but just subtracting the isBaseOf()
* offset if the value is != null.
*/
if (fdv.tintro)
funcdecl.tintro = fdv.tintro;
else if (!funcdecl.type.equals(fdv.type))
{
auto tnext = funcdecl.type.nextOf();
if (auto handle = tnext.isClassHandle())
{
if (handle.semanticRun < PASS.semanticdone && !handle.isBaseInfoComplete())
handle.dsymbolSemantic(null);
}
/* Only need to have a tintro if the vptr
* offsets differ
*/
int offset;
if (fdv.type.nextOf().isBaseOf(tnext, &offset))
{
funcdecl.tintro = fdv.type;
}
}
break;
}
}
/* Go through all the interface bases.
* If this function is covariant with any members of those interface
* functions, set the tintro.
*/
Linterfaces:
bool foundVtblMatch = false;
for (ClassDeclaration bcd = cd; !foundVtblMatch && bcd; bcd = bcd.baseClass)
{
foreach (b; bcd.interfaces)
{
vi = funcdecl.findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.dim);
switch (vi)
{
case -1:
break;
case -2:
// can't determine because of forward references
funcdecl.errors = true;
return;
default:
{
auto fdv = cast(FuncDeclaration)b.sym.vtbl[vi];
Type ti = null;
foundVtblMatch = true;
/* Remember which functions this overrides
*/
funcdecl.foverrides.push(fdv);
/* Should we really require 'override' when implementing
* an interface function?
*/
//if (!isOverride())
// warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv.toPrettyChars());
if (fdv.tintro)
ti = fdv.tintro;
else if (!funcdecl.type.equals(fdv.type))
{
/* Only need to have a tintro if the vptr
* offsets differ
*/
int offset;
if (fdv.type.nextOf().isBaseOf(funcdecl.type.nextOf(), &offset))
{
ti = fdv.type;
}
}
if (ti)
{
if (funcdecl.tintro)
{
if (!funcdecl.tintro.nextOf().equals(ti.nextOf()) && !funcdecl.tintro.nextOf().isBaseOf(ti.nextOf(), null) && !ti.nextOf().isBaseOf(funcdecl.tintro.nextOf(), null))
{
funcdecl.error("incompatible covariant types `%s` and `%s`", funcdecl.tintro.toChars(), ti.toChars());
}
}
else
{
funcdecl.tintro = ti;
}
}
}
}
}
}
if (foundVtblMatch)
{
goto L2;
}
if (!doesoverride && funcdecl.isOverride() && (funcdecl.type.nextOf() || !may_override))
{
BaseClass* bc = null;
Dsymbol s = null;
for (size_t i = 0; i < cd.baseclasses.dim; i++)
{
bc = (*cd.baseclasses)[i];
s = bc.sym.search_correct(funcdecl.ident);
if (s)
break;
}
if (s)
{
HdrGenState hgs;
OutBuffer buf;
auto fd = s.isFuncDeclaration();
functionToBufferFull(cast(TypeFunction)(funcdecl.type), &buf,
new Identifier(funcdecl.toPrettyChars()), &hgs, null);
const(char)* funcdeclToChars = buf.peekChars();
if (fd)
{
OutBuffer buf1;
if (fd.ident == funcdecl.ident)
hgs.fullQual = true;
functionToBufferFull(cast(TypeFunction)(fd.type), &buf1,
new Identifier(fd.toPrettyChars()), &hgs, null);
error(funcdecl.loc, "function `%s` does not override any function, did you mean to override `%s`?",
funcdeclToChars, buf1.peekChars());
}
else
{
error(funcdecl.loc, "function `%s` does not override any function, did you mean to override %s `%s`?",
funcdeclToChars, s.kind, s.toPrettyChars());
errorSupplemental(funcdecl.loc, "Functions are the only declarations that may be overriden");
}
}
else
funcdecl.error("does not override any function");
}
L2:
objc.setSelector(funcdecl, sc);
objc.checkLinkage(funcdecl);
objc.addToClassMethodList(funcdecl, cd);
objc.setAsOptional(funcdecl, sc);
/* Go through all the interface bases.
* Disallow overriding any final functions in the interface(s).
*/
foreach (b; cd.interfaces)
{
if (b.sym)
{
if (auto s = search_function(b.sym, funcdecl.ident))
{
if (auto f2 = s.isFuncDeclaration())
{
f2 = f2.overloadExactMatch(funcdecl.type);
if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_)
funcdecl.error("cannot override `final` function `%s.%s`", b.sym.toChars(), f2.toPrettyChars());
}
}
}
}
if (funcdecl.isOverride)
{
if (funcdecl.storage_class & STC.disable)
deprecation(funcdecl.loc,
"`%s` cannot be annotated with `@disable` because it is overriding a function in the base class",
funcdecl.toPrettyChars);
if (funcdecl.isDeprecated && !(funcdecl.foverrides.length && funcdecl.foverrides[0].isDeprecated))
deprecation(funcdecl.loc,
"`%s` cannot be marked as `deprecated` because it is overriding a function in the base class",
funcdecl.toPrettyChars);
}
}
else if (funcdecl.isOverride() && !parent.isTemplateInstance())
funcdecl.error("`override` only applies to class member functions");
if (auto ti = parent.isTemplateInstance)
{
objc.setSelector(funcdecl, sc);
objc.setAsOptional(funcdecl, sc);
}
objc.validateSelector(funcdecl);
objc.validateOptional(funcdecl);
// Reflect this.type to f because it could be changed by findVtblIndex
f = funcdecl.type.toTypeFunction();
Ldone:
if (!funcdecl.fbody && !funcdecl.allowsContractWithoutBody())
funcdecl.error("`in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract");
/* Do not allow template instances to add virtual functions
* to a class.
*/
if (funcdecl.isVirtual())
{
if (auto ti = parent.isTemplateInstance())
{
// Take care of nested templates
while (1)
{
TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
if (!ti2)
break;
ti = ti2;
}
// If it's a member template
ClassDeclaration cd = ti.tempdecl.isClassMember();
if (cd)
{
funcdecl.error("cannot use template to add virtual function to class `%s`", cd.toChars());
}
}
}
funcdecl.checkMain(); // Check main() parameters and return type
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
if (funcdecl.canInferAttributes(sc))
funcdecl.initInferAttributes();
Module.dprogress++;
funcdecl.semanticRun = PASS.semanticdone;
/* Save scope for possible later use (if we need the
* function internals)
*/
funcdecl._scope = sc.copy();
funcdecl._scope.setNoFree();
__gshared bool printedMain = false; // semantic might run more than once
if (global.params.verbose && !printedMain)
{
const(char)* type = funcdecl.isMain() ? "main" : funcdecl.isWinMain() ? "winmain" : funcdecl.isDllMain() ? "dllmain" : cast(const(char)*)null;
Module mod = sc._module;
if (type && mod)
{
printedMain = true;
auto name = mod.srcfile.toChars();
auto path = FileName.searchPath(global.path, name, true);
message("entry %-10s\t%s", type, path ? path : name);
}
}
if (funcdecl.fbody && sc._module.isRoot() &&
(funcdecl.isMain() || funcdecl.isWinMain() || funcdecl.isDllMain() || funcdecl.isCMain()))
global.hasMainFunction = true;
if (funcdecl.fbody && funcdecl.isMain() && sc._module.isRoot())
{
// check if `_d_cmain` is defined
bool cmainTemplateExists()
{
auto rootSymbol = sc.search(funcdecl.loc, Id.empty, null);
if (auto moduleSymbol = rootSymbol.search(funcdecl.loc, Id.object))
if (moduleSymbol.search(funcdecl.loc, Id.CMain))
return true;
return false;
}
// Only mixin `_d_cmain` if it is defined
if (cmainTemplateExists())
{
// add `mixin _d_cmain!();` to the declaring module
auto tqual = new TypeIdentifier(funcdecl.loc, Id.CMain);
auto tm = new TemplateMixin(funcdecl.loc, null, tqual, null);
sc._module.members.push(tm);
}
}
assert(funcdecl.type.ty != Terror || funcdecl.errors);
// semantic for parameters' UDAs
foreach (i, param; f.parameterList)
{
if (param && param.userAttribDecl)
param.userAttribDecl.dsymbolSemantic(sc);
}
}
/// Do the semantic analysis on the external interface to the function.
override void visit(FuncDeclaration funcdecl)
{
funcDeclarationSemantic(funcdecl);
}
override void visit(CtorDeclaration ctd)
{
//printf("CtorDeclaration::semantic() %s\n", 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(ctd);
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) // empty default ctor w/o any varargs
{
if (ctd.fbody || !(ctd.storage_class & STC.disable))
{
ctd.error("default constructor for structs only allowed " ~
"with `@disable`, no body, and no parameters");
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[0].defaultArg)
{
// if the first parameter has a default argument, then the rest does as well
if (ctd.storage_class & STC.disable)
{
ctd.error("is marked `@disable`, so it cannot have default "~
"arguments for all parameters.");
errorSupplemental(ctd.loc, "Use `@disable this();` if you want to disable default initialization.");
}
else
ctd.error("all parameters have default arguments, "~
"but structs cannot have default constructors.");
}
else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
{
//printf("tf: %s\n", tf.toChars());
auto param = tf.parameterList[0];
if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
{
//printf("copy constructor\n");
ctd.isCpCtor = true;
}
}
}
else if (dim == 0 && tf.parameterList.varargs == VarArg.none)
{
ad.defaultCtor = ctd;
}
}
// https://issues.dlang.org/show_bug.cgi?id=22593
else if (auto ti = ctd.parent.isTemplateInstance())
{
if (sd && sd.hasCopyCtor && (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
{
auto param = tf.parameterList[0];
// if the template instance introduces an rvalue constructor
// between the members of a struct declaration, we should check if a
// copy constructor exists and issue an error in that case.
if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
{
.error(ctd.loc, "Cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars);
.errorSupplemental(ti.loc, "Template instance `%s` creates a rvalue constructor for `struct %s`",
ti.toChars(), sd.toChars());
}
}
}
}
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(pbd);
sc.pop();
}
override void visit(DtorDeclaration dd)
{
//printf("DtorDeclaration::semantic() %s\n", toChars());
//printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id::dtor.toChars(), 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 (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.dim;
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(dd);
sc.pop();
}
override void visit(StaticCtorDeclaration scd)
{
//printf("StaticCtorDeclaration::semantic()\n");
if (scd.semanticRun >= PASS.semanticdone)
return;
if (scd._scope)
{
sc = scd._scope;
scd._scope = null;
}
scd.parent = sc.parent;
Dsymbol p = scd.parent.pastMixin();
if (!p.isScopeDsymbol())
{
const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : "");
error(scd.loc, "`%sstatic` constructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars());
scd.type = Type.terror;
scd.errors = true;
return;
}
if (!scd.type)
scd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, scd.storage_class);
/* If the static ctor 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 (scd.isInstantiated() && scd.semanticRun < PASS.semantic)
{
/* Add this prefix to the constructor:
* ```
* static int gate;
* if (++gate != 1) return;
* ```
* or, for shared constructor:
* ```
* shared int gate;
* if (core.atomic.atomicOp!"+="(gate, 1) != 1) return;
* ```
*/
const bool isShared = !!scd.isSharedStaticCtorDeclaration();
auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0);
auto sa = new Statements();
Statement s = new ExpStatement(Loc.initial, v);
sa.push(s);
Expression e;
if (isShared)
{
e = doAtomicOp("+=", v.ident, IntegerExp.literal!(1));
if (e is null)
{
scd.error("shared static constructor within a template require `core.atomic : atomicOp` to be present");
return;
}
}
else
{
e = new AddAssignExp(
Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!1);
}
e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!1);
s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
sa.push(s);
if (scd.fbody)
sa.push(scd.fbody);
scd.fbody = new CompoundStatement(Loc.initial, sa);
}
const LINK save = sc.linkage;
if (save != LINK.d)
{
const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : "");
deprecation(scd.loc, "`%sstatic` constructor can only be of D linkage", s);
// Just correct it
sc.linkage = LINK.d;
}
funcDeclarationSemantic(scd);
sc.linkage = save;
// We're going to need ModuleInfo
Module m = scd.getModule();
if (!m)
m = sc._module;
if (m)
{
m.needmoduleinfo = 1;
//printf("module1 %s needs moduleinfo\n", m.toChars());
}
}
override void visit(StaticDtorDeclaration sdd)
{
if (sdd.semanticRun >= PASS.semanticdone)
return;
if (sdd._scope)
{
sc = sdd._scope;
sdd._scope = null;
}
sdd.parent = sc.parent;
Dsymbol p = sdd.parent.pastMixin();
if (!p.isScopeDsymbol())
{
const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : "");
error(sdd.loc, "`%sstatic` destructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars());
sdd.type = Type.terror;
sdd.errors = true;
return;
}
if (!sdd.type)
sdd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sdd.storage_class);
/* If the static ctor 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 (sdd.isInstantiated() && sdd.semanticRun < PASS.semantic)
{
/* Add this prefix to the constructor:
* ```
* static int gate;
* if (--gate != 0) return;
* ```
* or, for shared constructor:
* ```
* shared int gate;
* if (core.atomic.atomicOp!"-="(gate, 1) != 0) return;
* ```
*/
const bool isShared = !!sdd.isSharedStaticDtorDeclaration();
auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0);
auto sa = new Statements();
Statement s = new ExpStatement(Loc.initial, v);
sa.push(s);
Expression e;
if (isShared)
{
e = doAtomicOp("-=", v.ident, IntegerExp.literal!(1));
if (e is null)
{
sdd.error("shared static destructo within a template require `core.atomic : atomicOp` to be present");
return;
}
}
else
{
e = new AddAssignExp(
Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!(-1));
}
e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!0);
s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
sa.push(s);
if (sdd.fbody)
sa.push(sdd.fbody);
sdd.fbody = new CompoundStatement(Loc.initial, sa);
sdd.vgate = v;
}
const LINK save = sc.linkage;
if (save != LINK.d)
{
const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : "");
deprecation(sdd.loc, "`%sstatic` destructor can only be of D linkage", s);
// Just correct it
sc.linkage = LINK.d;
}
funcDeclarationSemantic(sdd);
sc.linkage = save;
// We're going to need ModuleInfo
Module m = sdd.getModule();
if (!m)
m = sc._module;
if (m)
{
m.needmoduleinfo = 1;
//printf("module2 %s needs moduleinfo\n", m.toChars());
}
}
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
)
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.flags = (sc.flags & ~SCOPE.contract) | SCOPE.invariant_;
sc.linkage = LINK.d;
funcDeclarationSemantic(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(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(nd);
}
override void visit(StructDeclaration sd)
{
//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;
int 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);
if (auto ts = sd.type.isTypeStruct())
if (ts.sym != sd)
{
auto ti = ts.sym.isInstantiated();
if (ti && isError(ti))
ts.sym = sd;
}
// Ungag errors when not speculative
Ungag ungag = sd.ungagSpeculative();
if (sd.semanticRun == PASS.initial)
{
sd.visibility = sc.visibility;
sd.alignment = sc.alignment();
sd.storage_class |= sc.stc;
if (sd.storage_class & STC.abstract_)
sd.error("structs, unions cannot be `abstract`");
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;
UserAttributeDeclaration.checkGNUABITag(sd, sc.linkage);
if (!sd.members) // if opaque declaration
{
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); sd.errors |= s.errors; } );
if (sd.errors)
sd.type = Type.terror;
if (!sd.determineFields())
{
if (sd.type.ty != Terror)
{
sd.error(sd.loc, "circular or forward reference");
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();
//printf("\tdeferring %s\n", toChars());
return deferDsymbolSemantic(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);
sd.hasCopyCtor = buildCopyCtor(sd, sc2);
sd.postblit = buildPostBlit(sd, sc2);
buildOpAssign(sd, sc2);
buildOpEquals(sd, sc2);
if (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);
Module.dprogress++;
sd.semanticRun = PASS.semanticdone;
//printf("-StructDeclaration::semantic(this=%p, '%s')\n", sd, sd.toChars());
sc2.pop();
if (sd.ctor)
{
Dsymbol scall = sd.search(Loc.initial, Id.call);
if (scall)
{
uint xerrors = global.startGagging();
sc = sc.push();
sc.tinst = null;
sc.minst = null;
auto fcall = resolveFuncCall(sd.loc, sc, scall, null, null, null, FuncResolveFlag.quiet);
sc = sc.pop();
global.endGagging(xerrors);
if (fcall && fcall.isStatic())
{
sd.error(fcall.loc, "`static opCall` is hidden by constructors and can never be called");
errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
}
}
}
if (sd.type.ty == Tstruct && (cast(TypeStruct)sd.type).sym != sd)
{
// https://issues.dlang.org/show_bug.cgi?id=19024
StructDeclaration sym = (cast(TypeStruct)sd.type).sym;
version (none)
{
printf("this = %p %s\n", sd, sd.toChars());
printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars());
}
sd.error("already exists at %s. Perhaps in another function with the same name?", 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);
}
// @@@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.");
}
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;
int 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_)
cldec.error("storage class `auto` is invalid when declaring a class, did you mean to use `scope`?");
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;
UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage);
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.dim;)
{
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.dim)
{
BaseClass* b = (*cldec.baseclasses)[0];
Type tb = b.type.toBasetype();
TypeClass tc = tb.isTypeClass();
if (!tc)
{
if (b.type != Type.terror)
cldec.error("base type must be `class` or `interface`, not `%s`", 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)
{
cldec.error("circular inheritance");
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.dim;)
{
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.dim)
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)
{
cldec.error("inherits from duplicate interface `%s`", 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(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()
{
cldec.error("missing or corrupt object.d");
fatal();
}
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_)
cldec.error("cannot inherit from class `%s` because it is `final`", 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.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.dim];
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.dim; 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(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.dim == 0)
{
cldec.error("C++ base class `%s` needs at least one virtual function", cldec.baseClass.toChars());
}
// Copy vtbl[] from base class
cldec.vtbl.setDim(cldec.baseClass.vtbl.dim);
memcpy(cldec.vtbl.tdata(), cldec.baseClass.vtbl.tdata(), (void*).sizeof * cldec.vtbl.dim);
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_)
cldec.error("static class cannot inherit from nested class `%s`", 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())
{
cldec.error("is nested within `%s`, but super class `%s` is nested within `%s`",
cldec.toParentLocal().toChars(),
cldec.baseClass.toChars(),
cldec.baseClass.toParentLocal().toChars());
}
else
{
cldec.error("is not nested, but super class `%s` is nested within `%s`",
cldec.baseClass.toChars(),
cldec.baseClass.toParentLocal().toChars());
}
cldec.enclosing = null;
}
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())
{
cldec.error("needs the frame pointer of `%s`, but super class `%s` needs the frame pointer of `%s`",
cldec.toParent2().toChars(),
cldec.baseClass.toChars(),
cldec.baseClass.toParent2().toChars());
}
else
{
cldec.error("doesn't need a frame pointer, but super class `%s` needs the frame pointer of `%s`",
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.dim 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(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, null, FuncResolveFlag.quiet);
if (!fd) // try shared base ctor instead
fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type.sharedOf, null, 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, 0, tf);
ctor.storage_class |= STC.inference;
ctor.flags |= FUNCFLAG.generated;
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
{
cldec.error("cannot implicitly generate a default constructor when base class `%s` is missing a default constructor",
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))
cldec.error(f.loc, "identity assignment operator overload is illegal");
}
cldec.inv = buildInv(cldec, sc2);
Module.dprogress++;
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)
{
cldec.error("cannot infer `abstract` attribute due to circular dependencies");
}
}
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());
}
cldec.error("already exists at %s. Perhaps in another function with the same name?", cd.loc.toChars());
}
if (global.errors != errors)
{
// The type is no good.
cldec.type = Type.terror;
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_))
{
vd.error("Field members of a `synchronized` class cannot be `%s`",
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);
// @@@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;
int 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.dim;)
{
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.dim && sc.linkage == LINK.cpp)
idec.classKind = ClassKind.cpp;
idec.cppnamespace = sc.namespace;
UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage);
if (sc.linkage == LINK.objc)
objc.setObjc(idec);
// Check for errors, handle forward references
BCLoop:
for (size_t i = 0; i < idec.baseclasses.dim;)
{
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)
idec.error("base type must be `interface`, not `%s`", 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)
{
idec.error("inherits from duplicate interface `%s`", b2.sym.toChars());
idec.baseclasses.remove(i);
continue BCLoop;
}
}
if (tc.sym == idec || idec.isBaseOf2(tc.sym))
{
idec.error("circular inheritance of interface");
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(idec, scx);
}
idec.baseok = Baseok.done;
idec.interfaces = idec.baseclasses.tdata()[0 .. idec.baseclasses.dim];
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.dim; 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(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.dim;
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) );
Module.dprogress++;
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);
// @@@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.");
}
}
/*******************************************
* Add members of EnumDeclaration to the symbol table(s).
* Params:
* ed = EnumDeclaration
* sc = context of `ed`
* sds = symbol table that `ed` resides in
*/
void addEnumMembers(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
{
if (ed.added)
return;
ed.added = true;
if (!ed.members)
return;
const bool isCEnum = (sc.flags & SCOPE.Cfile) != 0; // it's an ImportC enum
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())
{
em.ed = ed;
if (isCEnum)
{
em.addMember(sc, ed); // add em to ed's symbol table
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);
}
}
});
}
void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* fargs)
{
//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;
tempinst.error(tempinst.loc, "recursive template expansion");
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, fargs))
{
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);
TemplateStats.incInstance(tempdecl, tempinst);
tempdecl.checkDeprecated(tempinst.loc, sc);
// If tempdecl is a mixin, disallow it
if (tempdecl.ismixin)
{
tempinst.error("mixin templates are not regular templates");
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, fargs);
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)
{
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(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());
}
uint errorsave = global.errors;
tempinst.inst = tempinst;
tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
//printf("parent = '%s'\n", parent.kind());
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.dim - 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.dim; i++)
{
if ((*tempdecl.parameters)[i].isTemplateThisParameter() is null)
continue;
Type t = isType((*tempinst.tiargs)[i]);
assert(t);
if (StorageClass 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)
{
tempinst.error("template instantiation `%s` forward references template declaration `%s`", 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 = 0;
// Declare each template parameter as an alias for the argument type
Scope* paramscope = _scope.push();
paramscope.stc = 0;
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.dim = %d\n", tempinst.members.dim);
if (tempinst.members.dim)
{
Dsymbol s;
if (Dsymbol.oneMembers(tempinst.members, &s, tempdecl.ident) && s)
{
//printf("tempdecl.ident = %s, s = '%s'\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
//printf("setting aliasdecl\n");
tempinst.aliasdecl = s;
}
}
/* If function template declaration
*/
if (fargs && 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.fargs = fargs;
}
}
// 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.dim)
{
Dsymbol s;
if (Dsymbol.oneMembers(tempinst.members, &s, tempdecl.ident) && s)
{
if (!tempinst.aliasdecl || tempinst.aliasdecl != s)
{
//printf("tempdecl.ident = %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.dim; 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.dim)
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.flags & SCOPE.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());
tempinst.trySemantic3(sc2);
for (size_t i = 0; i < deferred.dim; 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.dim == 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
tempinst.error("recursive expansion");
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.dim)
{
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();
}
Laftersemantic:
sc2.pop();
_scope.pop();
// Give additional context info if error occurred during instantiation
if (global.errors != errorsave)
{
if (!tempinst.errors)
{
if (!tempdecl.literal)
tempinst.error(tempinst.loc, "error instantiating");
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 = 0;
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 = 0;
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\n", ds.toChars());
// 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;
// TypeTraits needs to know if it's located in an AliasDeclaration
const oldflags = sc.flags;
sc.flags |= SCOPE.alias_;
void normalRet()
{
sc.flags = oldflags;
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())
{
// 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.dim)
{
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)
{
ds.error("cannot resolve");
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)
ds.error("cannot alias an expression `%s`", 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)
{
ds.error("undefined identifier `%s`", ds.ident.toChars());
return null;
}
if (as.errors)
return null;
auto ad = as.isAliasDeclaration();
if (!ad)
{
ds.error("identifier `%s` must be an alias declaration", as.toChars());
return null;
}
if (ad.overnext)
{
ds.error("cannot reassign overloaded alias");
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())
{
ds.error("must be a member of a template");
return null;
}
return ad;
}
auto aliassym = findAliasDeclaration(ds, sc);
if (!aliassym)
return errorRet();
if (aliassym.adFlags & Declaration.wasRead)
{
if (!aliassym.errors)
error(ds.loc, "%s was read, so cannot reassign", aliassym.toChars());
aliassym.errors = true;
return errorRet();
}
aliassym.adFlags |= Declaration.ignoreRead; // 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;
/* 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 == aliassym)
{
ds.error("cannot resolve");
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)
ds.error("cannot alias an expression `%s`", e.toChars());
return errorRet();
}
}
}
ds.type = t;
}
if (s == aliassym)
{
assert(global.errors);
return errorRet();
}
if (s) // it's a symbolic alias
{
//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.adFlags &= ~Declaration.ignoreRead;
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;
}
/***************************************
* 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.dim = %d\n", toChars(), fields.dim);
// determineFields can be called recursively from one of the fields's v.semantic
ad.fields.setDim(0);
static int func(Dsymbol s, AggregateDeclaration ad)
{
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.aliassym)
return 0; // If this variable was really a tuple, skip it.
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 " : "";
ad.error("cannot have field `%s` with %ssame struct type", 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.dim; i++)
{
auto s = (*ad.members)[i];
if (s.apply(&func, 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)
{
__gshared Import imp = null;
__gshared Identifier[1] id;
assert(op == "-=" || op == "+=");
const loc = Loc.initial;
// Below code is similar to `loadStdMath` (used for `^^` operator)
if (!imp)
{
id[0] = Id.core;
auto s = new Import(Loc.initial, id[], Id.atomic, null, true);
// Module.load will call fatal() if there's no std.math available.
// Gag the error here, pushing the error handling to the caller.
uint errors = global.startGagging();
s.load(null);
if (s.mod)
{
s.mod.importAll(null);
s.mod.dsymbolSemantic(null);
}
global.endGagging(errors);
imp = s;
}
// Module couldn't be loaded
if (imp.mod is null)
return null;
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, imp.mod);
auto dti = new DotTemplateInstanceExp(
loc, sc, Id.atomicOp, tiargs);
return CallExp.create(loc, dti, args);
}