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