1 /**
2  * Defines a `Dsymbol` representing an aggregate, which is a `struct`, `union` or `class`.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions),
5  *                $(LINK2 https://dlang.org/spec/class.html, Class).
6  *
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
13  */
14 
15 module dmd.aggregate;
16 
17 import core.stdc.stdio;
18 import core.checkedint;
19 
20 import dmd.aliasthis;
21 import dmd.arraytypes;
22 import dmd.astenums;
23 import dmd.attrib;
24 import dmd.declaration;
25 import dmd.dscope;
26 import dmd.dstruct;
27 import dmd.dsymbol;
28 import dmd.dsymbolsem;
29 import dmd.dtemplate;
30 import dmd.errors;
31 import dmd.expression;
32 import dmd.func;
33 import dmd.globals;
34 import dmd.hdrgen;
35 import dmd.id;
36 import dmd.identifier;
37 import dmd.location;
38 import dmd.mtype;
39 import dmd.tokens;
40 import dmd.typesem : defaultInit;
41 import dmd.visitor;
42 
43 /**
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.
48  */
49 enum ClassKind : ubyte
50 {
51     /// the aggregate is a d(efault) class
52     d,
53     /// the aggregate is a C++ struct/class/interface
54     cpp,
55     /// the aggregate is an Objective-C class/interface
56     objc,
57     /// the aggregate is a C struct
58     c,
59 }
60 
61 /**
62  * Give a nice string for a class kind for error messages
63  * Params:
64  *     c = class kind
65  * Returns:
66  *     0-terminated string for `c`
67  */
68 const(char)* toChars(ClassKind c) @safe
69 {
70     final switch (c)
71     {
72         case ClassKind.d:
73             return "D";
74         case ClassKind.cpp:
75             return "C++";
76         case ClassKind.objc:
77             return "Objective-C";
78         case ClassKind.c:
79             return "C";
80     }
81 }
82 
83 /**
84  * If an aggregate has a pargma(mangle, ...) this holds the information
85  * to mangle.
86  */
87 struct MangleOverride
88 {
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
91 }
92 
93 /***********************************************************
94  * Abstract aggregate as a common ancestor for Class- and StructDeclaration.
95  */
96 extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
97 {
98     Type type;                  ///
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
104 
105     /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
106     ClassKind classKind;
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 ?
110     CPPMANGLE cppmangle;
111 
112     /// overridden symbol with pragma(mangle, "...") if not null
113     MangleOverride* pMangleOverride;
114 
115     /**
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.
121      *
122      * See AggregateDeclaraton::makeNested for the details.
123      */
124     Dsymbol enclosing;
125 
126     VarDeclaration vthis;   /// 'this' parameter if this aggregate is nested
127     VarDeclaration vthis2;  /// 'this' parameter if this aggregate is a template and is nested
128 
129     // Special member functions
130     FuncDeclarations invs;  /// Array of invariants
131     FuncDeclaration inv;    /// Merged invariant calling all members of invs
132 
133     /// CtorDeclaration or TemplateDeclaration
134     Dsymbol ctor;
135 
136     /// default constructor - should have no arguments, because
137     /// it would be stored in TypeInfo_Class.defaultConstructor
138     CtorDeclaration defaultCtor;
139 
140     AliasThis aliasthis;    /// forward unresolved lookups to aliasthis
141 
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
148 
149     Expression getRTInfo;   /// pointer to GC info generated by object.RTInfo(this)
150 
151     ///
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
156 
157     final extern (D) this(const ref Loc loc, Identifier id)
158     {
159         super(loc, id);
160         visibility = Visibility(Visibility.Kind.public_);
161     }
162 
163     /***************************************
164      * Create a new scope from sc.
165      * semantic, semantic2 and semantic3 will use this for aggregate members.
166      */
167     Scope* newScope(Scope* sc)
168     {
169         auto sc2 = sc.push(this);
170         sc2.stc &= STC.flowThruAggregate;
171         sc2.parent = this;
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;
178         return sc2;
179     }
180 
181     override final void setScope(Scope* sc)
182     {
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);
189     }
190 
191     /***************************************
192      * Returns:
193      *      The total number of fields minus the number of hidden fields.
194      */
195     extern (D) final size_t nonHiddenFields()
196     {
197         return fields.length - isNested() - (vthis2 !is null);
198     }
199 
200     /***************************************
201      * Collect all instance fields, then determine instance size.
202      * Returns:
203      *      false if failed to determine the size.
204      */
205     extern (D) final bool determineSize(const ref Loc loc)
206     {
207         //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
208 
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
214 
215         if (!members)
216         {
217             .error(loc, "%s `%s` unknown size", kind, toPrettyChars);
218             return false;
219         }
220 
221         if (_scope)
222             dsymbolSemantic(this, null);
223 
224         // Determine the instance size of base class first.
225         if (auto cd = isClassDeclaration())
226         {
227             cd = cd.baseClass;
228             if (cd && !cd.determineSize(loc))
229                 goto Lfail;
230         }
231 
232         // Determine instance fields when sizeok == Sizeok.none
233         if (!this.determineFields())
234             goto Lfail;
235         if (sizeok != Sizeok.done)
236             finalizeSize();
237 
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.
243 
244     Lfail:
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
250         if (!global.gag)
251         {
252             type = Type.terror;
253             errors = true;
254         }
255         return false;
256     }
257 
258     abstract void finalizeSize();
259 
260     override final uinteger_t size(const ref Loc loc)
261     {
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;
266     }
267 
268     /***************************************
269      * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
270      * field initializers have unique memory space on instance.
271      * Returns:
272      *      true if any errors happen.
273      */
274     extern (D) final bool checkOverlappedFields()
275     {
276         //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
277         assert(sizeok == Sizeok.done);
278         size_t nfields = fields.length;
279         if (isNested())
280         {
281             auto cd = isClassDeclaration();
282             if (!cd || !cd.baseClass || !cd.baseClass.isNested())
283                 nfields--;
284             if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
285                 nfields--;
286         }
287         bool errors = false;
288 
289         // Fill in missing any elements with default initializers
290         foreach (i; 0 .. nfields)
291         {
292             auto vd = fields[i];
293             if (vd.errors)
294             {
295                 errors = true;
296                 continue;
297             }
298 
299             const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
300 
301             // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
302             foreach (j; 0 .. nfields)
303             {
304                 if (i == j)
305                     continue;
306                 auto v2 = fields[j];
307                 if (v2.errors)
308                 {
309                     errors = true;
310                     continue;
311                 }
312                 if (!vd.isOverlappedWith(v2))
313                     continue;
314 
315                 // vd and v2 are overlapping.
316                 vd.overlapped = true;
317                 v2.overlapped = true;
318 
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;
323 
324                 if (i > j)
325                     continue;
326 
327                 if (!v2._init)
328                     continue;
329 
330                 if (v2._init.isVoidInitializer())
331                     continue;
332 
333                 if (vd._init && !vdIsVoidInit && v2._init)
334                 {
335                     .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
336                     errors = true;
337                 }
338                 else if (v2._init && i < j)
339                 {
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());
342                     errors = true;
343                 }
344             }
345         }
346         return errors;
347     }
348 
349     /***************************************
350      * Fill out remainder of elements[] with default initializers for fields[].
351      * Params:
352      *      loc         = location
353      *      elements    = explicit arguments which given to construct object.
354      *      ctorinit    = true if the elements will be used for default initialization.
355      * Returns:
356      *      false if any errors occur.
357      *      Otherwise, returns true and the missing arguments will be pushed in elements[].
358      */
359     final bool fill(const ref Loc loc, ref Expressions elements, bool ctorinit)
360     {
361         //printf("AggregateDeclaration::fill() %s\n", toChars());
362         assert(sizeok == Sizeok.done);
363         const nfields = nonHiddenFields();
364         bool errors = false;
365 
366         size_t dim = elements.length;
367         elements.setDim(nfields);
368         foreach (size_t i; dim .. nfields)
369             elements[i] = null;
370 
371         // Fill in missing any elements with default initializers
372         foreach (i; 0 .. nfields)
373         {
374             if (elements[i])
375                 continue;
376 
377             auto vd = fields[i];
378             auto vx = vd;
379             if (vd._init && vd._init.isVoidInitializer())
380                 vx = null;
381 
382             // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
383             size_t fieldi = i;
384             foreach (j; 0 .. nfields)
385             {
386                 if (i == j)
387                     continue;
388                 auto v2 = fields[j];
389                 if (!vd.isOverlappedWith(v2))
390                     continue;
391 
392                 if (elements[j])
393                 {
394                     vx = null;
395                     break;
396                 }
397                 if (v2._init && v2._init.isVoidInitializer())
398                     continue;
399 
400                 version (all)
401                 {
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
405                      */
406                     if (!vx)
407                     {
408                         vx = v2;
409                         fieldi = j;
410                     }
411                     else if (v2._init)
412                     {
413                         .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
414                         errors = true;
415                     }
416                 }
417                 else
418                 {
419                     // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
420 
421                     /* Prefer explicitly initialized field
422                      * union U { int a; int b = 2; }
423                      * U u;    // OK (u.b == 2)
424                      */
425                     if (!vx || !vx._init && v2._init)
426                     {
427                         vx = v2;
428                         fieldi = j;
429                     }
430                     else if (vx != vd && !vx.isOverlappedWith(v2))
431                     {
432                         // Both vx and v2 fills vd, but vx and v2 does not overlap
433                     }
434                     else if (vx._init && v2._init)
435                     {
436                         .error(loc, "overlapping default initialization for field `%s` and `%s`",
437                             v2.toChars(), vd.toChars());
438                         errors = true;
439                     }
440                     else
441                         assert(vx._init || !vx._init && !v2._init);
442                 }
443             }
444             if (vx)
445             {
446                 Expression e;
447                 if (vx.type.size() == 0)
448                 {
449                     e = null;
450                 }
451                 else if (vx._init)
452                 {
453                     assert(!vx._init.isVoidInitializer());
454                     if (vx.inuse)   // https://issues.dlang.org/show_bug.cgi?id=18057
455                     {
456                         .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars());
457                         errors = true;
458                     }
459                     else
460                         e = vx.getConstInitializer(false);
461                 }
462                 else
463                 {
464                     if ((vx.storage_class & STC.nodefaultctor) && !ctorinit)
465                     {
466                         .error(loc, "field `%s.%s` must be initialized because it has no default constructor",
467                             type.toChars(), vx.toChars());
468                         errors = true;
469                     }
470                     /* https://issues.dlang.org/show_bug.cgi?id=12509
471                      * Get the element of static array type.
472                      */
473                     Type telem = vx.type;
474                     if (telem.ty == Tsarray)
475                     {
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.
480                          */
481                         TypeSArray tsa;
482                         while ((tsa = telem.toBasetype().isTypeSArray()) !is null)
483                             telem = tsa.next;
484                         if (telem.ty == Tvoid)
485                             telem = Type.tuns8.addMod(telem.mod);
486                     }
487                     if (telem.needsNested() && ctorinit)
488                         e = telem.defaultInit(loc);
489                     else
490                         e = telem.defaultInitLiteral(loc);
491                 }
492                 elements[fieldi] = e;
493             }
494         }
495         foreach (e; elements)
496         {
497             if (e && e.op == EXP.error)
498                 return false;
499         }
500 
501         return !errors;
502     }
503 
504     override final Type getType()
505     {
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
510          */
511         if (semanticRun == PASS.initial && !isInterfaceDeclaration())
512         {
513             auto stc = storage_class;
514             if (_scope)
515                 stc |= _scope.stc;
516             type = type.addSTC(stc);
517         }
518         return type;
519     }
520 
521     // is aggregate deprecated?
522     override final bool isDeprecated() const
523     {
524         return !!(this.storage_class & STC.deprecated_);
525     }
526 
527     /// Flag this aggregate as deprecated
528     extern (D) final void setDeprecated()
529     {
530         this.storage_class |= STC.deprecated_;
531     }
532 
533     /****************************************
534      * Returns true if there's an extra member which is the 'this'
535      * pointer to the enclosing context (enclosing aggregate or function)
536      */
537     final bool isNested() const
538     {
539         return enclosing !is null;
540     }
541 
542     /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
543      */
544     extern (D) final void makeNested()
545     {
546         if (enclosing) // if already nested
547             return;
548         if (sizeok == Sizeok.done)
549             return;
550         if (isUnionDeclaration() || isInterfaceDeclaration())
551             return;
552         if (storage_class & STC.static_)
553             return;
554 
555         // If nested struct, add in hidden 'this' pointer to outer scope
556         auto s = toParentLocal();
557         if (!s)
558             s = toParent2();
559         if (!s)
560             return;
561         Type t = null;
562         if (auto fd = s.isFuncDeclaration())
563         {
564             enclosing = fd;
565 
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.
569              */
570             t = Type.tvoidptr;
571         }
572         else if (auto ad = s.isAggregateDeclaration())
573         {
574             if (isClassDeclaration() && ad.isClassDeclaration())
575             {
576                 enclosing = ad;
577             }
578             else if (isStructDeclaration())
579             {
580                 if (auto ti = ad.parent.isTemplateInstance())
581                 {
582                     enclosing = ti.enclosing;
583                 }
584             }
585             t = ad.handleType();
586         }
587         if (enclosing)
588         {
589             //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
590             assert(t);
591             if (t.ty == Tstruct)
592                 t = Type.tvoidptr; // t should not be a ref type
593 
594             assert(!vthis);
595             vthis = new ThisDeclaration(loc, t);
596             //vthis.storage_class |= STC.ref_;
597 
598             // Emulate vthis.addMember()
599             members.push(vthis);
600 
601             // Emulate vthis.dsymbolSemantic()
602             vthis.storage_class |= STC.field;
603             vthis.parent = this;
604             vthis.visibility = Visibility(Visibility.Kind.public_);
605             vthis.alignment = t.alignment();
606             vthis.semanticRun = PASS.semanticdone;
607 
608             if (sizeok == Sizeok.fwd)
609                 fields.push(vthis);
610 
611             makeNested2();
612         }
613     }
614 
615     /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
616      */
617     extern (D) final void makeNested2()
618     {
619         if (vthis2)
620             return;
621         if (!vthis)
622             makeNested();   // can't add second before first
623         if (!vthis)
624             return;
625         if (sizeok == Sizeok.done)
626             return;
627         if (isUnionDeclaration() || isInterfaceDeclaration())
628             return;
629         if (storage_class & STC.static_)
630             return;
631 
632         auto s0 = toParentLocal();
633         auto s = toParent2();
634         if (!s || !s0 || s == s0)
635             return;
636         auto cd = s.isClassDeclaration();
637         Type t = cd ? cd.type : Type.tvoidptr;
638 
639         vthis2 = new ThisDeclaration(loc, t);
640         //vthis2.storage_class |= STC.ref_;
641 
642         // Emulate vthis2.addMember()
643         members.push(vthis2);
644 
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;
651 
652         if (sizeok == Sizeok.fwd)
653             fields.push(vthis2);
654     }
655 
656     override final bool isExport() const
657     {
658         return visibility.kind == Visibility.Kind.export_;
659     }
660 
661     /*******************************************
662      * Look for constructor declaration.
663      */
664     extern (D) final Dsymbol searchCtor()
665     {
666         auto s = search(Loc.initial, Id.ctor);
667         if (s)
668         {
669             if (!(s.isCtorDeclaration() ||
670                   s.isTemplateDeclaration() ||
671                   s.isOverloadSet()))
672             {
673                 .error(s.loc, "%s `%s` is not a constructor; identifiers starting with `__` are reserved for the implementation", s.kind(), s.toPrettyChars());
674                 errors = true;
675                 s = null;
676             }
677         }
678         if (s && s.toParent() != this)
679             s = null; // search() looks through ancestor classes
680         if (s)
681         {
682             // Finish all constructors semantics to determine this.noDefaultCtor.
683             static int searchCtor(Dsymbol s, void*)
684             {
685                 auto f = s.isCtorDeclaration();
686                 if (f && f.semanticRun == PASS.initial)
687                     f.dsymbolSemantic(null);
688                 return 0;
689             }
690 
691             for (size_t i = 0; i < members.length; i++)
692             {
693                 auto sm = (*members)[i];
694                 sm.apply(&searchCtor, null);
695             }
696         }
697         return s;
698     }
699 
700     override final Visibility visible() pure nothrow @nogc @safe
701     {
702         return visibility;
703     }
704 
705     // 'this' type
706     final Type handleType()
707     {
708         return type;
709     }
710 
711     // Does this class have an invariant function?
712     final bool hasInvariant()
713     {
714         return invs.length != 0;
715     }
716 
717     // Back end
718     void* sinit;  /// initializer symbol
719 
720     override final inout(AggregateDeclaration) isAggregateDeclaration() inout
721     {
722         return this;
723     }
724 
725     override void accept(Visitor v)
726     {
727         v.visit(this);
728     }
729 }
730 
731 /*********************************
732  * Iterate this dsymbol or members of this scoped dsymbol, then
733  * call `fp` with the found symbol and `params`.
734  * 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.
739  * Returns:
740  *  nonzero if the iteration is aborted by the return value of fp,
741  *  or 0 if it's completed.
742  */
743 int apply(Dsymbol symbol, int function(Dsymbol, void*) fp, void* ctx)
744 {
745     if (auto nd = symbol.isNspace())
746     {
747         return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
748     }
749     if (auto ad = symbol.isAttribDeclaration())
750     {
751         return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
752     }
753     if (auto tm = symbol.isTemplateMixin())
754     {
755         if (tm._scope) // if fwd reference
756             dsymbolSemantic(tm, null); // try to resolve it
757 
758         return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
759     }
760 
761     return fp(symbol, ctx);
762 }
763 
764 /****************************
765  * Do byte or word alignment as necessary.
766  * Align sizes of 0, as we may not know array sizes yet.
767  * Params:
768  *   alignment = struct alignment that is in effect
769  *   memalignsize = natural alignment of field
770  *   offset = offset to be aligned
771  * Returns:
772  *   aligned offset
773  */
774 public uint alignmember(structalign_t alignment, uint memalignsize, uint offset) pure nothrow @safe
775 {
776     //debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, offset);
777     uint alignvalue;
778 
779     if (alignment.isDefault())
780     {
781         // Alignment in Target::fieldalignsize must match what the
782         // corresponding C compiler's default alignment behavior is.
783         alignvalue = memalignsize;
784     }
785     else if (alignment.isPack())    // #pragma pack semantics
786     {
787         alignvalue = alignment.get();
788         if (memalignsize < alignvalue)
789             alignvalue = memalignsize;      // align to min(memalignsize, alignment)
790     }
791     else if (alignment.get() > 1)
792     {
793         // Align on alignment boundary, which must be a positive power of 2
794         alignvalue = alignment.get();
795     }
796     else
797         return offset;
798 
799     assert(alignvalue && !(alignvalue & (alignvalue - 1))); // non-zero and power of 2
800     return (offset + alignvalue - 1) & ~(alignvalue - 1);
801 }
802 
803 /****************************************
804  * Place a field (mem) into an aggregate (agg), which can be a struct, union or class
805  * Params:
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
814  * Returns:
815  *    aligned offset to place field at
816  *
817  */
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
820 {
821     static if (0)
822     {
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);
830     }
831 
832     uint ofs = nextoffset;
833 
834     const uint actualAlignment =
835         alignment.isDefault() || alignment.isPack() && memalignsize < alignment.get()
836                     ? memalignsize : alignment.get();
837 
838     // Ensure no overflow for (memsize + actualAlignment + ofs)
839     bool overflow;
840     const sz = addu(memsize, actualAlignment, overflow);
841     addu(ofs, sz, overflow);
842     if (overflow) assert(0);
843 
844     // Skip no-op for noreturn without custom aligment
845     if (memalignsize != 0 || !alignment.isDefault())
846         ofs = alignmember(alignment, memalignsize, ofs);
847 
848     uint memoffset = ofs;
849     ofs += memsize;
850     if (ofs > aggsize)
851         aggsize = ofs;
852     if (!isunion)
853     {
854         nextoffset = ofs;
855         //printf("     revised nextoffset:   %u\n", ofs);
856     }
857 
858     if (aggalignsize < actualAlignment)
859         aggalignsize = actualAlignment;
860 
861     return memoffset;
862 }