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