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-2023 by The D Language Foundation, All Rights Reserved
8 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
9 * License: $(LINK2 https://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
;
21 import dmd
.arraytypes
;
24 import dmd
.declaration
;
28 import dmd
.dsymbolsem
;
31 import dmd
.expression
;
36 import dmd
.identifier
;
40 import dmd
.typesem
: defaultInit
;
44 * The ClassKind enum is used in AggregateDeclaration AST nodes to
45 * specify the linkage type of the struct/class/interface or if it
46 * is an anonymous class. If the class is anonymous it is also
47 * considered to be a D class.
49 enum ClassKind
: ubyte
51 /// the aggregate is a d(efault) class
53 /// the aggregate is a C++ struct/class/interface
55 /// the aggregate is an Objective-C class/interface
57 /// the aggregate is a C struct
62 * Give a nice string for a class kind for error messages
66 * 0-terminated string for `c`
68 const(char)* toChars(ClassKind c
) @safe
84 * If an aggregate has a pargma(mangle, ...) this holds the information
89 Dsymbol agg
; // The symbol to copy template parameters from (if any)
90 Identifier id
; // the name to override the aggregate's with, defaults to agg.ident
93 /***********************************************************
94 * Abstract aggregate as a common ancestor for Class- and StructDeclaration.
96 extern (C
++) abstract class AggregateDeclaration
: ScopeDsymbol
99 StorageClass storage_class
; ///
100 uint structsize
; /// size of struct
101 uint alignsize
; /// size of struct for alignment purposes
102 VarDeclarations fields
; /// VarDeclaration fields
103 Dsymbol deferred
; /// any deferred semantic2() or semantic3() symbol
105 /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
107 /// Specify whether to mangle the aggregate as a `class` or a `struct`
108 /// This information is used by the MSVC mangler
109 /// Only valid for class and struct. TODO: Merge with ClassKind ?
112 /// overridden symbol with pragma(mangle, "...") if not null
113 MangleOverride
* pMangleOverride
;
116 * !=null if is nested
117 * pointing to the dsymbol that directly enclosing it.
118 * 1. The function that enclosing it (nested struct and class)
119 * 2. The class that enclosing it (nested class only)
120 * 3. If enclosing aggregate is template, its enclosing dsymbol.
122 * See AggregateDeclaraton::makeNested for the details.
126 VarDeclaration vthis
; /// 'this' parameter if this aggregate is nested
127 VarDeclaration vthis2
; /// 'this' parameter if this aggregate is a template and is nested
129 // Special member functions
130 FuncDeclarations invs
; /// Array of invariants
131 FuncDeclaration inv
; /// Merged invariant calling all members of invs
133 /// CtorDeclaration or TemplateDeclaration
136 /// default constructor - should have no arguments, because
137 /// it would be stored in TypeInfo_Class.defaultConstructor
138 CtorDeclaration defaultCtor
;
140 AliasThis aliasthis
; /// forward unresolved lookups to aliasthis
142 DtorDeclarations userDtors
; /// user-defined destructors (`~this()`) - mixins can yield multiple ones
143 DtorDeclaration aggrDtor
; /// aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes)
144 DtorDeclaration dtor
; /// the aggregate destructor exposed as `__xdtor` alias
145 /// (same as aggrDtor, except for C++ classes with virtual dtor on Windows)
146 DtorDeclaration tidtor
; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
147 DtorDeclaration fieldDtor
; /// function destructing (non-inherited) fields
149 Expression getRTInfo
; /// pointer to GC info generated by object.RTInfo(this)
152 Visibility visibility
;
153 bool noDefaultCtor
; /// no default construction
154 bool disableNew
; /// disallow allocations using `new`
155 Sizeok sizeok
= Sizeok
.none
; /// set when structsize contains valid data
157 final extern (D
) this(const ref Loc loc
, Identifier id
)
160 visibility
= Visibility(Visibility
.Kind
.public_
);
163 /***************************************
164 * Create a new scope from sc.
165 * semantic, semantic2 and semantic3 will use this for aggregate members.
167 Scope
* newScope(Scope
* sc
)
169 auto sc2
= sc
.push(this);
170 sc2
.stc &= STC
.flowThruAggregate
;
172 sc2
.inunion
= isUnionDeclaration();
173 sc2
.visibility
= Visibility(Visibility
.Kind
.public_
);
174 sc2
.explicitVisibility
= 0;
175 sc2
.aligndecl
= null;
176 sc2
.userAttribDecl
= null;
177 sc2
.namespace
= null;
181 override final void setScope(Scope
* sc
)
183 // Might need a scope to resolve forward references. The check for
184 // semanticRun prevents unnecessary setting of _scope during deferred
185 // setScope phases for aggregates which already finished semantic().
186 // See https://issues.dlang.org/show_bug.cgi?id=16607
187 if (semanticRun
< PASS
.semanticdone
)
188 ScopeDsymbol
.setScope(sc
);
191 /***************************************
193 * The total number of fields minus the number of hidden fields.
195 extern (D
) final size_t
nonHiddenFields()
197 return fields
.length
- isNested() - (vthis2
!is null);
200 /***************************************
201 * Collect all instance fields, then determine instance size.
203 * false if failed to determine the size.
205 extern (D
) final bool determineSize(const ref Loc loc
)
207 //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
209 // The previous instance size finalizing had:
210 if (type
.ty
== Terror || errors
)
211 return false; // failed already
212 if (sizeok
== Sizeok
.done
)
213 return true; // succeeded
217 .error(loc
, "%s `%s` unknown size", kind
, toPrettyChars
);
222 dsymbolSemantic(this, null);
224 // Determine the instance size of base class first.
225 if (auto cd
= isClassDeclaration())
228 if (cd
&& !cd
.determineSize(loc
))
232 // Determine instance fields when sizeok == Sizeok.none
233 if (!this.determineFields())
235 if (sizeok
!= Sizeok
.done
)
238 // this aggregate type has:
239 if (type
.ty
== Terror
)
240 return false; // marked as invalid during the finalizing.
241 if (sizeok
== Sizeok
.done
)
242 return true; // succeeded to calculate instance size.
245 // There's unresolvable forward reference.
246 if (type
!= Type
.terror
)
247 error(loc
, "%s `%s` no size because of forward reference", kind
, toPrettyChars
);
248 // Don't cache errors from speculative semantic, might be resolvable later.
249 // https://issues.dlang.org/show_bug.cgi?id=16574
258 abstract void finalizeSize();
260 override final uinteger_t
size(const ref Loc loc
)
262 //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
263 bool ok
= determineSize(loc
);
264 //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
265 return ok ? structsize
: SIZE_INVALID
;
268 /***************************************
269 * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
270 * field initializers have unique memory space on instance.
272 * true if any errors happen.
274 extern (D
) final bool checkOverlappedFields()
276 //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
277 assert(sizeok
== Sizeok
.done
);
278 size_t nfields
= fields
.length
;
281 auto cd
= isClassDeclaration();
282 if (!cd ||
!cd
.baseClass ||
!cd
.baseClass
.isNested())
284 if (vthis2
&& !(cd
&& cd
.baseClass
&& cd
.baseClass
.vthis2
))
289 // Fill in missing any elements with default initializers
290 foreach (i
; 0 .. nfields
)
299 const vdIsVoidInit
= vd
._init
&& vd
._init
.isVoidInitializer();
301 // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
302 foreach (j
; 0 .. nfields
)
312 if (!vd
.isOverlappedWith(v2
))
315 // vd and v2 are overlapping.
316 vd
.overlapped
= true;
317 v2
.overlapped
= true;
319 if (!MODimplicitConv(vd
.type
.mod
, v2
.type
.mod
))
320 v2
.overlapUnsafe
= true;
321 if (!MODimplicitConv(v2
.type
.mod
, vd
.type
.mod
))
322 vd
.overlapUnsafe
= true;
330 if (v2
._init
.isVoidInitializer())
333 if (vd
._init
&& !vdIsVoidInit
&& v2
._init
)
335 .error(loc
, "overlapping default initialization for field `%s` and `%s`", v2
.toChars(), vd
.toChars());
338 else if (v2
._init
&& i
< j
)
340 .error(v2
.loc
, "union field `%s` with default initialization `%s` must be before field `%s`",
341 v2
.toChars(), dmd
.hdrgen
.toChars(v2
._init
), vd
.toChars());
349 /***************************************
350 * Fill out remainder of elements[] with default initializers for fields[].
353 * elements = explicit arguments which given to construct object.
354 * ctorinit = true if the elements will be used for default initialization.
356 * false if any errors occur.
357 * Otherwise, returns true and the missing arguments will be pushed in elements[].
359 final bool fill(const ref Loc loc
, ref Expressions elements
, bool ctorinit
)
361 //printf("AggregateDeclaration::fill() %s\n", toChars());
362 assert(sizeok
== Sizeok
.done
);
363 const nfields
= nonHiddenFields();
366 size_t dim
= elements
.length
;
367 elements
.setDim(nfields
);
368 foreach (size_t i
; dim
.. nfields
)
371 // Fill in missing any elements with default initializers
372 foreach (i
; 0 .. nfields
)
379 if (vd
._init
&& vd
._init
.isVoidInitializer())
382 // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
384 foreach (j
; 0 .. nfields
)
389 if (!vd
.isOverlappedWith(v2
))
397 if (v2
._init
&& v2
._init
.isVoidInitializer())
402 /* Prefer first found non-void-initialized field
403 * union U { int a; int b = 2; }
404 * U u; // Error: overlapping initialization for field a and b
413 .error(loc
, "overlapping initialization for field `%s` and `%s`", v2
.toChars(), vd
.toChars());
419 // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
421 /* Prefer explicitly initialized field
422 * union U { int a; int b = 2; }
423 * U u; // OK (u.b == 2)
425 if (!vx ||
!vx
._init
&& v2
._init
)
430 else if (vx
!= vd
&& !vx
.isOverlappedWith(v2
))
432 // Both vx and v2 fills vd, but vx and v2 does not overlap
434 else if (vx
._init
&& v2
._init
)
436 .error(loc
, "overlapping default initialization for field `%s` and `%s`",
437 v2
.toChars(), vd
.toChars());
441 assert(vx
._init ||
!vx
._init
&& !v2
._init
);
447 if (vx
.type
.size() == 0)
453 assert(!vx
._init
.isVoidInitializer());
454 if (vx
.inuse
) // https://issues.dlang.org/show_bug.cgi?id=18057
456 .error(loc
, "%s `%s` recursive initialization of field", vx
.kind(), vx
.toPrettyChars());
460 e
= vx
.getConstInitializer(false);
464 if ((vx
.storage_class
& STC
.nodefaultctor
) && !ctorinit
)
466 .error(loc
, "field `%s.%s` must be initialized because it has no default constructor",
467 type
.toChars(), vx
.toChars());
470 /* https://issues.dlang.org/show_bug.cgi?id=12509
471 * Get the element of static array type.
473 Type telem
= vx
.type
;
474 if (telem
.ty
== Tsarray
)
476 /* We cannot use Type::baseElemOf() here.
477 * If the bottom of the Tsarray is an enum type, baseElemOf()
478 * will return the base of the enum, and its default initializer
479 * would be different from the enum's.
482 while ((tsa
= telem
.toBasetype().isTypeSArray()) !is null)
484 if (telem
.ty
== Tvoid
)
485 telem
= Type
.tuns8
.addMod(telem
.mod
);
487 if (telem
.needsNested() && ctorinit
)
488 e
= telem
.defaultInit(loc
);
490 e
= telem
.defaultInitLiteral(loc
);
492 elements
[fieldi
] = e
;
495 foreach (e
; elements
)
497 if (e
&& e
.op
== EXP
.error
)
504 override final Type
getType()
506 /* Apply storage classes to forward references. (Issue 22254)
507 * Note: Avoid interfaces for now. Implementing qualifiers on interface
508 * definitions exposed some issues in their TypeInfo generation in DMD.
509 * Related PR: https://github.com/dlang/dmd/pull/13312
511 if (semanticRun
== PASS
.initial
&& !isInterfaceDeclaration())
513 auto stc = storage_class
;
516 type
= type
.addSTC(stc);
521 // is aggregate deprecated?
522 override final bool isDeprecated() const
524 return !!(this.storage_class
& STC
.deprecated_
);
527 /// Flag this aggregate as deprecated
528 extern (D
) final void setDeprecated()
530 this.storage_class |
= STC
.deprecated_
;
533 /****************************************
534 * Returns true if there's an extra member which is the 'this'
535 * pointer to the enclosing context (enclosing aggregate or function)
537 final bool isNested() const
539 return enclosing
!is null;
542 /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
544 extern (D
) final void makeNested()
546 if (enclosing
) // if already nested
548 if (sizeok
== Sizeok
.done
)
550 if (isUnionDeclaration() ||
isInterfaceDeclaration())
552 if (storage_class
& STC
.static_
)
555 // If nested struct, add in hidden 'this' pointer to outer scope
556 auto s
= toParentLocal();
562 if (auto fd
= s
.isFuncDeclaration())
566 /* https://issues.dlang.org/show_bug.cgi?id=14422
567 * If a nested class parent is a function, its
568 * context pointer (== `outer`) should be void* always.
572 else if (auto ad
= s
.isAggregateDeclaration())
574 if (isClassDeclaration() && ad
.isClassDeclaration())
578 else if (isStructDeclaration())
580 if (auto ti
= ad
.parent
.isTemplateInstance())
582 enclosing
= ti
.enclosing
;
589 //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
592 t
= Type
.tvoidptr
; // t should not be a ref type
595 vthis
= new ThisDeclaration(loc
, t
);
596 //vthis.storage_class |= STC.ref_;
598 // Emulate vthis.addMember()
601 // Emulate vthis.dsymbolSemantic()
602 vthis
.storage_class |
= STC
.field
;
604 vthis
.visibility
= Visibility(Visibility
.Kind
.public_
);
605 vthis
.alignment
= t
.alignment();
606 vthis
.semanticRun
= PASS
.semanticdone
;
608 if (sizeok
== Sizeok
.fwd
)
615 /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
617 extern (D
) final void makeNested2()
622 makeNested(); // can't add second before first
625 if (sizeok
== Sizeok
.done
)
627 if (isUnionDeclaration() ||
isInterfaceDeclaration())
629 if (storage_class
& STC
.static_
)
632 auto s0
= toParentLocal();
633 auto s
= toParent2();
634 if (!s ||
!s0 || s
== s0
)
636 auto cd
= s
.isClassDeclaration();
637 Type t
= cd ? cd
.type
: Type
.tvoidptr
;
639 vthis2
= new ThisDeclaration(loc
, t
);
640 //vthis2.storage_class |= STC.ref_;
642 // Emulate vthis2.addMember()
643 members
.push(vthis2
);
645 // Emulate vthis2.dsymbolSemantic()
646 vthis2
.storage_class |
= STC
.field
;
647 vthis2
.parent
= this;
648 vthis2
.visibility
= Visibility(Visibility
.Kind
.public_
);
649 vthis2
.alignment
= t
.alignment();
650 vthis2
.semanticRun
= PASS
.semanticdone
;
652 if (sizeok
== Sizeok
.fwd
)
656 override final bool isExport() const
658 return visibility
.kind
== Visibility
.Kind
.export_
;
661 /*******************************************
662 * Look for constructor declaration.
664 extern (D
) final Dsymbol
searchCtor()
666 auto s
= this.search(Loc
.initial
, Id
.ctor
);
669 if (!(s
.isCtorDeclaration() ||
670 s
.isTemplateDeclaration() ||
673 .error(s
.loc
, "%s `%s` is not a constructor; identifiers starting with `__` are reserved for the implementation", s
.kind(), s
.toPrettyChars());
678 if (s
&& s
.toParent() != this)
679 s
= null; // search() looks through ancestor classes
682 // Finish all constructors semantics to determine this.noDefaultCtor.
683 static int searchCtor(Dsymbol s
, void*)
685 auto f
= s
.isCtorDeclaration();
686 if (f
&& f
.semanticRun
== PASS
.initial
)
687 f
.dsymbolSemantic(null);
691 for (size_t i
= 0; i
< members
.length
; i
++)
693 auto sm
= (*members
)[i
];
694 sm
.apply(&searchCtor
, null);
700 override final Visibility
visible() pure nothrow @nogc @safe
706 final Type
handleType()
711 // Does this class have an invariant function?
712 final bool hasInvariant()
714 return invs
.length
!= 0;
718 void* sinit
; /// initializer symbol
720 override final inout(AggregateDeclaration
) isAggregateDeclaration() inout
725 override void accept(Visitor v
)
731 /*********************************
732 * Iterate this dsymbol or members of this scoped dsymbol, then
733 * call `fp` with the found symbol and `params`.
735 * symbol = the dsymbol or parent of members to call fp on
736 * fp = function pointer to process the iterated symbol.
737 * If it returns nonzero, the iteration will be aborted.
738 * ctx = context parameter passed to fp.
740 * nonzero if the iteration is aborted by the return value of fp,
741 * or 0 if it's completed.
743 int apply(Dsymbol symbol
, int function(Dsymbol
, void*) fp
, void* ctx
)
745 if (auto nd
= symbol
.isNspace())
747 return nd
.members
.foreachDsymbol( (s
) { return s
&& s
.apply(fp
, ctx
); } );
749 if (auto ad
= symbol
.isAttribDeclaration())
751 return ad
.include(ad
._scope
).foreachDsymbol( (s
) { return s
&& s
.apply(fp
, ctx
); } );
753 if (auto tm
= symbol
.isTemplateMixin())
755 if (tm
._scope
) // if fwd reference
756 dsymbolSemantic(tm
, null); // try to resolve it
758 return tm
.members
.foreachDsymbol( (s
) { return s
&& s
.apply(fp
, ctx
); } );
761 return fp(symbol
, ctx
);
764 /****************************
765 * Do byte or word alignment as necessary.
766 * Align sizes of 0, as we may not know array sizes yet.
768 * alignment = struct alignment that is in effect
769 * memalignsize = natural alignment of field
770 * offset = offset to be aligned
774 public uint alignmember(structalign_t alignment
, uint memalignsize
, uint offset
) pure nothrow @safe
776 //debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, offset);
779 if (alignment
.isDefault())
781 // Alignment in Target::fieldalignsize must match what the
782 // corresponding C compiler's default alignment behavior is.
783 alignvalue
= memalignsize
;
785 else if (alignment
.isPack()) // #pragma pack semantics
787 alignvalue
= alignment
.get();
788 if (memalignsize
< alignvalue
)
789 alignvalue
= memalignsize
; // align to min(memalignsize, alignment)
791 else if (alignment
.get() > 1)
793 // Align on alignment boundary, which must be a positive power of 2
794 alignvalue
= alignment
.get();
799 assert(alignvalue
&& !(alignvalue
& (alignvalue
- 1))); // non-zero and power of 2
800 return (offset
+ alignvalue
- 1) & ~(alignvalue
- 1);
803 /****************************************
804 * Place a field (mem) into an aggregate (agg), which can be a struct, union or class
806 * nextoffset = location just past the end of the previous field in the aggregate.
807 * Updated to be just past the end of this field to be placed, i.e. the future nextoffset
808 * memsize = size of field
809 * memalignsize = natural alignment of field
810 * alignment = alignment in effect for this field
811 * aggsize = size of aggregate (updated)
812 * aggalignsize = alignment of aggregate (updated)
813 * isunion = the aggregate is a union
815 * aligned offset to place field at
818 public uint placeField(ref uint nextoffset
, uint memsize
, uint memalignsize
,
819 structalign_t alignment
, ref uint aggsize
, ref uint aggalignsize
, bool isunion
) @safe pure nothrow
823 printf("placeField() nextoffset: %u\n", nextoffset
);
824 printf(": memsize: %u\n", memsize
);
825 printf(": memalignsize: %u\n", memalignsize
);
826 printf(": alignment: %u\n", alignment
.get());
827 printf(": aggsize: %u\n", aggsize
);
828 printf(": aggalignsize: %u\n", aggalignsize
);
829 printf(": isunion: %d\n", isunion
);
832 uint ofs
= nextoffset
;
834 const uint actualAlignment
=
835 alignment
.isDefault() || alignment
.isPack() && memalignsize
< alignment
.get()
836 ? memalignsize
: alignment
.get();
838 // Ensure no overflow for (memsize + actualAlignment + ofs)
840 const sz
= addu(memsize
, actualAlignment
, overflow
);
841 addu(ofs
, sz
, overflow
);
842 if (overflow
) assert(0);
844 // Skip no-op for noreturn without custom aligment
845 if (memalignsize
!= 0 ||
!alignment
.isDefault())
846 ofs
= alignmember(alignment
, memalignsize
, ofs
);
848 uint memoffset
= ofs
;
855 //printf(" revised nextoffset: %u\n", ofs);
858 if (aggalignsize
< actualAlignment
)
859 aggalignsize
= actualAlignment
;