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-2020 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
13  */
14 
15 module dmd.aggregate;
16 
17 import core.stdc.stdio;
18 import core.checkedint;
19 
20 import dmd.aliasthis;
21 import dmd.apply;
22 import dmd.arraytypes;
23 import dmd.gluelayer : Symbol;
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.id;
35 import dmd.identifier;
36 import dmd.mtype;
37 import dmd.tokens;
38 import dmd.typesem : defaultInit;
39 import dmd.visitor;
40 
41 enum Sizeok : int
42 {
43     none,           // size of aggregate is not yet able to compute
44     fwd,            // size of aggregate is ready to compute
45     inProcess,      // in the midst of computing the size
46     done,           // size of aggregate is set correctly
47 }
48 
49 enum Baseok : int
50 {
51     none,             // base classes not computed yet
52     start,            // in process of resolving base classes
53     done,             // all base classes are resolved
54     semanticdone,     // all base classes semantic done
55 }
56 
57 /**
58  * The ClassKind enum is used in AggregateDeclaration AST nodes to
59  * specify the linkage type of the struct/class/interface or if it
60  * is an anonymous class. If the class is anonymous it is also
61  * considered to be a D class.
62  */
63 enum ClassKind : int
64 {
65     /// the aggregate is a d(efault) class
66     d,
67     /// the aggregate is a C++ struct/class/interface
68     cpp,
69     /// the aggregate is an Objective-C class/interface
70     objc,
71 }
72 
73 /***********************************************************
74  */
75 extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
76 {
77     Type type;
78     StorageClass storage_class;
79     Prot protection;
80     uint structsize;        // size of struct
81     uint alignsize;         // size of struct for alignment purposes
82     VarDeclarations fields; // VarDeclaration fields
83     Sizeok sizeok = Sizeok.none;  // set when structsize contains valid data
84     Dsymbol deferred;       // any deferred semantic2() or semantic3() symbol
85 
86     /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
87     ClassKind classKind;
88     /// Specify whether to mangle the aggregate as a `class` or a `struct`
89     /// This information is used by the MSVC mangler
90     /// Only valid for class and struct. TODO: Merge with ClassKind ?
91     CPPMANGLE cppmangle;
92 
93     /* !=null if is nested
94      * pointing to the dsymbol that directly enclosing it.
95      * 1. The function that enclosing it (nested struct and class)
96      * 2. The class that enclosing it (nested class only)
97      * 3. If enclosing aggregate is template, its enclosing dsymbol.
98      * See AggregateDeclaraton::makeNested for the details.
99      */
100     Dsymbol enclosing;
101 
102     VarDeclaration vthis;   // 'this' parameter if this aggregate is nested
103     VarDeclaration vthis2;  // 'this' parameter if this aggregate is a template and is nested
104 
105     // Special member functions
106     FuncDeclarations invs;          // Array of invariants
107     FuncDeclaration inv;            // invariant
108     NewDeclaration aggNew;          // allocator
109 
110     // CtorDeclaration or TemplateDeclaration
111     Dsymbol ctor;
112 
113     // default constructor - should have no arguments, because
114     // it would be stored in TypeInfo_Class.defaultConstructor
115     CtorDeclaration defaultCtor;
116 
117     AliasThis aliasthis;    // forward unresolved lookups to aliasthis
118     bool noDefaultCtor;     // no default construction
119 
120     DtorDeclarations dtors; // Array of destructors
121     DtorDeclaration dtor;   // aggregate destructor
122     DtorDeclaration primaryDtor; // non-deleting C++ destructor, same as dtor for D
123     DtorDeclaration tidtor; // aggregate destructor used in TypeInfo (must have extern(D) ABI)
124     FuncDeclaration fieldDtor;   // aggregate destructor for just the fields
125 
126     Expression getRTInfo;   // pointer to GC info generated by object.RTInfo(this)
127 
128     final extern (D) this(const ref Loc loc, Identifier id)
129     {
130         super(loc, id);
131         protection = Prot(Prot.Kind.public_);
132     }
133 
134     /***************************************
135      * Create a new scope from sc.
136      * semantic, semantic2 and semantic3 will use this for aggregate members.
137      */
138     Scope* newScope(Scope* sc)
139     {
140         auto sc2 = sc.push(this);
141         sc2.stc &= STCFlowThruAggregate;
142         sc2.parent = this;
143         sc2.inunion = isUnionDeclaration();
144         sc2.protection = Prot(Prot.Kind.public_);
145         sc2.explicitProtection = 0;
146         sc2.aligndecl = null;
147         sc2.userAttribDecl = null;
148         sc2.namespace = null;
149         return sc2;
150     }
151 
152     override final void setScope(Scope* sc)
153     {
154         // Might need a scope to resolve forward references. The check for
155         // semanticRun prevents unnecessary setting of _scope during deferred
156         // setScope phases for aggregates which already finished semantic().
157         // See https://issues.dlang.org/show_bug.cgi?id=16607
158         if (semanticRun < PASS.semanticdone)
159             ScopeDsymbol.setScope(sc);
160     }
161 
162     /***************************************
163      * Find all instance fields, then push them into `fields`.
164      *
165      * Runs semantic() for all instance field variables, but also
166      * the field types can remain yet not resolved forward references,
167      * except direct recursive definitions.
168      * After the process sizeok is set to Sizeok.fwd.
169      *
170      * Returns:
171      *      false if any errors occur.
172      */
173     final bool determineFields()
174     {
175         if (_scope)
176             dsymbolSemantic(this, null);
177         if (sizeok != Sizeok.none)
178             return true;
179 
180         //printf("determineFields() %s, fields.dim = %d\n", toChars(), fields.dim);
181         // determineFields can be called recursively from one of the fields's v.semantic
182         fields.setDim(0);
183 
184         static int func(Dsymbol s, AggregateDeclaration ad)
185         {
186             auto v = s.isVarDeclaration();
187             if (!v)
188                 return 0;
189             if (v.storage_class & STC.manifest)
190                 return 0;
191 
192             if (v.semanticRun < PASS.semanticdone)
193                 v.dsymbolSemantic(null);
194             // Return in case a recursive determineFields triggered by v.semantic already finished
195             if (ad.sizeok != Sizeok.none)
196                 return 1;
197 
198             if (v.aliassym)
199                 return 0;   // If this variable was really a tuple, skip it.
200 
201             if (v.storage_class & (STC.static_ | STC.extern_ | STC.tls | STC.gshared | STC.manifest | STC.ctfe | STC.templateparameter))
202                 return 0;
203             if (!v.isField() || v.semanticRun < PASS.semanticdone)
204                 return 1;   // unresolvable forward reference
205 
206             ad.fields.push(v);
207 
208             if (v.storage_class & STC.ref_)
209                 return 0;
210             auto tv = v.type.baseElemOf();
211             if (tv.ty != Tstruct)
212                 return 0;
213             if (ad == (cast(TypeStruct)tv).sym)
214             {
215                 const(char)* psz = (v.type.toBasetype().ty == Tsarray) ? "static array of " : "";
216                 ad.error("cannot have field `%s` with %ssame struct type", v.toChars(), psz);
217                 ad.type = Type.terror;
218                 ad.errors = true;
219                 return 1;
220             }
221             return 0;
222         }
223 
224         if (members)
225         {
226             for (size_t i = 0; i < members.dim; i++)
227             {
228                 auto s = (*members)[i];
229                 if (s.apply(&func, this))
230                 {
231                     if (sizeok != Sizeok.none)
232                     {
233                         // recursive determineFields already finished
234                         return true;
235                     }
236                     return false;
237                 }
238             }
239         }
240 
241         if (sizeok != Sizeok.done)
242             sizeok = Sizeok.fwd;
243 
244         return true;
245     }
246 
247     /***************************************
248      * Returns:
249      *      The total number of fields minus the number of hidden fields.
250      */
251     final size_t nonHiddenFields()
252     {
253         return fields.dim - isNested() - (vthis2 !is null);
254     }
255 
256     /***************************************
257      * Collect all instance fields, then determine instance size.
258      * Returns:
259      *      false if failed to determine the size.
260      */
261     final bool determineSize(Loc loc)
262     {
263         //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
264 
265         // The previous instance size finalizing had:
266         if (type.ty == Terror)
267             return false;   // failed already
268         if (sizeok == Sizeok.done)
269             return true;    // succeeded
270 
271         if (!members)
272         {
273             error(loc, "unknown size");
274             return false;
275         }
276 
277         if (_scope)
278             dsymbolSemantic(this, null);
279 
280         // Determine the instance size of base class first.
281         if (auto cd = isClassDeclaration())
282         {
283             cd = cd.baseClass;
284             if (cd && !cd.determineSize(loc))
285                 goto Lfail;
286         }
287 
288         // Determine instance fields when sizeok == Sizeok.none
289         if (!determineFields())
290             goto Lfail;
291         if (sizeok != Sizeok.done)
292             finalizeSize();
293 
294         // this aggregate type has:
295         if (type.ty == Terror)
296             return false;   // marked as invalid during the finalizing.
297         if (sizeok == Sizeok.done)
298             return true;    // succeeded to calculate instance size.
299 
300     Lfail:
301         // There's unresolvable forward reference.
302         if (type != Type.terror)
303             error(loc, "no size because of forward reference");
304         // Don't cache errors from speculative semantic, might be resolvable later.
305         // https://issues.dlang.org/show_bug.cgi?id=16574
306         if (!global.gag)
307         {
308             type = Type.terror;
309             errors = true;
310         }
311         return false;
312     }
313 
314     abstract void finalizeSize();
315 
316     override final d_uns64 size(const ref Loc loc)
317     {
318         //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
319         bool ok = determineSize(loc);
320         //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
321         return ok ? structsize : SIZE_INVALID;
322     }
323 
324     /***************************************
325      * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
326      * field initializers have unique memory space on instance.
327      * Returns:
328      *      true if any errors happen.
329      */
330     extern (D) final bool checkOverlappedFields()
331     {
332         //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
333         assert(sizeok == Sizeok.done);
334         size_t nfields = fields.dim;
335         if (isNested())
336         {
337             auto cd = isClassDeclaration();
338             if (!cd || !cd.baseClass || !cd.baseClass.isNested())
339                 nfields--;
340             if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
341                 nfields--;
342         }
343         bool errors = false;
344 
345         // Fill in missing any elements with default initializers
346         foreach (i; 0 .. nfields)
347         {
348             auto vd = fields[i];
349             if (vd.errors)
350             {
351                 errors = true;
352                 continue;
353             }
354 
355             const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
356 
357             // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
358             foreach (j; 0 .. nfields)
359             {
360                 if (i == j)
361                     continue;
362                 auto v2 = fields[j];
363                 if (v2.errors)
364                 {
365                     errors = true;
366                     continue;
367                 }
368                 if (!vd.isOverlappedWith(v2))
369                     continue;
370 
371                 // vd and v2 are overlapping.
372                 vd.overlapped = true;
373                 v2.overlapped = true;
374 
375                 if (!MODimplicitConv(vd.type.mod, v2.type.mod))
376                     v2.overlapUnsafe = true;
377                 if (!MODimplicitConv(v2.type.mod, vd.type.mod))
378                     vd.overlapUnsafe = true;
379 
380                 if (i > j)
381                     continue;
382 
383                 if (!v2._init)
384                     continue;
385 
386                 if (v2._init.isVoidInitializer())
387                     continue;
388 
389                 if (vd._init && !vdIsVoidInit && v2._init)
390                 {
391                     .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
392                     errors = true;
393                 }
394                 else if (v2._init && i < j)
395                 {
396                     // @@@DEPRECATED_v2.086@@@.
397                     .deprecation(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
398                         v2.toChars(), v2._init.toChars(), vd.toChars());
399                     //errors = true;
400                 }
401             }
402         }
403         return errors;
404     }
405 
406     /***************************************
407      * Fill out remainder of elements[] with default initializers for fields[].
408      * Params:
409      *      loc         = location
410      *      elements    = explicit arguments which given to construct object.
411      *      ctorinit    = true if the elements will be used for default initialization.
412      * Returns:
413      *      false if any errors occur.
414      *      Otherwise, returns true and the missing arguments will be pushed in elements[].
415      */
416     final bool fill(Loc loc, Expressions* elements, bool ctorinit)
417     {
418         //printf("AggregateDeclaration::fill() %s\n", toChars());
419         assert(sizeok == Sizeok.done);
420         assert(elements);
421         const nfields = nonHiddenFields();
422         bool errors = false;
423 
424         size_t dim = elements.dim;
425         elements.setDim(nfields);
426         foreach (size_t i; dim .. nfields)
427             (*elements)[i] = null;
428 
429         // Fill in missing any elements with default initializers
430         foreach (i; 0 .. nfields)
431         {
432             if ((*elements)[i])
433                 continue;
434 
435             auto vd = fields[i];
436             auto vx = vd;
437             if (vd._init && vd._init.isVoidInitializer())
438                 vx = null;
439 
440             // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
441             size_t fieldi = i;
442             foreach (j; 0 .. nfields)
443             {
444                 if (i == j)
445                     continue;
446                 auto v2 = fields[j];
447                 if (!vd.isOverlappedWith(v2))
448                     continue;
449 
450                 if ((*elements)[j])
451                 {
452                     vx = null;
453                     break;
454                 }
455                 if (v2._init && v2._init.isVoidInitializer())
456                     continue;
457 
458                 version (all)
459                 {
460                     /* Prefer first found non-void-initialized field
461                      * union U { int a; int b = 2; }
462                      * U u;    // Error: overlapping initialization for field a and b
463                      */
464                     if (!vx)
465                     {
466                         vx = v2;
467                         fieldi = j;
468                     }
469                     else if (v2._init)
470                     {
471                         .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
472                         errors = true;
473                     }
474                 }
475                 else
476                 {
477                     // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
478 
479                     /* Prefer explicitly initialized field
480                      * union U { int a; int b = 2; }
481                      * U u;    // OK (u.b == 2)
482                      */
483                     if (!vx || !vx._init && v2._init)
484                     {
485                         vx = v2;
486                         fieldi = j;
487                     }
488                     else if (vx != vd && !vx.isOverlappedWith(v2))
489                     {
490                         // Both vx and v2 fills vd, but vx and v2 does not overlap
491                     }
492                     else if (vx._init && v2._init)
493                     {
494                         .error(loc, "overlapping default initialization for field `%s` and `%s`",
495                             v2.toChars(), vd.toChars());
496                         errors = true;
497                     }
498                     else
499                         assert(vx._init || !vx._init && !v2._init);
500                 }
501             }
502             if (vx)
503             {
504                 Expression e;
505                 if (vx.type.size() == 0)
506                 {
507                     e = null;
508                 }
509                 else if (vx._init)
510                 {
511                     assert(!vx._init.isVoidInitializer());
512                     if (vx.inuse)   // https://issues.dlang.org/show_bug.cgi?id=18057
513                     {
514                         vx.error(loc, "recursive initialization of field");
515                         errors = true;
516                     }
517                     else
518                         e = vx.getConstInitializer(false);
519                 }
520                 else
521                 {
522                     if ((vx.storage_class & STC.nodefaultctor) && !ctorinit)
523                     {
524                         .error(loc, "field `%s.%s` must be initialized because it has no default constructor",
525                             type.toChars(), vx.toChars());
526                         errors = true;
527                     }
528                     /* https://issues.dlang.org/show_bug.cgi?id=12509
529                      * Get the element of static array type.
530                      */
531                     Type telem = vx.type;
532                     if (telem.ty == Tsarray)
533                     {
534                         /* We cannot use Type::baseElemOf() here.
535                          * If the bottom of the Tsarray is an enum type, baseElemOf()
536                          * will return the base of the enum, and its default initializer
537                          * would be different from the enum's.
538                          */
539                         while (telem.toBasetype().ty == Tsarray)
540                             telem = (cast(TypeSArray)telem.toBasetype()).next;
541                         if (telem.ty == Tvoid)
542                             telem = Type.tuns8.addMod(telem.mod);
543                     }
544                     if (telem.needsNested() && ctorinit)
545                         e = telem.defaultInit(loc);
546                     else
547                         e = telem.defaultInitLiteral(loc);
548                 }
549                 (*elements)[fieldi] = e;
550             }
551         }
552         foreach (e; *elements)
553         {
554             if (e && e.op == TOK.error)
555                 return false;
556         }
557 
558         return !errors;
559     }
560 
561     /****************************
562      * Do byte or word alignment as necessary.
563      * Align sizes of 0, as we may not know array sizes yet.
564      * Params:
565      *   alignment = struct alignment that is in effect
566      *   size = alignment requirement of field
567      *   poffset = pointer to offset to be aligned
568      */
569     extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe
570     {
571         //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
572         switch (alignment)
573         {
574         case cast(structalign_t)1:
575             // No alignment
576             break;
577 
578         case cast(structalign_t)STRUCTALIGN_DEFAULT:
579             // Alignment in Target::fieldalignsize must match what the
580             // corresponding C compiler's default alignment behavior is.
581             assert(size > 0 && !(size & (size - 1)));
582             *poffset = (*poffset + size - 1) & ~(size - 1);
583             break;
584 
585         default:
586             // Align on alignment boundary, which must be a positive power of 2
587             assert(alignment > 0 && !(alignment & (alignment - 1)));
588             *poffset = (*poffset + alignment - 1) & ~(alignment - 1);
589             break;
590         }
591     }
592 
593     /****************************************
594      * Place a member (mem) into an aggregate (agg), which can be a struct, union or class
595      * Returns:
596      *      offset to place field at
597      *
598      * nextoffset:    next location in aggregate
599      * memsize:       size of member
600      * memalignsize:  natural alignment of member
601      * alignment:     alignment in effect for this member
602      * paggsize:      size of aggregate (updated)
603      * paggalignsize: alignment of aggregate (updated)
604      * isunion:       the aggregate is a union
605      */
606     extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize,
607         structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion)
608     {
609         uint ofs = *nextoffset;
610 
611         const uint actualAlignment =
612             alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment;
613 
614         // Ensure no overflow
615         bool overflow;
616         const sz = addu(memsize, actualAlignment, overflow);
617         addu(ofs, sz, overflow);
618         if (overflow) assert(0);
619 
620         alignmember(alignment, memalignsize, &ofs);
621         uint memoffset = ofs;
622         ofs += memsize;
623         if (ofs > *paggsize)
624             *paggsize = ofs;
625         if (!isunion)
626             *nextoffset = ofs;
627 
628         if (*paggalignsize < actualAlignment)
629             *paggalignsize = actualAlignment;
630 
631         return memoffset;
632     }
633 
634     override final Type getType()
635     {
636         return type;
637     }
638 
639     // is aggregate deprecated?
640     override final bool isDeprecated() const
641     {
642         return !!(this.storage_class & STC.deprecated_);
643     }
644 
645     /// Flag this aggregate as deprecated
646     final void setDeprecated()
647     {
648         this.storage_class |= STC.deprecated_;
649     }
650 
651     /****************************************
652      * Returns true if there's an extra member which is the 'this'
653      * pointer to the enclosing context (enclosing aggregate or function)
654      */
655     final bool isNested() const
656     {
657         return enclosing !is null;
658     }
659 
660     /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
661      */
662     extern (D) final void makeNested()
663     {
664         if (enclosing) // if already nested
665             return;
666         if (sizeok == Sizeok.done)
667             return;
668         if (isUnionDeclaration() || isInterfaceDeclaration())
669             return;
670         if (storage_class & STC.static_)
671             return;
672 
673         // If nested struct, add in hidden 'this' pointer to outer scope
674         auto s = toParentLocal();
675         if (!s)
676             s = toParent2();
677         if (!s)
678             return;
679         Type t = null;
680         if (auto fd = s.isFuncDeclaration())
681         {
682             enclosing = fd;
683 
684             /* https://issues.dlang.org/show_bug.cgi?id=14422
685              * If a nested class parent is a function, its
686              * context pointer (== `outer`) should be void* always.
687              */
688             t = Type.tvoidptr;
689         }
690         else if (auto ad = s.isAggregateDeclaration())
691         {
692             if (isClassDeclaration() && ad.isClassDeclaration())
693             {
694                 enclosing = ad;
695             }
696             else if (isStructDeclaration())
697             {
698                 if (auto ti = ad.parent.isTemplateInstance())
699                 {
700                     enclosing = ti.enclosing;
701                 }
702             }
703             t = ad.handleType();
704         }
705         if (enclosing)
706         {
707             //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
708             assert(t);
709             if (t.ty == Tstruct)
710                 t = Type.tvoidptr; // t should not be a ref type
711 
712             assert(!vthis);
713             vthis = new ThisDeclaration(loc, t);
714             //vthis.storage_class |= STC.ref_;
715 
716             // Emulate vthis.addMember()
717             members.push(vthis);
718 
719             // Emulate vthis.dsymbolSemantic()
720             vthis.storage_class |= STC.field;
721             vthis.parent = this;
722             vthis.protection = Prot(Prot.Kind.public_);
723             vthis.alignment = t.alignment();
724             vthis.semanticRun = PASS.semanticdone;
725 
726             if (sizeok == Sizeok.fwd)
727                 fields.push(vthis);
728 
729             makeNested2();
730         }
731     }
732 
733     /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
734      */
735     extern (D) final void makeNested2()
736     {
737         if (vthis2)
738             return;
739         if (!vthis)
740             makeNested();   // can't add second before first
741         if (!vthis)
742             return;
743         if (sizeok == Sizeok.done)
744             return;
745         if (isUnionDeclaration() || isInterfaceDeclaration())
746             return;
747         if (storage_class & STC.static_)
748             return;
749 
750         auto s0 = toParentLocal();
751         auto s = toParent2();
752         if (!s || !s0 || s == s0)
753             return;
754         auto cd = s.isClassDeclaration();
755         Type t = cd ? cd.type : Type.tvoidptr;
756 
757         vthis2 = new ThisDeclaration(loc, t);
758         //vthis2.storage_class |= STC.ref_;
759 
760         // Emulate vthis2.addMember()
761         members.push(vthis2);
762 
763         // Emulate vthis2.dsymbolSemantic()
764         vthis2.storage_class |= STC.field;
765         vthis2.parent = this;
766         vthis2.protection = Prot(Prot.Kind.public_);
767         vthis2.alignment = t.alignment();
768         vthis2.semanticRun = PASS.semanticdone;
769 
770         if (sizeok == Sizeok.fwd)
771             fields.push(vthis2);
772     }
773 
774     override final bool isExport() const
775     {
776         return protection.kind == Prot.Kind.export_;
777     }
778 
779     /*******************************************
780      * Look for constructor declaration.
781      */
782     final Dsymbol searchCtor()
783     {
784         auto s = search(Loc.initial, Id.ctor);
785         if (s)
786         {
787             if (!(s.isCtorDeclaration() ||
788                   s.isTemplateDeclaration() ||
789                   s.isOverloadSet()))
790             {
791                 s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation");
792                 errors = true;
793                 s = null;
794             }
795         }
796         if (s && s.toParent() != this)
797             s = null; // search() looks through ancestor classes
798         if (s)
799         {
800             // Finish all constructors semantics to determine this.noDefaultCtor.
801             struct SearchCtor
802             {
803                 extern (C++) static int fp(Dsymbol s, void* ctxt)
804                 {
805                     auto f = s.isCtorDeclaration();
806                     if (f && f.semanticRun == PASS.init)
807                         f.dsymbolSemantic(null);
808                     return 0;
809                 }
810             }
811 
812             for (size_t i = 0; i < members.dim; i++)
813             {
814                 auto sm = (*members)[i];
815                 sm.apply(&SearchCtor.fp, null);
816             }
817         }
818         return s;
819     }
820 
821     override final Prot prot() pure nothrow @nogc @safe
822     {
823         return protection;
824     }
825 
826     // 'this' type
827     final Type handleType()
828     {
829         return type;
830     }
831 
832     // Back end
833     Symbol* stag; // tag symbol for debug data
834     Symbol* sinit;
835 
836     override final inout(AggregateDeclaration) isAggregateDeclaration() inout
837     {
838         return this;
839     }
840 
841     override void accept(Visitor v)
842     {
843         v.visit(this);
844     }
845 }