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