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-2022 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.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 * Give a nice string for a class kind for error messages
62 * Params:
63 * c = class kind
64 * Returns:
65 * 0-terminated string for `c`
66 */
toChars(ClassKind c)67 const(char)* toChars(ClassKind c)
68 {
69 final switch (c)
70 {
71 case ClassKind.d:
72 return "D";
73 case ClassKind.cpp:
74 return "C++";
75 case ClassKind.objc:
76 return "Objective-C";
77 case ClassKind.c:
78 return "C";
79 }
80 }
81
82 /**
83 * If an aggregate has a pargma(mangle, ...) this holds the information
84 * to mangle.
85 */
86 struct MangleOverride
87 {
88 Dsymbol agg; // The symbol to copy template parameters from (if any)
89 Identifier id; // the name to override the aggregate's with, defaults to agg.ident
90 }
91
92 /***********************************************************
93 * Abstract aggregate as a common ancestor for Class- and StructDeclaration.
94 */
95 extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
96 {
97 Type type; ///
98 StorageClass storage_class; ///
99 uint structsize; /// size of struct
100 uint alignsize; /// size of struct for alignment purposes
101 VarDeclarations fields; /// VarDeclaration fields
102 Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol
103
104 /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
105 ClassKind classKind;
106 /// Specify whether to mangle the aggregate as a `class` or a `struct`
107 /// This information is used by the MSVC mangler
108 /// Only valid for class and struct. TODO: Merge with ClassKind ?
109 CPPMANGLE cppmangle;
110
111 /// overridden symbol with pragma(mangle, "...") if not null
112 MangleOverride* mangleOverride;
113
114 /**
115 * !=null if is nested
116 * pointing to the dsymbol that directly enclosing it.
117 * 1. The function that enclosing it (nested struct and class)
118 * 2. The class that enclosing it (nested class only)
119 * 3. If enclosing aggregate is template, its enclosing dsymbol.
120 *
121 * See AggregateDeclaraton::makeNested for the details.
122 */
123 Dsymbol enclosing;
124
125 VarDeclaration vthis; /// 'this' parameter if this aggregate is nested
126 VarDeclaration vthis2; /// 'this' parameter if this aggregate is a template and is nested
127
128 // Special member functions
129 FuncDeclarations invs; /// Array of invariants
130 FuncDeclaration inv; /// Merged invariant calling all members of invs
131
132 /// CtorDeclaration or TemplateDeclaration
133 Dsymbol ctor;
134
135 /// default constructor - should have no arguments, because
136 /// it would be stored in TypeInfo_Class.defaultConstructor
137 CtorDeclaration defaultCtor;
138
139 AliasThis aliasthis; /// forward unresolved lookups to aliasthis
140
141 DtorDeclarations userDtors; /// user-defined destructors (`~this()`) - mixins can yield multiple ones
142 DtorDeclaration aggrDtor; /// aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes)
143 DtorDeclaration dtor; /// the aggregate destructor exposed as `__xdtor` alias
144 /// (same as aggrDtor, except for C++ classes with virtual dtor on Windows)
145 DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
146 DtorDeclaration fieldDtor; /// function destructing (non-inherited) fields
147
148 Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
149
150 ///
151 Visibility visibility;
152 bool noDefaultCtor; /// no default construction
153 bool disableNew; /// disallow allocations using `new`
154 Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data
155
this(const ref Loc loc,Identifier id)156 final extern (D) this(const ref Loc loc, Identifier id)
157 {
158 super(loc, id);
159 visibility = Visibility(Visibility.Kind.public_);
160 }
161
162 /***************************************
163 * Create a new scope from sc.
164 * semantic, semantic2 and semantic3 will use this for aggregate members.
165 */
newScope(Scope * sc)166 Scope* newScope(Scope* sc)
167 {
168 auto sc2 = sc.push(this);
169 sc2.stc &= STC.flowThruAggregate;
170 sc2.parent = this;
171 sc2.inunion = isUnionDeclaration();
172 sc2.visibility = Visibility(Visibility.Kind.public_);
173 sc2.explicitVisibility = 0;
174 sc2.aligndecl = null;
175 sc2.userAttribDecl = null;
176 sc2.namespace = null;
177 return sc2;
178 }
179
setScope(Scope * sc)180 override final void setScope(Scope* sc)
181 {
182 // Might need a scope to resolve forward references. The check for
183 // semanticRun prevents unnecessary setting of _scope during deferred
184 // setScope phases for aggregates which already finished semantic().
185 // See https://issues.dlang.org/show_bug.cgi?id=16607
186 if (semanticRun < PASS.semanticdone)
187 ScopeDsymbol.setScope(sc);
188 }
189
190 /***************************************
191 * Returns:
192 * The total number of fields minus the number of hidden fields.
193 */
nonHiddenFields()194 final size_t nonHiddenFields()
195 {
196 return fields.dim - isNested() - (vthis2 !is null);
197 }
198
199 /***************************************
200 * Collect all instance fields, then determine instance size.
201 * Returns:
202 * false if failed to determine the size.
203 */
determineSize(const ref Loc loc)204 final bool determineSize(const ref Loc loc)
205 {
206 //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
207
208 // The previous instance size finalizing had:
209 if (type.ty == Terror)
210 return false; // failed already
211 if (sizeok == Sizeok.done)
212 return true; // succeeded
213
214 if (!members)
215 {
216 error(loc, "unknown size");
217 return false;
218 }
219
220 if (_scope)
221 dsymbolSemantic(this, null);
222
223 // Determine the instance size of base class first.
224 if (auto cd = isClassDeclaration())
225 {
226 cd = cd.baseClass;
227 if (cd && !cd.determineSize(loc))
228 goto Lfail;
229 }
230
231 // Determine instance fields when sizeok == Sizeok.none
232 if (!this.determineFields())
233 goto Lfail;
234 if (sizeok != Sizeok.done)
235 finalizeSize();
236
237 // this aggregate type has:
238 if (type.ty == Terror)
239 return false; // marked as invalid during the finalizing.
240 if (sizeok == Sizeok.done)
241 return true; // succeeded to calculate instance size.
242
243 Lfail:
244 // There's unresolvable forward reference.
245 if (type != Type.terror)
246 error(loc, "no size because of forward reference");
247 // Don't cache errors from speculative semantic, might be resolvable later.
248 // https://issues.dlang.org/show_bug.cgi?id=16574
249 if (!global.gag)
250 {
251 type = Type.terror;
252 errors = true;
253 }
254 return false;
255 }
256
257 abstract void finalizeSize();
258
size(const ref Loc loc)259 override final uinteger_t size(const ref Loc loc)
260 {
261 //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
262 bool ok = determineSize(loc);
263 //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
264 return ok ? structsize : SIZE_INVALID;
265 }
266
267 /***************************************
268 * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
269 * field initializers have unique memory space on instance.
270 * Returns:
271 * true if any errors happen.
272 */
checkOverlappedFields()273 extern (D) final bool checkOverlappedFields()
274 {
275 //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
276 assert(sizeok == Sizeok.done);
277 size_t nfields = fields.dim;
278 if (isNested())
279 {
280 auto cd = isClassDeclaration();
281 if (!cd || !cd.baseClass || !cd.baseClass.isNested())
282 nfields--;
283 if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
284 nfields--;
285 }
286 bool errors = false;
287
288 // Fill in missing any elements with default initializers
289 foreach (i; 0 .. nfields)
290 {
291 auto vd = fields[i];
292 if (vd.errors)
293 {
294 errors = true;
295 continue;
296 }
297
298 const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
299
300 // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
301 foreach (j; 0 .. nfields)
302 {
303 if (i == j)
304 continue;
305 auto v2 = fields[j];
306 if (v2.errors)
307 {
308 errors = true;
309 continue;
310 }
311 if (!vd.isOverlappedWith(v2))
312 continue;
313
314 // vd and v2 are overlapping.
315 vd.overlapped = true;
316 v2.overlapped = true;
317
318 if (!MODimplicitConv(vd.type.mod, v2.type.mod))
319 v2.overlapUnsafe = true;
320 if (!MODimplicitConv(v2.type.mod, vd.type.mod))
321 vd.overlapUnsafe = true;
322
323 if (i > j)
324 continue;
325
326 if (!v2._init)
327 continue;
328
329 if (v2._init.isVoidInitializer())
330 continue;
331
332 if (vd._init && !vdIsVoidInit && v2._init)
333 {
334 .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
335 errors = true;
336 }
337 else if (v2._init && i < j)
338 {
339 .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
340 v2.toChars(), v2._init.toChars(), vd.toChars());
341 errors = true;
342 }
343 }
344 }
345 return errors;
346 }
347
348 /***************************************
349 * Fill out remainder of elements[] with default initializers for fields[].
350 * Params:
351 * loc = location
352 * elements = explicit arguments which given to construct object.
353 * ctorinit = true if the elements will be used for default initialization.
354 * Returns:
355 * false if any errors occur.
356 * Otherwise, returns true and the missing arguments will be pushed in elements[].
357 */
fill(const ref Loc loc,Expressions * elements,bool ctorinit)358 final bool fill(const ref Loc loc, Expressions* elements, bool ctorinit)
359 {
360 //printf("AggregateDeclaration::fill() %s\n", toChars());
361 assert(sizeok == Sizeok.done);
362 assert(elements);
363 const nfields = nonHiddenFields();
364 bool errors = false;
365
366 size_t dim = elements.dim;
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 */
alignmember(structalign_t alignment,uint memalignsize,uint * poffset)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 */
placeField(uint * nextoffset,uint memsize,uint memalignsize,structalign_t alignment,uint * paggsize,uint * paggalignsize,bool isunion)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
getType()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?
isDeprecated()606 override final bool isDeprecated() const
607 {
608 return !!(this.storage_class & STC.deprecated_);
609 }
610
611 /// Flag this aggregate as deprecated
setDeprecated()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 */
isNested()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 */
makeNested()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 */
makeNested2()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
isExport()740 override final bool isExport() const
741 {
742 return visibility.kind == Visibility.Kind.export_;
743 }
744
745 /*******************************************
746 * Look for constructor declaration.
747 */
searchCtor()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.dim; i++)
779 {
780 auto sm = (*members)[i];
781 sm.apply(&SearchCtor.fp, null);
782 }
783 }
784 return s;
785 }
786
visible()787 override final Visibility visible() pure nothrow @nogc @safe
788 {
789 return visibility;
790 }
791
792 // 'this' type
handleType()793 final Type handleType()
794 {
795 return type;
796 }
797
798 // Does this class have an invariant function?
hasInvariant()799 final bool hasInvariant()
800 {
801 return invs.length != 0;
802 }
803
804 // Back end
805 void* sinit; /// initializer symbol
806
inout(AggregateDeclaration)807 override final inout(AggregateDeclaration) isAggregateDeclaration() inout
808 {
809 return this;
810 }
811
accept(Visitor v)812 override void accept(Visitor v)
813 {
814 v.visit(this);
815 }
816 }
817