/** * Miscellaneous declarations, including typedef, alias, variable declarations including the * implicit this declaration, type tuples, ClassInfo, ModuleInfo and various TypeInfos. * * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/declaration.d, _declaration.d) * Documentation: https://dlang.org/phobos/dmd_declaration.html * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/declaration.d */ module dmd.declaration; import core.stdc.stdio; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.attrib; import dmd.ctorflow; import dmd.dclass; import dmd.delegatize; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; import dmd.globals; import dmd.gluelayer; import dmd.id; import dmd.identifier; import dmd.init; import dmd.initsem; import dmd.intrange; import dmd.mtype; import dmd.common.outbuffer; import dmd.root.rootobject; import dmd.target; import dmd.tokens; import dmd.typesem; import dmd.visitor; /************************************ * Check to see the aggregate type is nested and its context pointer is * accessible from the current scope. * Returns true if error occurs. */ bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0) { Dsymbol sparent = ad.toParentLocal(); Dsymbol sparent2 = ad.toParent2(); Dsymbol s = sc.func; if (ad.isNested() && s) { //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent); //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars()); //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars()); if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2)) { error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars()); return true; } } bool result = false; for (size_t i = iStart; i < ad.fields.dim; i++) { VarDeclaration vd = ad.fields[i]; Type tb = vd.type.baseElemOf(); if (tb.ty == Tstruct) { result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym); } } return result; } /*********************************************** * Mark variable v as modified if it is inside a constructor that var * is a field in. */ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) { //printf("modifyFieldVar(var = %s)\n", var.toChars()); Dsymbol s = sc.func; while (1) { FuncDeclaration fd = null; if (s) fd = s.isFuncDeclaration(); if (fd && ((fd.isCtorDeclaration() && var.isField()) || (fd.isStaticCtorDeclaration() && !var.isField())) && fd.toParentDecl() == var.toParent2() && (!e1 || e1.op == EXP.this_)) { bool result = true; var.ctorinit = true; //printf("setting ctorinit\n"); if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof) { assert(e1); auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 || var.type.needsNested()); const dim = sc.ctorflow.fieldinit.length; auto ad = fd.isMemberDecl(); assert(ad); size_t i; for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? { if (ad.fields[i] == var) break; } assert(i < dim); auto fieldInit = &sc.ctorflow.fieldinit[i]; const fi = fieldInit.csx; if (fi & CSX.this_ctor) { if (var.type.isMutable() && e1.type.isMutable()) result = false; else { const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); .errorSupplemental(fieldInit.loc, "Previous initialization is here."); } } else if (sc.inLoop || (fi & CSX.label)) { if (!mustInit && var.type.isMutable() && e1.type.isMutable()) result = false; else { const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars()); } } fieldInit.csx |= CSX.this_ctor; fieldInit.loc = e1.loc; if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 { foreach (j, v; ad.fields) { if (v is var || !var.isOverlappedWith(v)) continue; v.ctorinit = true; sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; } } } else if (fd != sc.func) { if (var.type.isMutable()) result = false; else if (sc.func.fes) { const(char)* p = var.isField() ? "field" : var.kind(); .error(loc, "%s %s `%s` initialization is not allowed in foreach loop", MODtoChars(var.type.mod), p, var.toChars()); } else { const(char)* p = var.isField() ? "field" : var.kind(); .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`", MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); } } else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && var.type.isImmutable()) { .error(loc, "%s %s `%s` initialization is not allowed in `static this`", MODtoChars(var.type.mod), var.kind(), var.toChars()); errorSupplemental(loc, "Use `shared static this` instead."); } return result; } else { if (s) { s = s.toParentP(var.toParent2()); continue; } } break; } return false; } /****************************************** */ extern (C++) void ObjectNotFound(Identifier id) { error(Loc.initial, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars()); fatal(); } /* Accumulator for successive matches. */ struct MatchAccumulator { int count; // number of matches found so far MATCH last = MATCH.nomatch; // match level of lastf FuncDeclaration lastf; // last matching function we found FuncDeclaration nextf; // if ambiguous match, this is the "other" function } /*********************************************************** */ extern (C++) abstract class Declaration : Dsymbol { Type type; Type originalType; // before semantic analysis StorageClass storage_class = STC.undefined_; Visibility visibility; LINK _linkage = LINK.default_; // may be `LINK.system`; use `resolvedLinkage()` to resolve it short inuse; // used to detect cycles ubyte adFlags; // control re-assignment of AliasDeclaration (put here for packing reasons) enum wasRead = 1; // set if AliasDeclaration was read enum ignoreRead = 2; // ignore any reads of AliasDeclaration Symbol* isym; // import version of csym // overridden symbol with pragma(mangle, "...") const(char)[] mangleOverride; final extern (D) this(Identifier ident) { super(ident); visibility = Visibility(Visibility.Kind.undefined); } final extern (D) this(const ref Loc loc, Identifier ident) { super(loc, ident); visibility = Visibility(Visibility.Kind.undefined); } override const(char)* kind() const { return "declaration"; } override final uinteger_t size(const ref Loc loc) { assert(type); const sz = type.size(); if (sz == SIZE_INVALID) errors = true; return sz; } /** * Issue an error if an attempt to call a disabled method is made * * If the declaration is disabled but inside a disabled function, * returns `true` but do not issue an error message. * * Params: * loc = Location information of the call * sc = Scope in which the call occurs * isAliasedDeclaration = if `true` searches overload set * * Returns: * `true` if this `Declaration` is `@disable`d, `false` otherwise. */ extern (D) final bool checkDisabled(Loc loc, Scope* sc, bool isAliasedDeclaration = false) { if (!(storage_class & STC.disable)) return false; if (sc.func && sc.func.storage_class & STC.disable) return true; if (auto p = toParent()) { if (auto postblit = isPostBlitDeclaration()) { /* https://issues.dlang.org/show_bug.cgi?id=21885 * * If the generated postblit is disabled, it * means that one of the fields has a disabled * postblit. Print the first field that has * a disabled postblit. */ if (postblit.isGenerated()) { auto sd = p.isStructDeclaration(); assert(sd); for (size_t i = 0; i < sd.fields.dim; i++) { auto structField = sd.fields[i]; if (structField.overlapped) continue; Type tv = structField.type.baseElemOf(); if (tv.ty != Tstruct) continue; auto sdv = (cast(TypeStruct)tv).sym; if (!sdv.postblit) continue; if (sdv.postblit.isDisabled()) { p.error(loc, "is not copyable because field `%s` is not copyable", structField.toChars()); return true; } } } p.error(loc, "is not copyable because it has a disabled postblit"); return true; } } // if the function is @disabled, maybe there // is an overload in the overload set that isn't if (isAliasedDeclaration) { FuncDeclaration fd = isFuncDeclaration(); if (fd) { for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) if (!(ovl.storage_class & STC.disable)) return false; } } if (auto ctor = isCtorDeclaration()) { if (ctor.isCpCtor && ctor.isGenerated()) { .error(loc, "Generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", parent.toPrettyChars()); return true; } } error(loc, "cannot be used because it is annotated with `@disable`"); return true; } /************************************* * Check to see if declaration can be modified in this context (sc). * Issue error if not. * Params: * loc = location for error messages * e1 = `null` or `this` expression when this declaration is a field * sc = context * flag = if the first bit is set it means do not issue error message for * invalid modification; if the second bit is set, it means that this declaration is a field and a subfield of it is modified. * Returns: * Modifiable.yes or Modifiable.initialization */ extern (D) final Modifiable checkModify(Loc loc, Scope* sc, Expression e1, ModifyFlags flag) { VarDeclaration v = isVarDeclaration(); if (v && v.canassign) return Modifiable.initialization; if (isParameter() || isResult()) { for (Scope* scx = sc; scx; scx = scx.enclosing) { if (scx.func == parent && (scx.flags & SCOPE.contract)) { const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result"; if (!(flag & ModifyFlags.noError)) error(loc, "cannot modify %s `%s` in contract", s, toChars()); return Modifiable.initialization; // do not report type related errors } } } if (e1 && e1.op == EXP.this_ && isField()) { VarDeclaration vthis = e1.isThisExp().var; for (Scope* scx = sc; scx; scx = scx.enclosing) { if (scx.func == vthis.parent && (scx.flags & SCOPE.contract)) { if (!(flag & ModifyFlags.noError)) error(loc, "cannot modify parameter `this` in contract"); return Modifiable.initialization; // do not report type related errors } } } if (v && (v.isCtorinit() || isField())) { // It's only modifiable if inside the right constructor if ((storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_)) return Modifiable.initialization; if (flag & ModifyFlags.fieldAssign) return Modifiable.yes; return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes; } return Modifiable.yes; } override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) { Dsymbol s = Dsymbol.search(loc, ident, flags); if (!s && type) { s = type.toDsymbol(_scope); if (s) s = s.search(loc, ident, flags); } return s; } final bool isStatic() const pure nothrow @nogc @safe { return (storage_class & STC.static_) != 0; } /// Returns the linkage, resolving the target-specific `System` one. final LINK resolvedLinkage() const { return _linkage == LINK.system ? target.systemLinkage() : _linkage; } bool isDelete() { return false; } bool isDataseg() { return false; } bool isThreadlocal() { return false; } bool isCodeseg() const pure nothrow @nogc @safe { return false; } final bool isFinal() const pure nothrow @nogc @safe { return (storage_class & STC.final_) != 0; } bool isAbstract() { return (storage_class & STC.abstract_) != 0; } final bool isConst() const pure nothrow @nogc @safe { return (storage_class & STC.const_) != 0; } final bool isImmutable() const pure nothrow @nogc @safe { return (storage_class & STC.immutable_) != 0; } final bool isWild() const pure nothrow @nogc @safe { return (storage_class & STC.wild) != 0; } final bool isAuto() const pure nothrow @nogc @safe { return (storage_class & STC.auto_) != 0; } final bool isScope() const pure nothrow @nogc @safe { return (storage_class & STC.scope_) != 0; } final bool isSynchronized() const pure nothrow @nogc @safe { return (storage_class & STC.synchronized_) != 0; } final bool isParameter() const pure nothrow @nogc @safe { return (storage_class & STC.parameter) != 0; } override final bool isDeprecated() const pure nothrow @nogc @safe { return (storage_class & STC.deprecated_) != 0; } final bool isDisabled() const pure nothrow @nogc @safe { return (storage_class & STC.disable) != 0; } final bool isOverride() const pure nothrow @nogc @safe { return (storage_class & STC.override_) != 0; } final bool isResult() const pure nothrow @nogc @safe { return (storage_class & STC.result) != 0; } final bool isField() const pure nothrow @nogc @safe { return (storage_class & STC.field) != 0; } final bool isIn() const pure nothrow @nogc @safe { return (storage_class & STC.in_) != 0; } final bool isOut() const pure nothrow @nogc @safe { return (storage_class & STC.out_) != 0; } final bool isRef() const pure nothrow @nogc @safe { return (storage_class & STC.ref_) != 0; } /// Returns: Whether the variable is a reference, annotated with `out` or `ref` final bool isReference() const pure nothrow @nogc @safe { return (storage_class & (STC.ref_ | STC.out_)) != 0; } final bool isFuture() const pure nothrow @nogc @safe { return (storage_class & STC.future) != 0; } override final Visibility visible() pure nothrow @nogc @safe { return visibility; } override final inout(Declaration) isDeclaration() inout pure nothrow @nogc @safe { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TupleDeclaration : Declaration { Objects* objects; bool isexp; // true: expression tuple TypeTuple tupletype; // !=null if this is a type tuple extern (D) this(const ref Loc loc, Identifier ident, Objects* objects) { super(loc, ident); this.objects = objects; } override TupleDeclaration syntaxCopy(Dsymbol s) { assert(0); } override const(char)* kind() const { return "tuple"; } override Type getType() { /* If this tuple represents a type, return that type */ //printf("TupleDeclaration::getType() %s\n", toChars()); if (isexp) return null; if (!tupletype) { /* It's only a type tuple if all the Object's are types */ for (size_t i = 0; i < objects.dim; i++) { RootObject o = (*objects)[i]; if (o.dyncast() != DYNCAST.type) { //printf("\tnot[%d], %p, %d\n", i, o, o.dyncast()); return null; } } /* We know it's a type tuple, so build the TypeTuple */ Types* types = cast(Types*)objects; auto args = new Parameters(objects.dim); OutBuffer buf; int hasdeco = 1; for (size_t i = 0; i < types.dim; i++) { Type t = (*types)[i]; //printf("type = %s\n", t.toChars()); version (none) { buf.printf("_%s_%d", ident.toChars(), i); const len = buf.offset; const name = buf.extractSlice().ptr; auto id = Identifier.idPool(name, len); auto arg = new Parameter(STC.in_, t, id, null); } else { auto arg = new Parameter(0, t, null, null, null); } (*args)[i] = arg; if (!t.deco) hasdeco = 0; } tupletype = new TypeTuple(args); if (hasdeco) return tupletype.typeSemantic(Loc.initial, null); } return tupletype; } override Dsymbol toAlias2() { //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars()); for (size_t i = 0; i < objects.dim; i++) { RootObject o = (*objects)[i]; if (Dsymbol s = isDsymbol(o)) { s = s.toAlias2(); (*objects)[i] = s; } } return this; } override bool needThis() { //printf("TupleDeclaration::needThis(%s)\n", toChars()); for (size_t i = 0; i < objects.dim; i++) { RootObject o = (*objects)[i]; if (o.dyncast() == DYNCAST.expression) { Expression e = cast(Expression)o; if (DsymbolExp ve = e.isDsymbolExp()) { Declaration d = ve.s.isDeclaration(); if (d && d.needThis()) { return true; } } } } return false; } override inout(TupleDeclaration) isTupleDeclaration() inout { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/declaration.html#AliasDeclaration */ extern (C++) final class AliasDeclaration : Declaration { Dsymbol aliassym; // alias ident = aliassym; Dsymbol overnext; // next in overload list Dsymbol _import; // !=null if unresolved internal alias for selective import extern (D) this(const ref Loc loc, Identifier ident, Type type) { super(loc, ident); //printf("AliasDeclaration(id = '%s', type = %p)\n", id.toChars(), type); //printf("type = '%s'\n", type.toChars()); this.type = type; assert(type); } extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) { super(loc, ident); //printf("AliasDeclaration(id = '%s', s = %p)\n", id.toChars(), s); assert(s != this); this.aliassym = s; assert(s); } static AliasDeclaration create(const ref Loc loc, Identifier id, Type type) { return new AliasDeclaration(loc, id, type); } override AliasDeclaration syntaxCopy(Dsymbol s) { //printf("AliasDeclaration::syntaxCopy()\n"); assert(!s); AliasDeclaration sa = type ? new AliasDeclaration(loc, ident, type.syntaxCopy()) : new AliasDeclaration(loc, ident, aliassym.syntaxCopy(null)); sa.comment = comment; sa.storage_class = storage_class; return sa; } override bool overloadInsert(Dsymbol s) { //printf("[%s] AliasDeclaration::overloadInsert('%s') s = %s %s @ [%s]\n", // loc.toChars(), toChars(), s.kind(), s.toChars(), s.loc.toChars()); /** Aliases aren't overloadable themselves, but if their Aliasee is * overloadable they are converted to an overloadable Alias (either * FuncAliasDeclaration or OverDeclaration). * * This is done by moving the Aliasee into such an overloadable alias * which is then used to replace the existing Aliasee. The original * Alias (_this_) remains a useless shell. * * This is a horrible mess. It was probably done to avoid replacing * existing AST nodes and references, but it needs a major * simplification b/c it's too complex to maintain. * * A simpler approach might be to merge any colliding symbols into a * simple Overload class (an array) and then later have that resolve * all collisions. */ if (semanticRun >= PASS.semanticdone) { /* Semantic analysis is already finished, and the aliased entity * is not overloadable. */ if (type) return false; /* When s is added in member scope by static if, mixin("code") or others, * aliassym is determined already. See the case in: test/compilable/test61.d */ auto sa = aliassym.toAlias(); if (auto td = s.toAlias().isTemplateDeclaration()) s = td.funcroot ? td.funcroot : td; if (auto fd = sa.isFuncDeclaration()) { auto fa = new FuncAliasDeclaration(ident, fd); fa.visibility = visibility; fa.parent = parent; aliassym = fa; return aliassym.overloadInsert(s); } if (auto td = sa.isTemplateDeclaration()) { auto od = new OverDeclaration(ident, td.funcroot ? td.funcroot : td); od.visibility = visibility; od.parent = parent; aliassym = od; return aliassym.overloadInsert(s); } if (auto od = sa.isOverDeclaration()) { if (sa.ident != ident || sa.parent != parent) { od = new OverDeclaration(ident, od); od.visibility = visibility; od.parent = parent; aliassym = od; } return od.overloadInsert(s); } if (auto os = sa.isOverloadSet()) { if (sa.ident != ident || sa.parent != parent) { os = new OverloadSet(ident, os); // TODO: visibility is lost here b/c OverloadSets have no visibility attribute // Might no be a practical issue, b/c the code below fails to resolve the overload anyhow. // ---- // module os1; // import a, b; // private alias merged = foo; // private alias to overload set of a.foo and b.foo // ---- // module os2; // import a, b; // public alias merged = bar; // public alias to overload set of a.bar and b.bar // ---- // module bug; // import os1, os2; // void test() { merged(123); } // should only look at os2.merged // // os.visibility = visibility; os.parent = parent; aliassym = os; } os.push(s); return true; } return false; } /* Don't know yet what the aliased symbol is, so assume it can * be overloaded and check later for correctness. */ if (overnext) return overnext.overloadInsert(s); if (s is this) return true; overnext = s; return true; } override const(char)* kind() const { return "alias"; } override Type getType() { if (type) return type; return toAlias().getType(); } override Dsymbol toAlias() { //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n", // loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse); assert(this != aliassym); //static int count; if (++count == 10) *(char*)0=0; // Reading the AliasDeclaration if (!(adFlags & ignoreRead)) adFlags |= wasRead; // can never assign to this AliasDeclaration again if (inuse == 1 && type && _scope) { inuse = 2; uint olderrors = global.errors; Dsymbol s = type.toDsymbol(_scope); //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this); if (global.errors != olderrors) goto Lerr; if (s) { s = s.toAlias(); if (global.errors != olderrors) goto Lerr; aliassym = s; inuse = 0; } else { Type t = type.typeSemantic(loc, _scope); if (t.ty == Terror) goto Lerr; if (global.errors != olderrors) goto Lerr; //printf("t = %s\n", t.toChars()); inuse = 0; } } if (inuse) { error("recursive alias declaration"); Lerr: // Avoid breaking "recursive alias" state during errors gagged if (global.gag) return this; aliassym = new AliasDeclaration(loc, ident, Type.terror); type = Type.terror; return aliassym; } if (semanticRun >= PASS.semanticdone) { // semantic is already done. // Do not see aliassym !is null, because of lambda aliases. // Do not see type.deco !is null, even so "alias T = const int;` needs // semantic analysis to take the storage class `const` as type qualifier. } else { if (_import && _import._scope) { /* If this is an internal alias for selective/renamed import, * load the module first. */ _import.dsymbolSemantic(null); } if (_scope) { aliasSemantic(this, _scope); } } inuse = 1; Dsymbol s = aliassym ? aliassym.toAlias() : this; inuse = 0; return s; } override Dsymbol toAlias2() { if (inuse) { error("recursive alias declaration"); return this; } inuse = 1; Dsymbol s = aliassym ? aliassym.toAlias2() : this; inuse = 0; return s; } override bool isOverloadable() const { // assume overloadable until alias is resolved return semanticRun < PASS.semanticdone || aliassym && aliassym.isOverloadable(); } override inout(AliasDeclaration) isAliasDeclaration() inout { return this; } /** Returns: `true` if this instance was created to make a template parameter visible in the scope of a template body, `false` otherwise */ extern (D) bool isAliasedTemplateParameter() const { return !!(storage_class & STC.templateparameter); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class OverDeclaration : Declaration { Dsymbol overnext; // next in overload list Dsymbol aliassym; extern (D) this(Identifier ident, Dsymbol s) { super(ident); this.aliassym = s; } override const(char)* kind() const { return "overload alias"; // todo } override bool equals(const RootObject o) const { if (this == o) return true; auto s = isDsymbol(o); if (!s) return false; if (auto od2 = s.isOverDeclaration()) return this.aliassym.equals(od2.aliassym); return this.aliassym == s; } override bool overloadInsert(Dsymbol s) { //printf("OverDeclaration::overloadInsert('%s') aliassym = %p, overnext = %p\n", s.toChars(), aliassym, overnext); if (overnext) return overnext.overloadInsert(s); if (s == this) return true; overnext = s; return true; } override bool isOverloadable() const { return true; } Dsymbol isUnique() { Dsymbol result = null; overloadApply(aliassym, (Dsymbol s) { if (result) { result = null; return 1; // ambiguous, done } else { result = s; return 0; } }); return result; } override inout(OverDeclaration) isOverDeclaration() inout { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) class VarDeclaration : Declaration { Initializer _init; FuncDeclarations nestedrefs; // referenced by these lexically nested functions Dsymbol aliassym; // if redone as alias to another symbol VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection Expression edtor; // if !=null, does the destruction of the variable IntRange* range; // if !=null, the variable is known to be within the range VarDeclarations* maybes; // STC.maybescope variables that are assigned to this STC.maybescope variable uint endlinnum; // line number of end of scope that this var lives in uint offset; uint sequenceNumber; // order the variables are declared structalign_t alignment; // When interpreting, these point to the value (NULL if value not determinable) // The index of this variable on the CTFE stack, AdrOnStackNone if not allocated enum AdrOnStackNone = ~0u; uint ctfeAdrOnStack; // `bool` fields that are compacted into bit fields in a string mixin private extern (D) static struct BitFields { bool isargptr; /// if parameter that _argptr points to bool ctorinit; /// it has been initialized in a ctor bool iscatchvar; /// this is the exception object variable in catch() clause bool isowner; /// this is an Owner, despite it being `scope` bool setInCtorOnly; /// field can only be set in a constructor, as it is const or immutable /// It is a class that was allocated on the stack /// /// This means the var is not rebindable once assigned, /// and the destructor gets run when it goes out of scope bool onstack; bool overlapped; /// if it is a field and has overlapping bool overlapUnsafe; /// if it is an overlapping field and the overlaps are unsafe bool doNotInferScope; /// do not infer 'scope' for this variable bool doNotInferReturn; /// do not infer 'return' for this variable bool isArgDtorVar; /// temporary created to handle scope destruction of a function argument } import dmd.common.bitfields : generateBitFields; mixin(generateBitFields!(BitFields, ushort)); byte canassign; // it can be assigned to ubyte isdataseg; // private data for isDataseg 0 unset, 1 true, 2 false final extern (D) this(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) in { assert(ident); } do { //printf("VarDeclaration('%s')\n", ident.toChars()); super(loc, ident); debug { if (!type && !_init) { //printf("VarDeclaration('%s')\n", ident.toChars()); //*(char*)0=0; } } assert(type || _init); this.type = type; this._init = _init; ctfeAdrOnStack = AdrOnStackNone; this.storage_class = storage_class; } static VarDeclaration create(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) { return new VarDeclaration(loc, type, ident, _init, storage_class); } override VarDeclaration syntaxCopy(Dsymbol s) { //printf("VarDeclaration::syntaxCopy(%s)\n", toChars()); assert(!s); auto v = new VarDeclaration(loc, type ? type.syntaxCopy() : null, ident, _init ? _init.syntaxCopy() : null, storage_class); v.comment = comment; return v; } override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) { //printf("VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars()); if (aliassym) { // If this variable was really a tuple, set the offsets for the tuple fields TupleDeclaration v2 = aliassym.isTupleDeclaration(); assert(v2); for (size_t i = 0; i < v2.objects.dim; i++) { RootObject o = (*v2.objects)[i]; assert(o.dyncast() == DYNCAST.expression); Expression e = cast(Expression)o; assert(e.op == EXP.dSymbol); DsymbolExp se = e.isDsymbolExp(); se.s.setFieldOffset(ad, fieldState, isunion); } return; } if (!isField()) return; assert(!(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 (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.dim; i++) { if (ad.fields[i] == this) { // 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 = type.toBasetype(); if (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(loc)) { type = Type.terror; 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(this); if (t.ty == Terror) return; /* If coming after a bit field in progress, * advance past the field */ fieldState.inFlight = false; const sz = t.size(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 offset = AggregateDeclaration.placeField( &fieldState.offset, memsize, memalignsize, 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 const(char)* kind() const { return "variable"; } override final inout(AggregateDeclaration) isThis() inout { if (!(storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))) { /* The casting is necessary because `s = s.parent` is otherwise rejected */ for (auto s = cast(Dsymbol)this; s; s = s.parent) { auto ad = (cast(inout)s).isMember(); if (ad) return ad; if (!s.parent || !s.parent.isTemplateMixin()) break; } } return null; } override final bool needThis() { //printf("VarDeclaration::needThis(%s, x%x)\n", toChars(), storage_class); return isField(); } override final bool isExport() const { return visibility.kind == Visibility.Kind.export_; } override final bool isImportedSymbol() const { if (visibility.kind == Visibility.Kind.export_ && !_init && (storage_class & STC.static_ || parent.isModule())) return true; return false; } final bool isCtorinit() const pure nothrow @nogc @safe { return setInCtorOnly; } /******************************* * Does symbol go into data segment? * Includes extern variables. */ override final bool isDataseg() { version (none) { printf("VarDeclaration::isDataseg(%p, '%s')\n", this, toChars()); printf("%llx, isModule: %p, isTemplateInstance: %p, isNspace: %p\n", storage_class & (STC.static_ | STC.const_), parent.isModule(), parent.isTemplateInstance(), parent.isNspace()); printf("parent = '%s'\n", parent.toChars()); } if (isdataseg == 0) // the value is not cached { isdataseg = 2; // The Variables does not go into the datasegment if (!canTakeAddressOf()) { return false; } Dsymbol parent = toParent(); if (!parent && !(storage_class & STC.static_)) { error("forward referenced"); type = Type.terror; } else if (storage_class & (STC.static_ | STC.extern_ | STC.gshared) || parent.isModule() || parent.isTemplateInstance() || parent.isNspace()) { assert(!isParameter() && !isResult()); isdataseg = 1; // It is in the DataSegment } } return (isdataseg == 1); } /************************************ * Does symbol go into thread local storage? */ override final bool isThreadlocal() { //printf("VarDeclaration::isThreadlocal(%p, '%s')\n", this, toChars()); /* Data defaults to being thread-local. It is not thread-local * if it is immutable, const or shared. */ bool i = isDataseg() && !(storage_class & (STC.immutable_ | STC.const_ | STC.shared_ | STC.gshared)); //printf("\treturn %d\n", i); return i; } /******************************************** * Can variable be read and written by CTFE? */ final bool isCTFE() { return (storage_class & STC.ctfe) != 0; // || !isDataseg(); } final bool isOverlappedWith(VarDeclaration v) { const vsz = v.type.size(); const tsz = type.size(); assert(vsz != SIZE_INVALID && tsz != SIZE_INVALID); // Overlap is checked by comparing bit offsets auto bitoffset = offset * 8; auto vbitoffset = v.offset * 8; // Bitsize of types are overridden by any bit-field widths. ulong tbitsize = void; if (auto bf = isBitFieldDeclaration()) { bitoffset += bf.bitOffset; tbitsize = bf.fieldWidth; } else tbitsize = tsz * 8; ulong vbitsize = void; if (auto vbf = v.isBitFieldDeclaration()) { vbitoffset += vbf.bitOffset; vbitsize = vbf.fieldWidth; } else vbitsize = vsz * 8; return bitoffset < vbitoffset + vbitsize && vbitoffset < bitoffset + tbitsize; } override final bool hasPointers() { //printf("VarDeclaration::hasPointers() %s, ty = %d\n", toChars(), type.ty); return (!isDataseg() && type.hasPointers()); } /************************************* * Return true if we can take the address of this variable. */ final bool canTakeAddressOf() { return !(storage_class & STC.manifest); } /****************************************** * Return true if variable needs to call the destructor. */ final bool needsScopeDtor() { //printf("VarDeclaration::needsScopeDtor() %s\n", toChars()); return edtor && !(storage_class & STC.nodtor); } /****************************************** * If a variable has a scope destructor call, return call for it. * Otherwise, return NULL. */ extern (D) final Expression callScopeDtor(Scope* sc) { //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); // Destruction of STC.field's is handled by buildDtor() if (storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field)) { return null; } if (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 = type.baseElemOf(); if (tv.ty == Tstruct) { StructDeclaration sd = (cast(TypeStruct)tv).sym; if (!sd.dtor || sd.errors) return null; const sz = type.size(); assert(sz != SIZE_INVALID); if (!sz) return null; if (type.toBasetype().ty == Tstruct) { // v.__xdtor() e = new VarExp(loc, this); /* 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(loc, e, sd.dtor, false); e = new CallExp(loc, e); } else { // __ArrayDtor(v[0 .. n]) e = new VarExp(loc, this); const sdsz = sd.type.size(); assert(sdsz != SIZE_INVALID && sdsz != 0); const n = sz / sdsz; SliceExp se = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), new IntegerExp(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(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); } return e; } // Destructors for classes if (storage_class & (STC.auto_ | STC.scope_) && !(storage_class & STC.parameter)) { for (ClassDeclaration cd = 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 (onstack) // if any destructors { // 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(loc, this); e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances e = new DotVarExp(loc, e, cd.dtor, false); e = new CallExp(loc, e); break; } // delete this; Expression ec; ec = new VarExp(loc, this); e = new DeleteExp(loc, ec, true); e.type = Type.tvoid; break; } } } return e; } /******************************************* * If variable has a constant expression initializer, get it. * Otherwise, return null. */ extern (D) final Expression getConstInitializer(bool needFullType = true) { assert(type && _init); // Ungag errors when not speculative uint oldgag = global.gag; if (global.gag) { Dsymbol sym = toParent().isAggregateDeclaration(); if (sym && !sym.isSpeculative()) global.gag = 0; } if (_scope) { inuse++; _init = _init.initializerSemantic(_scope, type, INITinterpret); _scope = null; inuse--; } Expression e = _init.initializerToExpression(needFullType ? type : null); global.gag = oldgag; return e; } /******************************************* * Helper function for the expansion of manifest constant. */ extern (D) final Expression expandInitializer(Loc loc) { assert((storage_class & STC.manifest) && _init); auto e = getConstInitializer(); if (!e) { .error(loc, "cannot make expression out of initializer for `%s`", toChars()); return ErrorExp.get(); } e = e.copy(); e.loc = loc; // for better error message return e; } override final void checkCtorConstInit() { version (none) { /* doesn't work if more than one static ctor */ if (ctorinit == 0 && isCtorinit() && !isField()) error("missing initializer in static constructor for const variable"); } } /************************************ * Check to see if this variable is actually in an enclosing function * rather than the current one. * Update nestedrefs[], closureVars[] and outerVars[]. * Returns: true if error occurs. */ extern (D) final bool checkNestedReference(Scope* sc, Loc loc) { //printf("VarDeclaration::checkNestedReference() %s\n", toChars()); if (sc.intypeof == 1 || (sc.flags & SCOPE.ctfe)) return false; if (!parent || parent == sc.parent) return false; if (isDataseg() || (storage_class & STC.manifest)) return false; // The current function FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); if (!fdthis) return false; // out of function scope Dsymbol p = toParent2(); // Function literals from fdthis to p must be delegates ensureStaticLinkTo(fdthis, p); // The function that this variable is in FuncDeclaration fdv = p.isFuncDeclaration(); if (!fdv || fdv == fdthis) return false; // Add fdthis to nestedrefs[] if not already there if (!nestedrefs.contains(fdthis)) nestedrefs.push(fdthis); //printf("\tfdv = %s\n", fdv.toChars()); //printf("\tfdthis = %s\n", fdthis.toChars()); if (loc.isValid()) { if (fdthis.getLevelAndCheck(loc, sc, fdv, this) == fdthis.LevelError) return true; } // Add this VarDeclaration to fdv.closureVars[] if not already there if (!sc.intypeof && !(sc.flags & SCOPE.compile) && // https://issues.dlang.org/show_bug.cgi?id=17605 (fdv.flags & FUNCFLAG.compileTimeOnly || !(fdthis.flags & FUNCFLAG.compileTimeOnly)) ) { if (!fdv.closureVars.contains(this)) fdv.closureVars.push(this); } if (!fdthis.outerVars.contains(this)) fdthis.outerVars.push(this); //printf("fdthis is %s\n", fdthis.toChars()); //printf("var %s in function %s is nested ref\n", toChars(), fdv.toChars()); // __dollar creates problems because it isn't a real variable // https://issues.dlang.org/show_bug.cgi?id=3326 if (ident == Id.dollar) { .error(loc, "cannnot use `$` inside a function literal"); return true; } if (ident == Id.withSym) // https://issues.dlang.org/show_bug.cgi?id=1759 { ExpInitializer ez = _init.isExpInitializer(); assert(ez); Expression e = ez.exp; if (e.op == EXP.construct || e.op == EXP.blit) e = (cast(AssignExp)e).e2; return lambdaCheckForNestedRef(e, sc); } return false; } override final Dsymbol toAlias() { //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym); if ((!type || !type.deco) && _scope) dsymbolSemantic(this, _scope); assert(this != aliassym); Dsymbol s = aliassym ? aliassym.toAlias() : this; return s; } // Eliminate need for dynamic_cast override final inout(VarDeclaration) isVarDeclaration() inout { return this; } override void accept(Visitor v) { v.visit(this); } } /******************************************************* * C11 6.7.2.1-4 bit fields */ extern (C++) class BitFieldDeclaration : VarDeclaration { Expression width; uint fieldWidth; uint bitOffset; final extern (D) this(const ref Loc loc, Type type, Identifier ident, Expression width) { super(loc, type, ident, null); this.width = width; this.storage_class |= STC.field; } override BitFieldDeclaration syntaxCopy(Dsymbol s) { //printf("BitFieldDeclaration::syntaxCopy(%s)\n", toChars()); assert(!s); auto bf = new BitFieldDeclaration(loc, type ? type.syntaxCopy() : null, ident, width.syntaxCopy()); bf.comment = comment; return bf; } override final inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return this; } override void accept(Visitor v) { v.visit(this); } override final void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) { //printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), toChars()); static void print(const ref FieldState fieldState) { printf("FieldState.offset = %d bytes\n", fieldState.offset); printf(" .fieldOffset = %d bytes\n", fieldState.fieldOffset); printf(" .bitOffset = %d bits\n", fieldState.bitOffset); printf(" .fieldSize = %d bytes\n", fieldState.fieldSize); printf(" .inFlight = %d\n\n", fieldState.inFlight); } //print(fieldState); Type t = type.toBasetype(); const bool anon = 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(this); if (t.ty == Terror) return; const sz = t.size(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 (fieldWidth == 0 && !anon) error(loc, "named bit fields cannot have 0 width"); if (fieldWidth > memsize * 8) error(loc, "bit field width %d is larger than type", fieldWidth); const style = target.c.bitFieldStyle; void startNewField() { uint alignsize; if (style == TargetC.BitFieldStyle.Gcc_Clang) { if (fieldWidth > 32) alignsize = memalignsize; else if (fieldWidth > 16) alignsize = 4; else if (fieldWidth > 8) alignsize = 2; else alignsize = 1; } else alignsize = memsize; // not memalignsize uint dummy; offset = AggregateDeclaration.placeField( &fieldState.offset, memsize, alignsize, alignment, &ad.structsize, (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? &dummy : &ad.alignsize, isunion); fieldState.inFlight = true; fieldState.fieldOffset = offset; fieldState.bitOffset = 0; fieldState.fieldSize = memsize; } if (style == TargetC.BitFieldStyle.Gcc_Clang) { if (fieldWidth == 0) { if (!isunion) { // Use type of zero width field to align to next field fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); ad.structsize = fieldState.offset; } fieldState.inFlight = false; return; } if (ad.alignsize == 0) ad.alignsize = 1; if (!anon && ad.alignsize < memalignsize) ad.alignsize = memalignsize; } else if (style == TargetC.BitFieldStyle.MS) { if (ad.alignsize == 0) ad.alignsize = 1; if (fieldWidth == 0) { if (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; } } else if (style == TargetC.BitFieldStyle.DM) { if (anon && fieldWidth && (!fieldState.inFlight || fieldState.bitOffset == 0)) return; // this probably should be a bug in DMC if (ad.alignsize == 0) ad.alignsize = 1; if (fieldWidth == 0) { if (fieldState.inFlight && !isunion) { const alsz = memsize; fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); ad.structsize = fieldState.offset; } fieldState.inFlight = false; return; } } if (!fieldState.inFlight) { startNewField(); } else if (style == TargetC.BitFieldStyle.Gcc_Clang) { if (fieldState.bitOffset + fieldWidth > memsize * 8) { //printf("start1 fieldState.bitOffset:%u fieldWidth:%u memsize:%u\n", fieldState.bitOffset, fieldWidth, memsize); startNewField(); } else { // if alignment boundary is crossed uint start = fieldState.fieldOffset * 8 + fieldState.bitOffset; uint end = start + fieldWidth; //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) { //printf("alignment is crossed\n"); startNewField(); } } } else if (style == TargetC.BitFieldStyle.DM || style == TargetC.BitFieldStyle.MS) { if (memsize != fieldState.fieldSize || fieldState.bitOffset + fieldWidth > fieldState.fieldSize * 8) { startNewField(); } } else assert(0); offset = fieldState.fieldOffset; bitOffset = fieldState.bitOffset; const pastField = bitOffset + fieldWidth; if (style == TargetC.BitFieldStyle.Gcc_Clang) { auto size = (pastField + 7) / 8; fieldState.fieldSize = size; //printf(" offset: %d, size: %d\n", offset, size); ad.structsize = offset + size; } else fieldState.fieldSize = memsize; //printf("at end: ad.structsize = %d\n", cast(int)ad.structsize); //print(fieldState); if (!isunion) { fieldState.offset = offset + fieldState.fieldSize; fieldState.bitOffset = pastField; } //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); } } /*********************************************************** * This is a shell around a back end symbol */ extern (C++) final class SymbolDeclaration : Declaration { AggregateDeclaration dsym; extern (D) this(const ref Loc loc, AggregateDeclaration dsym) { super(loc, dsym.ident); this.dsym = dsym; storage_class |= STC.const_; } // Eliminate need for dynamic_cast override inout(SymbolDeclaration) isSymbolDeclaration() inout { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) class TypeInfoDeclaration : VarDeclaration { Type tinfo; final extern (D) this(Type tinfo) { super(Loc.initial, Type.dtypeinfo.type, tinfo.getTypeInfoIdent(), null); this.tinfo = tinfo; storage_class = STC.static_ | STC.gshared; visibility = Visibility(Visibility.Kind.public_); _linkage = LINK.c; alignment.set(target.ptrsize); } static TypeInfoDeclaration create(Type tinfo) { return new TypeInfoDeclaration(tinfo); } override final TypeInfoDeclaration syntaxCopy(Dsymbol s) { assert(0); // should never be produced by syntax } override final const(char)* toChars() const { //printf("TypeInfoDeclaration::toChars() tinfo = %s\n", tinfo.toChars()); OutBuffer buf; buf.writestring("typeid("); buf.writestring(tinfo.toChars()); buf.writeByte(')'); return buf.extractChars(); } override final inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout @nogc nothrow pure @safe { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoStructDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfostruct) { ObjectNotFound(Id.TypeInfo_Struct); } type = Type.typeinfostruct.type; } static TypeInfoStructDeclaration create(Type tinfo) { return new TypeInfoStructDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoClassDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoclass) { ObjectNotFound(Id.TypeInfo_Class); } type = Type.typeinfoclass.type; } static TypeInfoClassDeclaration create(Type tinfo) { return new TypeInfoClassDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoInterfaceDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfointerface) { ObjectNotFound(Id.TypeInfo_Interface); } type = Type.typeinfointerface.type; } static TypeInfoInterfaceDeclaration create(Type tinfo) { return new TypeInfoInterfaceDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoPointerDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfopointer) { ObjectNotFound(Id.TypeInfo_Pointer); } type = Type.typeinfopointer.type; } static TypeInfoPointerDeclaration create(Type tinfo) { return new TypeInfoPointerDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoArrayDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoarray) { ObjectNotFound(Id.TypeInfo_Array); } type = Type.typeinfoarray.type; } static TypeInfoArrayDeclaration create(Type tinfo) { return new TypeInfoArrayDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfostaticarray) { ObjectNotFound(Id.TypeInfo_StaticArray); } type = Type.typeinfostaticarray.type; } static TypeInfoStaticArrayDeclaration create(Type tinfo) { return new TypeInfoStaticArrayDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoassociativearray) { ObjectNotFound(Id.TypeInfo_AssociativeArray); } type = Type.typeinfoassociativearray.type; } static TypeInfoAssociativeArrayDeclaration create(Type tinfo) { return new TypeInfoAssociativeArrayDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoEnumDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoenum) { ObjectNotFound(Id.TypeInfo_Enum); } type = Type.typeinfoenum.type; } static TypeInfoEnumDeclaration create(Type tinfo) { return new TypeInfoEnumDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoFunctionDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfofunction) { ObjectNotFound(Id.TypeInfo_Function); } type = Type.typeinfofunction.type; } static TypeInfoFunctionDeclaration create(Type tinfo) { return new TypeInfoFunctionDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoDelegateDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfodelegate) { ObjectNotFound(Id.TypeInfo_Delegate); } type = Type.typeinfodelegate.type; } static TypeInfoDelegateDeclaration create(Type tinfo) { return new TypeInfoDelegateDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoTupleDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfotypelist) { ObjectNotFound(Id.TypeInfo_Tuple); } type = Type.typeinfotypelist.type; } static TypeInfoTupleDeclaration create(Type tinfo) { return new TypeInfoTupleDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoConstDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoconst) { ObjectNotFound(Id.TypeInfo_Const); } type = Type.typeinfoconst.type; } static TypeInfoConstDeclaration create(Type tinfo) { return new TypeInfoConstDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoInvariantDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoinvariant) { ObjectNotFound(Id.TypeInfo_Invariant); } type = Type.typeinfoinvariant.type; } static TypeInfoInvariantDeclaration create(Type tinfo) { return new TypeInfoInvariantDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoSharedDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoshared) { ObjectNotFound(Id.TypeInfo_Shared); } type = Type.typeinfoshared.type; } static TypeInfoSharedDeclaration create(Type tinfo) { return new TypeInfoSharedDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoWildDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfowild) { ObjectNotFound(Id.TypeInfo_Wild); } type = Type.typeinfowild.type; } static TypeInfoWildDeclaration create(Type tinfo) { return new TypeInfoWildDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration { extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfovector) { ObjectNotFound(Id.TypeInfo_Vector); } type = Type.typeinfovector.type; } static TypeInfoVectorDeclaration create(Type tinfo) { return new TypeInfoVectorDeclaration(tinfo); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * For the "this" parameter to member functions */ extern (C++) final class ThisDeclaration : VarDeclaration { extern (D) this(const ref Loc loc, Type t) { super(loc, t, Id.This, null); storage_class |= STC.nodtor; } override ThisDeclaration syntaxCopy(Dsymbol s) { assert(0); // should never be produced by syntax } override inout(ThisDeclaration) isThisDeclaration() inout { return this; } override void accept(Visitor v) { v.visit(this); } }