| /** | |
| * 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_; | |
| 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; | |
| } | |
| 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); | |
| } | |
| } |