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