blob: b877828ffe56aa61ed6877cc2da01091c64d36c6 [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.intrange;
import dmd.hdrgen;
import dmd.mtype;
import dmd.mustuse;
import dmd.nogc;
import dmd.nspace;
import dmd.objc;
import dmd.opover;
import dmd.parse;
import dmd.root.array;
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;
}
/**
* Apply pragma printf/scanf to FuncDeclarations under `s`,
* poking through attribute declarations such as `extern(C)`
* but not through aggregates or function bodies.
*
* Params:
* s = symbol to apply
* printf = `true` for printf, `false` for scanf
*/
private void setPragmaPrintf(Dsymbol s, bool printf)
{
if (auto fd = s.isFuncDeclaration())
{
fd.printf = printf;
fd.scanf = !printf;
}
if (auto ad = s.isAttribDeclaration())
{
ad.include(null).foreachDsymbol( (s) { setPragmaPrintf(s, printf); } );
}
}
/*************************************
* 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++;
if (!dsym.isScope())
dsym.maybeScope = true;
Scope* scx = null;
if (dsym._scope)
{
sc = dsym._scope;
scx = sc;
dsym._scope = null;
}
if (!sc)
return;
dsym.semanticRun = PASS.semantic;
// 'static foreach' variables should not inherit scope properties
// https://issues.dlang.org/show_bug.cgi?id=19482
if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local))
{
dsym._linkage = LINK.d;
dsym.visibility = Visibility(Visibility.Kind.public_);
dsym.overlapped = false; // unset because it is modified early on this function
dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope()
}
else
{
/* Pick up storage classes from context, but except synchronized,
* override, abstract, and final.
*/
dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_));
dsym.userAttribDecl = sc.userAttribDecl;
dsym.cppnamespace = sc.namespace;
dsym._linkage = sc.linkage;
dsym.visibility = sc.visibility;
dsym.alignment = sc.alignment();
}
if (dsym.storage_class & STC.extern_ && dsym._init)
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 (dsym.storage_class & STC.gshared && !dsym.isMember())
{
sc.setUnsafe(false, dsym.loc, "__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 | dsym.storage_class;
if ((dsym.storage_class & STC.parameter) && (arg.storageClass & STC.parameter))
storage_class |= arg.storageClass;
auto v = new VarDeclaration(dsym.loc, arg.type, id, ti, storage_class);
//printf("declaring field %s of type %s\n", v.toChars(), v.type.toChars());
v.overlapped = dsym.overlapped;
v.dsymbolSemantic(sc);
Expression e = new VarExp(dsym.loc, v);
(*exps)[i] = e;
}
auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
v2.parent = dsym.parent;
v2.isexp = true;
dsym.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
// https://issues.dlang.org/show_bug.cgi?id=23168
if (dsym.storage_class & STC.returnScope)
{
dsym.storage_class &= ~(STC.return_ | STC.returnScope);
}
}
}
if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
{
}
else
{
AggregateDeclaration aad = parent.isAggregateDeclaration();
if (aad)
{
if (global.params.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 && sc.func)
{
if (dsym._init && dsym._init.isVoidInitializer())
{
if (dsym.type.hasPointers()) // also computes type size
sc.setUnsafe(false, dsym.loc,
"`void` initializers for pointers not allowed in safe functions");
else if (dsym.type.hasInvariant())
sc.setUnsafe(false, dsym.loc,
"`void` initializers for structs with invariants are not allowed in safe functions");
else if (dsym.type.hasSystemFields())
sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
"`void` initializers for `@system` variables 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())
{
sc.setUnsafe(false, dsym.loc, "`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");
// Don't allow non-extern, non-__gshared variables to be interfaced with C++
if (dsym._linkage == LINK.cpp && !(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.gshared)) && dsym.isDataseg())
{
const char* p = (dsym.storage_class & STC.shared_) ? "shared" : "static";
dsym.error("cannot have `extern(C++)` linkage because it is `%s`", p);
errorSupplemental(dsym.loc, "perhaps declare it as `__gshared` instead");
dsym.errors = true;
}
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(), dsym.toChars());
if (!ei)
{
ArrayInitializer ai = dsym._init.isArrayInitializer();
Expression e;
if (ai && tb.ty == Taarray)
e = ai.toAssocArrayLiteral();
else
e = dsym._init.initializerToExpression(null, (sc.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);
}
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 if (auto ale = ex.isArrayLiteralExp())
{
// or an array literal assigned to a `scope` variable
if (!dsym.type.nextOf().needsDestruction())
ale.onstack = true;
}
}
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;
}
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')\n", dsym.toChars());
if (dsym.semanticRun >= PASS.semanticdone)
return;
visit(cast(VarDeclaration)dsym);
if (dsym.errors)
return;
if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
{
dsym.error("bit-field must be member of struct, union, or class");
}
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)
{
static if (LOG)
{
printf("Import::semantic('%s') %s\n", toPrettyChars(), id.toChars());
scope(exit)
printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg);
}
if (imp.semanticRun > PASS.initial)
return;
if (imp._scope)
{
sc = imp._scope;
imp._scope = null;
}
if (!sc)
return;
imp.parent = sc.parent;
imp.semanticRun = PASS.semantic;
// Load if not already done so
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.buffer is null || (imp.id == Id.object && sc._module.ident == Id.object) ||
strcmp(sc._module.ident.toChars(), "__main") == 0)
return;
/* 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.buffer;
Module imod = sc._module;
if (!global.params.moduleDeps.name)
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();
}
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)
return;
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)[])
{
if (pd.ident == Id.printf || pd.ident == Id.scanf)
{
s.setPragmaPrintf(pd.ident == Id.printf);
continue;
}
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.pMangleOverride = new MangleOverride;
void setString(ref Expression e)
{
if (auto se = verifyMangleString(e))
{
const name = (cast(const(char)[])se.peekData()).xarraydup;
ad.pMangleOverride.id = Identifier.idPool(name);
e = se;
}
else
e.error("must be a string");
}
if (agg)
{
ad.pMangleOverride.agg = agg;
if (pd.args.dim == 2)
{
setString((*pd.args)[1]);
}
else
ad.pMangleOverride.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)
return noDeclarations();
if (!pragmaMsgSemantic(pd.loc, sc, pd.args))
return;
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.buffer && !global.params.moduleDeps.name)
{
OutBuffer* ob = global.params.moduleDeps.buffer;
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)
{
pragmaStartAddressSemantic(pd.loc, sc, pd.args);
return noDeclarations();
}
else if (pd.ident == Id.Pinline)
{
// 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())
{
if (isCtor)
f.isCrtCtor = true;
else
f.isCrtDtor = true;
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)
return attribSemantic(ns);
ns.cppnamespace = sc.namespace;
sc = sc.startCTFE();
ns.exp = ns.exp.expressionSemantic(sc);
ns.exp = resolveProperties(sc, ns.exp);
sc = sc.endCTFE();
ns.exp = ns.exp.ctfeInterpret();
// Can be either a tuple of strings or a string itself
if (auto te = ns.exp.isTupleExp())
{
expandTuples(te.exps);
CPPNamespaceDeclaration current = ns.cppnamespace;
for (size_t d = 0; d < te.exps.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.peekSlice(), 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", ed, 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;
}
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);
checkMustUseReserved(ed);
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);
//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;
// @@@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
// C11 6.7.2.2-2 value must be representable as an int.
// The sizemask represents all values that int will fit into,
// from 0..uint.max. We want to cover int.min..uint.max.
const mask = Type.tint32.sizemask();
IntRange ir = IntRange(SignExtendedNumber(~(mask >> 1), true),
SignExtendedNumber(mask));
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);
}
if (!ir.contains(getIntRange(ie)))
{
// C11 6.7.2.2-2
em.error("enum member value `%s` does not fit in an `int`", e.toChars());
return errorReturn(em);
}
nextValue = cast(int)ie.toInteger();
em.value = new IntegerExp(em.loc, nextValue, Type.tint32);
}
else
{
// C11 6.7.2.2-3 add 1 to value of previous enumeration constant
bool first = (em == (*em.ed.members)[0]);
if (!first)
{
import core.checkedint : adds;
bool overflow;
nextValue = adds(nextValue, 1, overflow);
if (overflow)
{
em.error("initialization with `%d+1` causes overflow for type `int`", nextValue - 1);
return errorReturn(em);
}
}
em.value = new IntegerExp(em.loc, nextValue, Type.tint32);
}
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.ddoc.doOutput)
{
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.<