2 * Defines a `Dsymbol` representing an aggregate, which is a `struct`, `union` or `class`.
4 * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions),
5 * $(LINK2 https://dlang.org/spec/class.html, Class).
7 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
8 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
9 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d, _aggregate.d)
11 * Documentation: https://dlang.org/phobos/dmd_aggregate.html
12 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aggregate.d
17 import core.stdc.stdio;
18 import core.checkedint;
22 import dmd.arraytypes;
24 import dmd.declaration;
28 import dmd.dsymbolsem;
31 import dmd.expression;
35 import dmd.identifier;
38 import dmd.typesem : defaultInit;
42 * The ClassKind enum is used in AggregateDeclaration AST nodes to
43 * specify the linkage type of the struct/class/interface or if it
44 * is an anonymous class. If the class is anonymous it is also
45 * considered to be a D class.
47 enum ClassKind : ubyte
49 /// the aggregate is a d(efault) class
51 /// the aggregate is a C++ struct/class/interface
53 /// the aggregate is an Objective-C class/interface
55 /// the aggregate is a C struct
60 * If an aggregate has a pargma(mangle, ...) this holds the information
65 Dsymbol agg; // The symbol to copy template parameters from (if any)
66 Identifier id; // the name to override the aggregate's with, defaults to agg.ident
69 /***********************************************************
70 * Abstract aggregate as a common ancestor for Class- and StructDeclaration.
72 extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
75 StorageClass storage_class; ///
76 uint structsize; /// size of struct
77 uint alignsize; /// size of struct for alignment purposes
78 VarDeclarations fields; /// VarDeclaration fields
79 Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol
81 /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
83 /// Specify whether to mangle the aggregate as a `class` or a `struct`
84 /// This information is used by the MSVC mangler
85 /// Only valid for class and struct. TODO: Merge with ClassKind ?
88 /// overridden symbol with pragma(mangle, "...") if not null
89 MangleOverride* mangleOverride;
93 * pointing to the dsymbol that directly enclosing it.
94 * 1. The function that enclosing it (nested struct and class)
95 * 2. The class that enclosing it (nested class only)
96 * 3. If enclosing aggregate is template, its enclosing dsymbol.
98 * See AggregateDeclaraton::makeNested for the details.
102 VarDeclaration vthis; /// 'this' parameter if this aggregate is nested
103 VarDeclaration vthis2; /// 'this' parameter if this aggregate is a template and is nested
105 // Special member functions
106 FuncDeclarations invs; /// Array of invariants
107 FuncDeclaration inv; /// Merged invariant calling all members of invs
109 /// CtorDeclaration or TemplateDeclaration
112 /// default constructor - should have no arguments, because
113 /// it would be stored in TypeInfo_Class.defaultConstructor
114 CtorDeclaration defaultCtor;
116 AliasThis aliasthis; /// forward unresolved lookups to aliasthis
118 DtorDeclarations dtors; /// Array of destructors
119 DtorDeclaration dtor; /// aggregate destructor calling dtors and member constructors
120 DtorDeclaration primaryDtor;/// non-deleting C++ destructor, same as dtor for D
121 DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
122 FuncDeclaration fieldDtor; /// aggregate destructor for just the fields
124 Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
127 Visibility visibility;
128 bool noDefaultCtor; /// no default construction
129 bool disableNew; /// disallow allocations using `new`
130 Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data
132 final extern (D) this(const ref Loc loc, Identifier id)
135 visibility = Visibility(Visibility.Kind.public_);
138 /***************************************
139 * Create a new scope from sc.
140 * semantic, semantic2 and semantic3 will use this for aggregate members.
142 Scope* newScope(Scope* sc)
144 auto sc2 = sc.push(this);
145 sc2.stc &= STC.flowThruAggregate;
147 sc2.inunion = isUnionDeclaration();
148 sc2.visibility = Visibility(Visibility.Kind.public_);
149 sc2.explicitVisibility = 0;
150 sc2.aligndecl = null;
151 sc2.userAttribDecl = null;
152 sc2.namespace = null;
156 override final void setScope(Scope* sc)
158 // Might need a scope to resolve forward references. The check for
159 // semanticRun prevents unnecessary setting of _scope during deferred
160 // setScope phases for aggregates which already finished semantic().
161 // See https://issues.dlang.org/show_bug.cgi?id=16607
162 if (semanticRun < PASS.semanticdone)
163 ScopeDsymbol.setScope(sc);
166 /***************************************
168 * The total number of fields minus the number of hidden fields.
170 final size_t nonHiddenFields()
172 return fields.dim - isNested() - (vthis2 !is null);
175 /***************************************
176 * Collect all instance fields, then determine instance size.
178 * false if failed to determine the size.
180 final bool determineSize(Loc loc)
182 //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
184 // The previous instance size finalizing had:
185 if (type.ty == Terror)
186 return false; // failed already
187 if (sizeok == Sizeok.done)
188 return true; // succeeded
192 error(loc, "unknown size");
197 dsymbolSemantic(this, null);
199 // Determine the instance size of base class first.
200 if (auto cd = isClassDeclaration())
203 if (cd && !cd.determineSize(loc))
207 // Determine instance fields when sizeok == Sizeok.none
208 if (!this.determineFields())
210 if (sizeok != Sizeok.done)
213 // this aggregate type has:
214 if (type.ty == Terror)
215 return false; // marked as invalid during the finalizing.
216 if (sizeok == Sizeok.done)
217 return true; // succeeded to calculate instance size.
220 // There's unresolvable forward reference.
221 if (type != Type.terror)
222 error(loc, "no size because of forward reference");
223 // Don't cache errors from speculative semantic, might be resolvable later.
224 // https://issues.dlang.org/show_bug.cgi?id=16574
233 abstract void finalizeSize();
235 override final d_uns64 size(const ref Loc loc)
237 //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
238 bool ok = determineSize(loc);
239 //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
240 return ok ? structsize : SIZE_INVALID;
243 /***************************************
244 * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
245 * field initializers have unique memory space on instance.
247 * true if any errors happen.
249 extern (D) final bool checkOverlappedFields()
251 //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
252 assert(sizeok == Sizeok.done);
253 size_t nfields = fields.dim;
256 auto cd = isClassDeclaration();
257 if (!cd || !cd.baseClass || !cd.baseClass.isNested())
259 if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
264 // Fill in missing any elements with default initializers
265 foreach (i; 0 .. nfields)
274 const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
276 // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
277 foreach (j; 0 .. nfields)
287 if (!vd.isOverlappedWith(v2))
290 // vd and v2 are overlapping.
291 vd.overlapped = true;
292 v2.overlapped = true;
294 if (!MODimplicitConv(vd.type.mod, v2.type.mod))
295 v2.overlapUnsafe = true;
296 if (!MODimplicitConv(v2.type.mod, vd.type.mod))
297 vd.overlapUnsafe = true;
305 if (v2._init.isVoidInitializer())
308 if (vd._init && !vdIsVoidInit && v2._init)
310 .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
313 else if (v2._init && i < j)
315 .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
316 v2.toChars(), v2._init.toChars(), vd.toChars());
324 /***************************************
325 * Fill out remainder of elements[] with default initializers for fields[].
328 * elements = explicit arguments which given to construct object.
329 * ctorinit = true if the elements will be used for default initialization.
331 * false if any errors occur.
332 * Otherwise, returns true and the missing arguments will be pushed in elements[].
334 final bool fill(Loc loc, Expressions* elements, bool ctorinit)
336 //printf("AggregateDeclaration::fill() %s\n", toChars());
337 assert(sizeok == Sizeok.done);
339 const nfields = nonHiddenFields();
342 size_t dim = elements.dim;
343 elements.setDim(nfields);
344 foreach (size_t i; dim .. nfields)
345 (*elements)[i] = null;
347 // Fill in missing any elements with default initializers
348 foreach (i; 0 .. nfields)
355 if (vd._init && vd._init.isVoidInitializer())
358 // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
360 foreach (j; 0 .. nfields)
365 if (!vd.isOverlappedWith(v2))
373 if (v2._init && v2._init.isVoidInitializer())
378 /* Prefer first found non-void-initialized field
379 * union U { int a; int b = 2; }
380 * U u; // Error: overlapping initialization for field a and b
389 .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
395 // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
397 /* Prefer explicitly initialized field
398 * union U { int a; int b = 2; }
399 * U u; // OK (u.b == 2)
401 if (!vx || !vx._init && v2._init)
406 else if (vx != vd && !vx.isOverlappedWith(v2))
408 // Both vx and v2 fills vd, but vx and v2 does not overlap
410 else if (vx._init && v2._init)
412 .error(loc, "overlapping default initialization for field `%s` and `%s`",
413 v2.toChars(), vd.toChars());
417 assert(vx._init || !vx._init && !v2._init);
423 if (vx.type.size() == 0)
429 assert(!vx._init.isVoidInitializer());
430 if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057
432 vx.error(loc, "recursive initialization of field");
436 e = vx.getConstInitializer(false);
440 if ((vx.storage_class & STC.nodefaultctor) && !ctorinit)
442 .error(loc, "field `%s.%s` must be initialized because it has no default constructor",
443 type.toChars(), vx.toChars());
446 /* https://issues.dlang.org/show_bug.cgi?id=12509
447 * Get the element of static array type.
449 Type telem = vx.type;
450 if (telem.ty == Tsarray)
452 /* We cannot use Type::baseElemOf() here.
453 * If the bottom of the Tsarray is an enum type, baseElemOf()
454 * will return the base of the enum, and its default initializer
455 * would be different from the enum's.
458 while ((tsa = telem.toBasetype().isTypeSArray()) !is null)
460 if (telem.ty == Tvoid)
461 telem = Type.tuns8.addMod(telem.mod);
463 if (telem.needsNested() && ctorinit)
464 e = telem.defaultInit(loc);
466 e = telem.defaultInitLiteral(loc);
468 (*elements)[fieldi] = e;
471 foreach (e; *elements)
473 if (e && e.op == TOK.error)
480 /****************************
481 * Do byte or word alignment as necessary.
482 * Align sizes of 0, as we may not know array sizes yet.
484 * alignment = struct alignment that is in effect
485 * size = alignment requirement of field
486 * poffset = pointer to offset to be aligned
488 extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe
490 //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
493 case cast(structalign_t)1:
497 case cast(structalign_t)STRUCTALIGN_DEFAULT:
498 // Alignment in Target::fieldalignsize must match what the
499 // corresponding C compiler's default alignment behavior is.
500 assert(size > 0 && !(size & (size - 1)));
501 *poffset = (*poffset + size - 1) & ~(size - 1);
505 // Align on alignment boundary, which must be a positive power of 2
506 assert(alignment > 0 && !(alignment & (alignment - 1)));
507 *poffset = (*poffset + alignment - 1) & ~(alignment - 1);
512 /****************************************
513 * Place a member (mem) into an aggregate (agg), which can be a struct, union or class
515 * offset to place field at
517 * nextoffset: next location in aggregate
518 * memsize: size of member
519 * memalignsize: natural alignment of member
520 * alignment: alignment in effect for this member
521 * paggsize: size of aggregate (updated)
522 * paggalignsize: alignment of aggregate (updated)
523 * isunion: the aggregate is a union
525 extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize,
526 structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion)
528 uint ofs = *nextoffset;
530 const uint actualAlignment =
531 alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment;
533 // Ensure no overflow
535 const sz = addu(memsize, actualAlignment, overflow);
536 addu(ofs, sz, overflow);
537 if (overflow) assert(0);
539 alignmember(alignment, memalignsize, &ofs);
540 uint memoffset = ofs;
547 if (*paggalignsize < actualAlignment)
548 *paggalignsize = actualAlignment;
553 override final Type getType()
558 // is aggregate deprecated?
559 override final bool isDeprecated() const
561 return !!(this.storage_class & STC.deprecated_);
564 /// Flag this aggregate as deprecated
565 final void setDeprecated()
567 this.storage_class |= STC.deprecated_;
570 /****************************************
571 * Returns true if there's an extra member which is the 'this'
572 * pointer to the enclosing context (enclosing aggregate or function)
574 final bool isNested() const
576 return enclosing !is null;
579 /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
581 extern (D) final void makeNested()
583 if (enclosing) // if already nested
585 if (sizeok == Sizeok.done)
587 if (isUnionDeclaration() || isInterfaceDeclaration())
589 if (storage_class & STC.static_)
592 // If nested struct, add in hidden 'this' pointer to outer scope
593 auto s = toParentLocal();
599 if (auto fd = s.isFuncDeclaration())
603 /* https://issues.dlang.org/show_bug.cgi?id=14422
604 * If a nested class parent is a function, its
605 * context pointer (== `outer`) should be void* always.
609 else if (auto ad = s.isAggregateDeclaration())
611 if (isClassDeclaration() && ad.isClassDeclaration())
615 else if (isStructDeclaration())
617 if (auto ti = ad.parent.isTemplateInstance())
619 enclosing = ti.enclosing;
626 //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
629 t = Type.tvoidptr; // t should not be a ref type
632 vthis = new ThisDeclaration(loc, t);
633 //vthis.storage_class |= STC.ref_;
635 // Emulate vthis.addMember()
638 // Emulate vthis.dsymbolSemantic()
639 vthis.storage_class |= STC.field;
641 vthis.visibility = Visibility(Visibility.Kind.public_);
642 vthis.alignment = t.alignment();
643 vthis.semanticRun = PASS.semanticdone;
645 if (sizeok == Sizeok.fwd)
652 /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
654 extern (D) final void makeNested2()
659 makeNested(); // can't add second before first
662 if (sizeok == Sizeok.done)
664 if (isUnionDeclaration() || isInterfaceDeclaration())
666 if (storage_class & STC.static_)
669 auto s0 = toParentLocal();
670 auto s = toParent2();
671 if (!s || !s0 || s == s0)
673 auto cd = s.isClassDeclaration();
674 Type t = cd ? cd.type : Type.tvoidptr;
676 vthis2 = new ThisDeclaration(loc, t);
677 //vthis2.storage_class |= STC.ref_;
679 // Emulate vthis2.addMember()
680 members.push(vthis2);
682 // Emulate vthis2.dsymbolSemantic()
683 vthis2.storage_class |= STC.field;
684 vthis2.parent = this;
685 vthis2.visibility = Visibility(Visibility.Kind.public_);
686 vthis2.alignment = t.alignment();
687 vthis2.semanticRun = PASS.semanticdone;
689 if (sizeok == Sizeok.fwd)
693 override final bool isExport() const
695 return visibility.kind == Visibility.Kind.export_;
698 /*******************************************
699 * Look for constructor declaration.
701 final Dsymbol searchCtor()
703 auto s = search(Loc.initial, Id.ctor);
706 if (!(s.isCtorDeclaration() ||
707 s.isTemplateDeclaration() ||
710 s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation");
715 if (s && s.toParent() != this)
716 s = null; // search() looks through ancestor classes
719 // Finish all constructors semantics to determine this.noDefaultCtor.
722 extern (C++) static int fp(Dsymbol s, void* ctxt)
724 auto f = s.isCtorDeclaration();
725 if (f && f.semanticRun == PASS.init)
726 f.dsymbolSemantic(null);
731 for (size_t i = 0; i < members.dim; i++)
733 auto sm = (*members)[i];
734 sm.apply(&SearchCtor.fp, null);
740 override final Visibility visible() pure nothrow @nogc @safe
746 final Type handleType()
751 // Does this class have an invariant function?
752 final bool hasInvariant()
754 return invs.length != 0;
758 void* sinit; /// initializer symbol
760 override final inout(AggregateDeclaration) isAggregateDeclaration() inout
765 override void accept(Visitor v)