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.apply;
22 import dmd.arraytypes;
23 import dmd.astenums;
24 import dmd.attrib;
25 import dmd.declaration;
26 import dmd.dscope;
27 import dmd.dstruct;
28 import dmd.dsymbol;
29 import dmd.dsymbolsem;
30 import dmd.dtemplate;
31 import dmd.errors;
32 import dmd.expression;
33 import dmd.func;
34 import dmd.globals;
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)
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     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     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, "unknown size");
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, "no size because of forward reference");
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(), v2._init.toChars(), 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                         vx.error(loc, "recursive initialization of field");
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     /****************************
505      * Do byte or word alignment as necessary.
506      * Align sizes of 0, as we may not know array sizes yet.
507      * Params:
508      *   alignment = struct alignment that is in effect
509      *   memalignsize = natural alignment of field
510      *   poffset = pointer to offset to be aligned
511      */
512     extern (D) static void alignmember(structalign_t alignment, uint memalignsize, uint* poffset) pure nothrow @safe
513     {
514         //debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, *poffset);
515         uint alignvalue;
516 
517         if (alignment.isDefault())
518         {
519             // Alignment in Target::fieldalignsize must match what the
520             // corresponding C compiler's default alignment behavior is.
521             alignvalue = memalignsize;
522         }
523         else if (alignment.isPack())    // #pragma pack semantics
524         {
525             alignvalue = alignment.get();
526             if (memalignsize < alignvalue)
527                 alignvalue = memalignsize;      // align to min(memalignsize, alignment)
528         }
529         else if (alignment.get() > 1)
530         {
531             // Align on alignment boundary, which must be a positive power of 2
532             alignvalue = alignment.get();
533         }
534         else
535             return;
536 
537         assert(alignvalue > 0 && !(alignvalue & (alignvalue - 1)));
538         *poffset = (*poffset + alignvalue - 1) & ~(alignvalue - 1);
539     }
540 
541     /****************************************
542      * Place a field (mem) into an aggregate (agg), which can be a struct, union or class
543      * Params:
544      *    nextoffset    = location just past the end of the previous field in the aggregate.
545      *                    Updated to be just past the end of this field to be placed, i.e. the future nextoffset
546      *    memsize       = size of field
547      *    memalignsize  = natural alignment of field
548      *    alignment     = alignment in effect for this field
549      *    paggsize      = size of aggregate (updated)
550      *    paggalignsize = alignment of aggregate (updated)
551      *    isunion       = the aggregate is a union
552      * Returns:
553      *    aligned offset to place field at
554      *
555      */
556     extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize,
557         structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion)
558     {
559         uint ofs = *nextoffset;
560 
561         const uint actualAlignment =
562             alignment.isDefault() || alignment.isPack() && memalignsize < alignment.get()
563                         ? memalignsize : alignment.get();
564 
565         // Ensure no overflow
566         bool overflow;
567         const sz = addu(memsize, actualAlignment, overflow);
568         addu(ofs, sz, overflow);
569         if (overflow) assert(0);
570 
571         // Skip no-op for noreturn without custom aligment
572         if (memalignsize != 0 || !alignment.isDefault())
573             alignmember(alignment, memalignsize, &ofs);
574 
575         uint memoffset = ofs;
576         ofs += memsize;
577         if (ofs > *paggsize)
578             *paggsize = ofs;
579         if (!isunion)
580             *nextoffset = ofs;
581 
582         if (*paggalignsize < actualAlignment)
583             *paggalignsize = actualAlignment;
584 
585         return memoffset;
586     }
587 
588     override final Type getType()
589     {
590         /* Apply storage classes to forward references. (Issue 22254)
591          * Note: Avoid interfaces for now. Implementing qualifiers on interface
592          * definitions exposed some issues in their TypeInfo generation in DMD.
593          * Related PR: https://github.com/dlang/dmd/pull/13312
594          */
595         if (semanticRun == PASS.initial && !isInterfaceDeclaration())
596         {
597             auto stc = storage_class;
598             if (_scope)
599                 stc |= _scope.stc;
600             type = type.addSTC(stc);
601         }
602         return type;
603     }
604 
605     // is aggregate deprecated?
606     override final bool isDeprecated() const
607     {
608         return !!(this.storage_class & STC.deprecated_);
609     }
610 
611     /// Flag this aggregate as deprecated
612     final void setDeprecated()
613     {
614         this.storage_class |= STC.deprecated_;
615     }
616 
617     /****************************************
618      * Returns true if there's an extra member which is the 'this'
619      * pointer to the enclosing context (enclosing aggregate or function)
620      */
621     final bool isNested() const
622     {
623         return enclosing !is null;
624     }
625 
626     /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
627      */
628     extern (D) final void makeNested()
629     {
630         if (enclosing) // if already nested
631             return;
632         if (sizeok == Sizeok.done)
633             return;
634         if (isUnionDeclaration() || isInterfaceDeclaration())
635             return;
636         if (storage_class & STC.static_)
637             return;
638 
639         // If nested struct, add in hidden 'this' pointer to outer scope
640         auto s = toParentLocal();
641         if (!s)
642             s = toParent2();
643         if (!s)
644             return;
645         Type t = null;
646         if (auto fd = s.isFuncDeclaration())
647         {
648             enclosing = fd;
649 
650             /* https://issues.dlang.org/show_bug.cgi?id=14422
651              * If a nested class parent is a function, its
652              * context pointer (== `outer`) should be void* always.
653              */
654             t = Type.tvoidptr;
655         }
656         else if (auto ad = s.isAggregateDeclaration())
657         {
658             if (isClassDeclaration() && ad.isClassDeclaration())
659             {
660                 enclosing = ad;
661             }
662             else if (isStructDeclaration())
663             {
664                 if (auto ti = ad.parent.isTemplateInstance())
665                 {
666                     enclosing = ti.enclosing;
667                 }
668             }
669             t = ad.handleType();
670         }
671         if (enclosing)
672         {
673             //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
674             assert(t);
675             if (t.ty == Tstruct)
676                 t = Type.tvoidptr; // t should not be a ref type
677 
678             assert(!vthis);
679             vthis = new ThisDeclaration(loc, t);
680             //vthis.storage_class |= STC.ref_;
681 
682             // Emulate vthis.addMember()
683             members.push(vthis);
684 
685             // Emulate vthis.dsymbolSemantic()
686             vthis.storage_class |= STC.field;
687             vthis.parent = this;
688             vthis.visibility = Visibility(Visibility.Kind.public_);
689             vthis.alignment = t.alignment();
690             vthis.semanticRun = PASS.semanticdone;
691 
692             if (sizeok == Sizeok.fwd)
693                 fields.push(vthis);
694 
695             makeNested2();
696         }
697     }
698 
699     /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
700      */
701     extern (D) final void makeNested2()
702     {
703         if (vthis2)
704             return;
705         if (!vthis)
706             makeNested();   // can't add second before first
707         if (!vthis)
708             return;
709         if (sizeok == Sizeok.done)
710             return;
711         if (isUnionDeclaration() || isInterfaceDeclaration())
712             return;
713         if (storage_class & STC.static_)
714             return;
715 
716         auto s0 = toParentLocal();
717         auto s = toParent2();
718         if (!s || !s0 || s == s0)
719             return;
720         auto cd = s.isClassDeclaration();
721         Type t = cd ? cd.type : Type.tvoidptr;
722 
723         vthis2 = new ThisDeclaration(loc, t);
724         //vthis2.storage_class |= STC.ref_;
725 
726         // Emulate vthis2.addMember()
727         members.push(vthis2);
728 
729         // Emulate vthis2.dsymbolSemantic()
730         vthis2.storage_class |= STC.field;
731         vthis2.parent = this;
732         vthis2.visibility = Visibility(Visibility.Kind.public_);
733         vthis2.alignment = t.alignment();
734         vthis2.semanticRun = PASS.semanticdone;
735 
736         if (sizeok == Sizeok.fwd)
737             fields.push(vthis2);
738     }
739 
740     override final bool isExport() const
741     {
742         return visibility.kind == Visibility.Kind.export_;
743     }
744 
745     /*******************************************
746      * Look for constructor declaration.
747      */
748     final Dsymbol searchCtor()
749     {
750         auto s = search(Loc.initial, Id.ctor);
751         if (s)
752         {
753             if (!(s.isCtorDeclaration() ||
754                   s.isTemplateDeclaration() ||
755                   s.isOverloadSet()))
756             {
757                 s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation");
758                 errors = true;
759                 s = null;
760             }
761         }
762         if (s && s.toParent() != this)
763             s = null; // search() looks through ancestor classes
764         if (s)
765         {
766             // Finish all constructors semantics to determine this.noDefaultCtor.
767             struct SearchCtor
768             {
769                 extern (C++) static int fp(Dsymbol s, void* ctxt)
770                 {
771                     auto f = s.isCtorDeclaration();
772                     if (f && f.semanticRun == PASS.initial)
773                         f.dsymbolSemantic(null);
774                     return 0;
775                 }
776             }
777 
778             for (size_t i = 0; i < members.length; i++)
779             {
780                 auto sm = (*members)[i];
781                 sm.apply(&SearchCtor.fp, null);
782             }
783         }
784         return s;
785     }
786 
787     override final Visibility visible() pure nothrow @nogc @safe
788     {
789         return visibility;
790     }
791 
792     // 'this' type
793     final Type handleType()
794     {
795         return type;
796     }
797 
798     // Does this class have an invariant function?
799     final bool hasInvariant()
800     {
801         return invs.length != 0;
802     }
803 
804     // Back end
805     void* sinit;  /// initializer symbol
806 
807     override final inout(AggregateDeclaration) isAggregateDeclaration() inout
808     {
809         return this;
810     }
811 
812     override void accept(Visitor v)
813     {
814         v.visit(this);
815     }
816 }