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 }