| /** |
| * Defines a `Dsymbol` representing an aggregate, which is a `struct`, `union` or `class`. |
| * |
| * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions), |
| * $(LINK2 https://dlang.org/spec/class.html, Class). |
| * |
| * 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/aggregate.d, _aggregate.d) |
| * Documentation: https://dlang.org/phobos/dmd_aggregate.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aggregate.d |
| */ |
| |
| module dmd.aggregate; |
| |
| import core.stdc.stdio; |
| import core.checkedint; |
| |
| import dmd.aliasthis; |
| import dmd.apply; |
| import dmd.arraytypes; |
| import dmd.astenums; |
| import dmd.attrib; |
| import dmd.declaration; |
| 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.id; |
| import dmd.identifier; |
| import dmd.mtype; |
| import dmd.tokens; |
| import dmd.typesem : defaultInit; |
| import dmd.visitor; |
| |
| /** |
| * The ClassKind enum is used in AggregateDeclaration AST nodes to |
| * specify the linkage type of the struct/class/interface or if it |
| * is an anonymous class. If the class is anonymous it is also |
| * considered to be a D class. |
| */ |
| enum ClassKind : ubyte |
| { |
| /// the aggregate is a d(efault) class |
| d, |
| /// the aggregate is a C++ struct/class/interface |
| cpp, |
| /// the aggregate is an Objective-C class/interface |
| objc, |
| /// the aggregate is a C struct |
| c, |
| } |
| |
| /** |
| * Give a nice string for a class kind for error messages |
| * Params: |
| * c = class kind |
| * Returns: |
| * 0-terminated string for `c` |
| */ |
| const(char)* toChars(ClassKind c) |
| { |
| final switch (c) |
| { |
| case ClassKind.d: |
| return "D"; |
| case ClassKind.cpp: |
| return "C++"; |
| case ClassKind.objc: |
| return "Objective-C"; |
| case ClassKind.c: |
| return "C"; |
| } |
| } |
| |
| /** |
| * If an aggregate has a pargma(mangle, ...) this holds the information |
| * to mangle. |
| */ |
| struct MangleOverride |
| { |
| Dsymbol agg; // The symbol to copy template parameters from (if any) |
| Identifier id; // the name to override the aggregate's with, defaults to agg.ident |
| } |
| |
| /*********************************************************** |
| * Abstract aggregate as a common ancestor for Class- and StructDeclaration. |
| */ |
| extern (C++) abstract class AggregateDeclaration : ScopeDsymbol |
| { |
| Type type; /// |
| StorageClass storage_class; /// |
| uint structsize; /// size of struct |
| uint alignsize; /// size of struct for alignment purposes |
| VarDeclarations fields; /// VarDeclaration fields |
| Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol |
| |
| /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface |
| ClassKind classKind; |
| /// Specify whether to mangle the aggregate as a `class` or a `struct` |
| /// This information is used by the MSVC mangler |
| /// Only valid for class and struct. TODO: Merge with ClassKind ? |
| CPPMANGLE cppmangle; |
| |
| /// overridden symbol with pragma(mangle, "...") if not null |
| MangleOverride* pMangleOverride; |
| |
| /** |
| * !=null if is nested |
| * pointing to the dsymbol that directly enclosing it. |
| * 1. The function that enclosing it (nested struct and class) |
| * 2. The class that enclosing it (nested class only) |
| * 3. If enclosing aggregate is template, its enclosing dsymbol. |
| * |
| * See AggregateDeclaraton::makeNested for the details. |
| */ |
| Dsymbol enclosing; |
| |
| VarDeclaration vthis; /// 'this' parameter if this aggregate is nested |
| VarDeclaration vthis2; /// 'this' parameter if this aggregate is a template and is nested |
| |
| // Special member functions |
| FuncDeclarations invs; /// Array of invariants |
| FuncDeclaration inv; /// Merged invariant calling all members of invs |
| |
| /// CtorDeclaration or TemplateDeclaration |
| Dsymbol ctor; |
| |
| /// default constructor - should have no arguments, because |
| /// it would be stored in TypeInfo_Class.defaultConstructor |
| CtorDeclaration defaultCtor; |
| |
| AliasThis aliasthis; /// forward unresolved lookups to aliasthis |
| |
| DtorDeclarations userDtors; /// user-defined destructors (`~this()`) - mixins can yield multiple ones |
| DtorDeclaration aggrDtor; /// aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes) |
| DtorDeclaration dtor; /// the aggregate destructor exposed as `__xdtor` alias |
| /// (same as aggrDtor, except for C++ classes with virtual dtor on Windows) |
| DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI) |
| DtorDeclaration fieldDtor; /// function destructing (non-inherited) fields |
| |
| Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this) |
| |
| /// |
| Visibility visibility; |
| bool noDefaultCtor; /// no default construction |
| bool disableNew; /// disallow allocations using `new` |
| Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data |
| |
| final extern (D) this(const ref Loc loc, Identifier id) |
| { |
| super(loc, id); |
| visibility = Visibility(Visibility.Kind.public_); |
| } |
| |
| /*************************************** |
| * Create a new scope from sc. |
| * semantic, semantic2 and semantic3 will use this for aggregate members. |
| */ |
| Scope* newScope(Scope* sc) |
| { |
| auto sc2 = sc.push(this); |
| sc2.stc &= STC.flowThruAggregate; |
| sc2.parent = this; |
| sc2.inunion = isUnionDeclaration(); |
| sc2.visibility = Visibility(Visibility.Kind.public_); |
| sc2.explicitVisibility = 0; |
| sc2.aligndecl = null; |
| sc2.userAttribDecl = null; |
| sc2.namespace = null; |
| return sc2; |
| } |
| |
| override final void setScope(Scope* sc) |
| { |
| // Might need a scope to resolve forward references. The check for |
| // semanticRun prevents unnecessary setting of _scope during deferred |
| // setScope phases for aggregates which already finished semantic(). |
| // See https://issues.dlang.org/show_bug.cgi?id=16607 |
| if (semanticRun < PASS.semanticdone) |
| ScopeDsymbol.setScope(sc); |
| } |
| |
| /*************************************** |
| * Returns: |
| * The total number of fields minus the number of hidden fields. |
| */ |
| final size_t nonHiddenFields() |
| { |
| return fields.dim - isNested() - (vthis2 !is null); |
| } |
| |
| /*************************************** |
| * Collect all instance fields, then determine instance size. |
| * Returns: |
| * false if failed to determine the size. |
| */ |
| final bool determineSize(const ref Loc loc) |
| { |
| //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); |
| |
| // The previous instance size finalizing had: |
| if (type.ty == Terror) |
| return false; // failed already |
| if (sizeok == Sizeok.done) |
| return true; // succeeded |
| |
| if (!members) |
| { |
| error(loc, "unknown size"); |
| return false; |
| } |
| |
| if (_scope) |
| dsymbolSemantic(this, null); |
| |
| // Determine the instance size of base class first. |
| if (auto cd = isClassDeclaration()) |
| { |
| cd = cd.baseClass; |
| if (cd && !cd.determineSize(loc)) |
| goto Lfail; |
| } |
| |
| // Determine instance fields when sizeok == Sizeok.none |
| if (!this.determineFields()) |
| goto Lfail; |
| if (sizeok != Sizeok.done) |
| finalizeSize(); |
| |
| // this aggregate type has: |
| if (type.ty == Terror) |
| return false; // marked as invalid during the finalizing. |
| if (sizeok == Sizeok.done) |
| return true; // succeeded to calculate instance size. |
| |
| Lfail: |
| // There's unresolvable forward reference. |
| if (type != Type.terror) |
| error(loc, "no size because of forward reference"); |
| // Don't cache errors from speculative semantic, might be resolvable later. |
| // https://issues.dlang.org/show_bug.cgi?id=16574 |
| if (!global.gag) |
| { |
| type = Type.terror; |
| errors = true; |
| } |
| return false; |
| } |
| |
| abstract void finalizeSize(); |
| |
| override final uinteger_t size(const ref Loc loc) |
| { |
| //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); |
| bool ok = determineSize(loc); |
| //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); |
| return ok ? structsize : SIZE_INVALID; |
| } |
| |
| /*************************************** |
| * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit |
| * field initializers have unique memory space on instance. |
| * Returns: |
| * true if any errors happen. |
| */ |
| extern (D) final bool checkOverlappedFields() |
| { |
| //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars()); |
| assert(sizeok == Sizeok.done); |
| size_t nfields = fields.dim; |
| if (isNested()) |
| { |
| auto cd = isClassDeclaration(); |
| if (!cd || !cd.baseClass || !cd.baseClass.isNested()) |
| nfields--; |
| if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2)) |
| nfields--; |
| } |
| bool errors = false; |
| |
| // Fill in missing any elements with default initializers |
| foreach (i; 0 .. nfields) |
| { |
| auto vd = fields[i]; |
| if (vd.errors) |
| { |
| errors = true; |
| continue; |
| } |
| |
| const vdIsVoidInit = vd._init && vd._init.isVoidInitializer(); |
| |
| // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. |
| foreach (j; 0 .. nfields) |
| { |
| if (i == j) |
| continue; |
| auto v2 = fields[j]; |
| if (v2.errors) |
| { |
| errors = true; |
| continue; |
| } |
| if (!vd.isOverlappedWith(v2)) |
| continue; |
| |
| // vd and v2 are overlapping. |
| vd.overlapped = true; |
| v2.overlapped = true; |
| |
| if (!MODimplicitConv(vd.type.mod, v2.type.mod)) |
| v2.overlapUnsafe = true; |
| if (!MODimplicitConv(v2.type.mod, vd.type.mod)) |
| vd.overlapUnsafe = true; |
| |
| if (i > j) |
| continue; |
| |
| if (!v2._init) |
| continue; |
| |
| if (v2._init.isVoidInitializer()) |
| continue; |
| |
| if (vd._init && !vdIsVoidInit && v2._init) |
| { |
| .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); |
| errors = true; |
| } |
| else if (v2._init && i < j) |
| { |
| .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`", |
| v2.toChars(), v2._init.toChars(), vd.toChars()); |
| errors = true; |
| } |
| } |
| } |
| return errors; |
| } |
| |
| /*************************************** |
| * Fill out remainder of elements[] with default initializers for fields[]. |
| * Params: |
| * loc = location |
| * elements = explicit arguments which given to construct object. |
| * ctorinit = true if the elements will be used for default initialization. |
| * Returns: |
| * false if any errors occur. |
| * Otherwise, returns true and the missing arguments will be pushed in elements[]. |
| */ |
| final bool fill(const ref Loc loc, ref Expressions elements, bool ctorinit) |
| { |
| //printf("AggregateDeclaration::fill() %s\n", toChars()); |
| assert(sizeok == Sizeok.done); |
| const nfields = nonHiddenFields(); |
| bool errors = false; |
| |
| size_t dim = elements.dim; |
| elements.setDim(nfields); |
| foreach (size_t i; dim .. nfields) |
| elements[i] = null; |
| |
| // Fill in missing any elements with default initializers |
| foreach (i; 0 .. nfields) |
| { |
| if (elements[i]) |
| continue; |
| |
| auto vd = fields[i]; |
| auto vx = vd; |
| if (vd._init && vd._init.isVoidInitializer()) |
| vx = null; |
| |
| // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. |
| size_t fieldi = i; |
| foreach (j; 0 .. nfields) |
| { |
| if (i == j) |
| continue; |
| auto v2 = fields[j]; |
| if (!vd.isOverlappedWith(v2)) |
| continue; |
| |
| if (elements[j]) |
| { |
| vx = null; |
| break; |
| } |
| if (v2._init && v2._init.isVoidInitializer()) |
| continue; |
| |
| version (all) |
| { |
| /* Prefer first found non-void-initialized field |
| * union U { int a; int b = 2; } |
| * U u; // Error: overlapping initialization for field a and b |
| */ |
| if (!vx) |
| { |
| vx = v2; |
| fieldi = j; |
| } |
| else if (v2._init) |
| { |
| .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); |
| errors = true; |
| } |
| } |
| else |
| { |
| // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always |
| |
| /* Prefer explicitly initialized field |
| * union U { int a; int b = 2; } |
| * U u; // OK (u.b == 2) |
| */ |
| if (!vx || !vx._init && v2._init) |
| { |
| vx = v2; |
| fieldi = j; |
| } |
| else if (vx != vd && !vx.isOverlappedWith(v2)) |
| { |
| // Both vx and v2 fills vd, but vx and v2 does not overlap |
| } |
| else if (vx._init && v2._init) |
| { |
| .error(loc, "overlapping default initialization for field `%s` and `%s`", |
| v2.toChars(), vd.toChars()); |
| errors = true; |
| } |
| else |
| assert(vx._init || !vx._init && !v2._init); |
| } |
| } |
| if (vx) |
| { |
| Expression e; |
| if (vx.type.size() == 0) |
| { |
| e = null; |
| } |
| else if (vx._init) |
| { |
| assert(!vx._init.isVoidInitializer()); |
| if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 |
| { |
| vx.error(loc, "recursive initialization of field"); |
| errors = true; |
| } |
| else |
| e = vx.getConstInitializer(false); |
| } |
| else |
| { |
| if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) |
| { |
| .error(loc, "field `%s.%s` must be initialized because it has no default constructor", |
| type.toChars(), vx.toChars()); |
| errors = true; |
| } |
| /* https://issues.dlang.org/show_bug.cgi?id=12509 |
| * Get the element of static array type. |
| */ |
| Type telem = vx.type; |
| if (telem.ty == Tsarray) |
| { |
| /* We cannot use Type::baseElemOf() here. |
| * If the bottom of the Tsarray is an enum type, baseElemOf() |
| * will return the base of the enum, and its default initializer |
| * would be different from the enum's. |
| */ |
| TypeSArray tsa; |
| while ((tsa = telem.toBasetype().isTypeSArray()) !is null) |
| telem = tsa.next; |
| if (telem.ty == Tvoid) |
| telem = Type.tuns8.addMod(telem.mod); |
| } |
| if (telem.needsNested() && ctorinit) |
| e = telem.defaultInit(loc); |
| else |
| e = telem.defaultInitLiteral(loc); |
| } |
| elements[fieldi] = e; |
| } |
| } |
| foreach (e; elements) |
| { |
| if (e && e.op == EXP.error) |
| return false; |
| } |
| |
| return !errors; |
| } |
| |
| /**************************** |
| * Do byte or word alignment as necessary. |
| * Align sizes of 0, as we may not know array sizes yet. |
| * Params: |
| * alignment = struct alignment that is in effect |
| * memalignsize = natural alignment of field |
| * poffset = pointer to offset to be aligned |
| */ |
| extern (D) static void alignmember(structalign_t alignment, uint memalignsize, uint* poffset) pure nothrow @safe |
| { |
| //debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, *poffset); |
| uint alignvalue; |
| |
| if (alignment.isDefault()) |
| { |
| // Alignment in Target::fieldalignsize must match what the |
| // corresponding C compiler's default alignment behavior is. |
| alignvalue = memalignsize; |
| } |
| else if (alignment.isPack()) // #pragma pack semantics |
| { |
| alignvalue = alignment.get(); |
| if (memalignsize < alignvalue) |
| alignvalue = memalignsize; // align to min(memalignsize, alignment) |
| } |
| else if (alignment.get() > 1) |
| { |
| // Align on alignment boundary, which must be a positive power of 2 |
| alignvalue = alignment.get(); |
| } |
| else |
| return; |
| |
| assert(alignvalue > 0 && !(alignvalue & (alignvalue - 1))); |
| *poffset = (*poffset + alignvalue - 1) & ~(alignvalue - 1); |
| } |
| |
| /**************************************** |
| * Place a field (mem) into an aggregate (agg), which can be a struct, union or class |
| * Params: |
| * nextoffset = location just past the end of the previous field in the aggregate. |
| * Updated to be just past the end of this field to be placed, i.e. the future nextoffset |
| * memsize = size of field |
| * memalignsize = natural alignment of field |
| * alignment = alignment in effect for this field |
| * paggsize = size of aggregate (updated) |
| * paggalignsize = alignment of aggregate (updated) |
| * isunion = the aggregate is a union |
| * Returns: |
| * aligned offset to place field at |
| * |
| */ |
| extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize, |
| structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion) |
| { |
| uint ofs = *nextoffset; |
| |
| const uint actualAlignment = |
| alignment.isDefault() || alignment.isPack() && memalignsize < alignment.get() |
| ? memalignsize : alignment.get(); |
| |
| // Ensure no overflow |
| bool overflow; |
| const sz = addu(memsize, actualAlignment, overflow); |
| addu(ofs, sz, overflow); |
| if (overflow) assert(0); |
| |
| // Skip no-op for noreturn without custom aligment |
| if (memalignsize != 0 || !alignment.isDefault()) |
| alignmember(alignment, memalignsize, &ofs); |
| |
| uint memoffset = ofs; |
| ofs += memsize; |
| if (ofs > *paggsize) |
| *paggsize = ofs; |
| if (!isunion) |
| *nextoffset = ofs; |
| |
| if (*paggalignsize < actualAlignment) |
| *paggalignsize = actualAlignment; |
| |
| return memoffset; |
| } |
| |
| override final Type getType() |
| { |
| /* Apply storage classes to forward references. (Issue 22254) |
| * Note: Avoid interfaces for now. Implementing qualifiers on interface |
| * definitions exposed some issues in their TypeInfo generation in DMD. |
| * Related PR: https://github.com/dlang/dmd/pull/13312 |
| */ |
| if (semanticRun == PASS.initial && !isInterfaceDeclaration()) |
| { |
| auto stc = storage_class; |
| if (_scope) |
| stc |= _scope.stc; |
| type = type.addSTC(stc); |
| } |
| return type; |
| } |
| |
| // is aggregate deprecated? |
| override final bool isDeprecated() const |
| { |
| return !!(this.storage_class & STC.deprecated_); |
| } |
| |
| /// Flag this aggregate as deprecated |
| final void setDeprecated() |
| { |
| this.storage_class |= STC.deprecated_; |
| } |
| |
| /**************************************** |
| * Returns true if there's an extra member which is the 'this' |
| * pointer to the enclosing context (enclosing aggregate or function) |
| */ |
| final bool isNested() const |
| { |
| return enclosing !is null; |
| } |
| |
| /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested. |
| */ |
| extern (D) final void makeNested() |
| { |
| if (enclosing) // if already nested |
| return; |
| if (sizeok == Sizeok.done) |
| return; |
| if (isUnionDeclaration() || isInterfaceDeclaration()) |
| return; |
| if (storage_class & STC.static_) |
| return; |
| |
| // If nested struct, add in hidden 'this' pointer to outer scope |
| auto s = toParentLocal(); |
| if (!s) |
| s = toParent2(); |
| if (!s) |
| return; |
| Type t = null; |
| if (auto fd = s.isFuncDeclaration()) |
| { |
| enclosing = fd; |
| |
| /* https://issues.dlang.org/show_bug.cgi?id=14422 |
| * If a nested class parent is a function, its |
| * context pointer (== `outer`) should be void* always. |
| */ |
| t = Type.tvoidptr; |
| } |
| else if (auto ad = s.isAggregateDeclaration()) |
| { |
| if (isClassDeclaration() && ad.isClassDeclaration()) |
| { |
| enclosing = ad; |
| } |
| else if (isStructDeclaration()) |
| { |
| if (auto ti = ad.parent.isTemplateInstance()) |
| { |
| enclosing = ti.enclosing; |
| } |
| } |
| t = ad.handleType(); |
| } |
| if (enclosing) |
| { |
| //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars()); |
| assert(t); |
| if (t.ty == Tstruct) |
| t = Type.tvoidptr; // t should not be a ref type |
| |
| assert(!vthis); |
| vthis = new ThisDeclaration(loc, t); |
| //vthis.storage_class |= STC.ref_; |
| |
| // Emulate vthis.addMember() |
| members.push(vthis); |
| |
| // Emulate vthis.dsymbolSemantic() |
| vthis.storage_class |= STC.field; |
| vthis.parent = this; |
| vthis.visibility = Visibility(Visibility.Kind.public_); |
| vthis.alignment = t.alignment(); |
| vthis.semanticRun = PASS.semanticdone; |
| |
| if (sizeok == Sizeok.fwd) |
| fields.push(vthis); |
| |
| makeNested2(); |
| } |
| } |
| |
| /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer. |
| */ |
| extern (D) final void makeNested2() |
| { |
| if (vthis2) |
| return; |
| if (!vthis) |
| makeNested(); // can't add second before first |
| if (!vthis) |
| return; |
| if (sizeok == Sizeok.done) |
| return; |
| if (isUnionDeclaration() || isInterfaceDeclaration()) |
| return; |
| if (storage_class & STC.static_) |
| return; |
| |
| auto s0 = toParentLocal(); |
| auto s = toParent2(); |
| if (!s || !s0 || s == s0) |
| return; |
| auto cd = s.isClassDeclaration(); |
| Type t = cd ? cd.type : Type.tvoidptr; |
| |
| vthis2 = new ThisDeclaration(loc, t); |
| //vthis2.storage_class |= STC.ref_; |
| |
| // Emulate vthis2.addMember() |
| members.push(vthis2); |
| |
| // Emulate vthis2.dsymbolSemantic() |
| vthis2.storage_class |= STC.field; |
| vthis2.parent = this; |
| vthis2.visibility = Visibility(Visibility.Kind.public_); |
| vthis2.alignment = t.alignment(); |
| vthis2.semanticRun = PASS.semanticdone; |
| |
| if (sizeok == Sizeok.fwd) |
| fields.push(vthis2); |
| } |
| |
| override final bool isExport() const |
| { |
| return visibility.kind == Visibility.Kind.export_; |
| } |
| |
| /******************************************* |
| * Look for constructor declaration. |
| */ |
| final Dsymbol searchCtor() |
| { |
| auto s = search(Loc.initial, Id.ctor); |
| if (s) |
| { |
| if (!(s.isCtorDeclaration() || |
| s.isTemplateDeclaration() || |
| s.isOverloadSet())) |
| { |
| s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation"); |
| errors = true; |
| s = null; |
| } |
| } |
| if (s && s.toParent() != this) |
| s = null; // search() looks through ancestor classes |
| if (s) |
| { |
| // Finish all constructors semantics to determine this.noDefaultCtor. |
| struct SearchCtor |
| { |
| extern (C++) static int fp(Dsymbol s, void* ctxt) |
| { |
| auto f = s.isCtorDeclaration(); |
| if (f && f.semanticRun == PASS.initial) |
| f.dsymbolSemantic(null); |
| return 0; |
| } |
| } |
| |
| for (size_t i = 0; i < members.dim; i++) |
| { |
| auto sm = (*members)[i]; |
| sm.apply(&SearchCtor.fp, null); |
| } |
| } |
| return s; |
| } |
| |
| override final Visibility visible() pure nothrow @nogc @safe |
| { |
| return visibility; |
| } |
| |
| // 'this' type |
| final Type handleType() |
| { |
| return type; |
| } |
| |
| // Does this class have an invariant function? |
| final bool hasInvariant() |
| { |
| return invs.length != 0; |
| } |
| |
| // Back end |
| void* sinit; /// initializer symbol |
| |
| override final inout(AggregateDeclaration) isAggregateDeclaration() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |