| /** |
| * 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.<
|