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