1 /** 2 * Struct and union declarations. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions) 5 * 6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dstruct.d, _dstruct.d) 10 * Documentation: https://dlang.org/phobos/dmd_dstruct.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dstruct.d 12 */ 13 14 module dmd.dstruct; 15 16 import dmd.aggregate; 17 import dmd.arraytypes; 18 import dmd.declaration; 19 import dmd.dmodule; 20 import dmd.dscope; 21 import dmd.dsymbol; 22 import dmd.dsymbolsem; 23 import dmd.dtemplate; 24 import dmd.errors; 25 import dmd.expression; 26 import dmd.expressionsem; 27 import dmd.func; 28 import dmd.globals; 29 import dmd.id; 30 import dmd.identifier; 31 import dmd.mtype; 32 import dmd.opover; 33 import dmd.semantic3; 34 import dmd.target; 35 import dmd.tokens; 36 import dmd.typesem; 37 import dmd.typinf; 38 import dmd.visitor; 39 40 /*************************************** 41 * Search sd for a member function of the form: 42 * `extern (D) string toString();` 43 * Params: 44 * sd = struct declaration to search 45 * Returns: 46 * FuncDeclaration of `toString()` if found, `null` if not 47 */ 48 extern (C++) FuncDeclaration search_toString(StructDeclaration sd) 49 { 50 Dsymbol s = search_function(sd, Id.tostring); 51 FuncDeclaration fd = s ? s.isFuncDeclaration() : null; 52 if (fd) 53 { 54 __gshared TypeFunction tftostring; 55 if (!tftostring) 56 { 57 tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d); 58 tftostring = tftostring.merge().toTypeFunction(); 59 } 60 fd = fd.overloadExactMatch(tftostring); 61 } 62 return fd; 63 } 64 65 /*************************************** 66 * Request additional semantic analysis for TypeInfo generation. 67 * Params: 68 * sc = context 69 * t = type that TypeInfo is being generated for 70 */ 71 extern (C++) void semanticTypeInfo(Scope* sc, Type t) 72 { 73 if (sc) 74 { 75 if (sc.intypeof) 76 return; 77 if (sc.flags & (SCOPE.ctfe | SCOPE.compile)) 78 return; 79 } 80 81 if (!t) 82 return; 83 84 void visitVector(TypeVector t) 85 { 86 semanticTypeInfo(sc, t.basetype); 87 } 88 89 void visitAArray(TypeAArray t) 90 { 91 semanticTypeInfo(sc, t.index); 92 semanticTypeInfo(sc, t.next); 93 } 94 95 void visitStruct(TypeStruct t) 96 { 97 //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); 98 StructDeclaration sd = t.sym; 99 100 /* Step 1: create TypeInfoDeclaration 101 */ 102 if (!sc) // inline may request TypeInfo. 103 { 104 Scope scx; 105 scx._module = sd.getModule(); 106 getTypeInfoType(sd.loc, t, &scx); 107 sd.requestTypeInfo = true; 108 } 109 else if (!sc.minst) 110 { 111 // don't yet have to generate TypeInfo instance if 112 // the typeid(T) expression exists in speculative scope. 113 } 114 else 115 { 116 getTypeInfoType(sd.loc, t, sc); 117 sd.requestTypeInfo = true; 118 119 // https://issues.dlang.org/show_bug.cgi?id=15149 120 // if the typeid operand type comes from a 121 // result of auto function, it may be yet speculative. 122 // unSpeculative(sc, sd); 123 } 124 125 /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. 126 * This should be done even if typeid(T) exists in speculative scope. 127 * Because it may appear later in non-speculative scope. 128 */ 129 if (!sd.members) 130 return; // opaque struct 131 if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.dtor && !sd.xhash && !search_toString(sd)) 132 return; // none of TypeInfo-specific members 133 134 // If the struct is in a non-root module, run semantic3 to get 135 // correct symbols for the member function. 136 if (sd.semanticRun >= PASS.semantic3) 137 { 138 // semantic3 is already done 139 } 140 else if (TemplateInstance ti = sd.isInstantiated()) 141 { 142 if (ti.minst && !ti.minst.isRoot()) 143 Module.addDeferredSemantic3(sd); 144 } 145 else 146 { 147 if (sd.inNonRoot()) 148 { 149 //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); 150 Module.addDeferredSemantic3(sd); 151 } 152 } 153 } 154 155 void visitTuple(TypeTuple t) 156 { 157 if (t.arguments) 158 { 159 foreach (arg; *t.arguments) 160 { 161 semanticTypeInfo(sc, arg.type); 162 } 163 } 164 } 165 166 /* Note structural similarity of this Type walker to that in isSpeculativeType() 167 */ 168 169 Type tb = t.toBasetype(); 170 switch (tb.ty) 171 { 172 case Tvector: visitVector(tb.isTypeVector()); break; 173 case Taarray: visitAArray(tb.isTypeAArray()); break; 174 case Tstruct: visitStruct(tb.isTypeStruct()); break; 175 case Ttuple: visitTuple (tb.isTypeTuple()); break; 176 177 case Tclass: 178 case Tenum: break; 179 180 default: semanticTypeInfo(sc, tb.nextOf()); break; 181 } 182 } 183 184 enum StructFlags : int 185 { 186 none = 0x0, 187 hasPointers = 0x1, // NB: should use noPointers as in ClassFlags 188 } 189 190 enum StructPOD : int 191 { 192 no, // struct is not POD 193 yes, // struct is POD 194 fwd, // POD not yet computed 195 } 196 197 /*********************************************************** 198 * All `struct` declarations are an instance of this. 199 */ 200 extern (C++) class StructDeclaration : AggregateDeclaration 201 { 202 bool zeroInit; // !=0 if initialize with 0 fill 203 bool hasIdentityAssign; // true if has identity opAssign 204 bool hasBlitAssign; // true if opAssign is a blit 205 bool hasIdentityEquals; // true if has identity opEquals 206 bool hasNoFields; // has no fields 207 bool hasCopyCtor; // copy constructor 208 // Even if struct is defined as non-root symbol, some built-in operations 209 // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo. 210 // For those, today TypeInfo_Struct is generated in COMDAT. 211 bool requestTypeInfo; 212 213 FuncDeclarations postblits; // Array of postblit functions 214 FuncDeclaration postblit; // aggregate postblit 215 216 FuncDeclaration xeq; // TypeInfo_Struct.xopEquals 217 FuncDeclaration xcmp; // TypeInfo_Struct.xopCmp 218 FuncDeclaration xhash; // TypeInfo_Struct.xtoHash 219 extern (C++) __gshared FuncDeclaration xerreq; // object.xopEquals 220 extern (C++) __gshared FuncDeclaration xerrcmp; // object.xopCmp 221 222 structalign_t alignment; // alignment applied outside of the struct 223 StructPOD ispod; // if struct is POD 224 225 // ABI-specific type(s) if the struct can be passed in registers 226 TypeTuple argTypes; 227 228 extern (D) this(const ref Loc loc, Identifier id, bool inObject) 229 { 230 super(loc, id); 231 zeroInit = false; // assume false until we do semantic processing 232 ispod = StructPOD.fwd; 233 // For forward references 234 type = new TypeStruct(this); 235 236 if (inObject) 237 { 238 if (id == Id.ModuleInfo && !Module.moduleinfo) 239 Module.moduleinfo = this; 240 } 241 } 242 243 static StructDeclaration create(Loc loc, Identifier id, bool inObject) 244 { 245 return new StructDeclaration(loc, id, inObject); 246 } 247 248 override StructDeclaration syntaxCopy(Dsymbol s) 249 { 250 StructDeclaration sd = 251 s ? cast(StructDeclaration)s 252 : new StructDeclaration(loc, ident, false); 253 ScopeDsymbol.syntaxCopy(sd); 254 return sd; 255 } 256 257 final void semanticTypeInfoMembers() 258 { 259 if (xeq && 260 xeq._scope && 261 xeq.semanticRun < PASS.semantic3done) 262 { 263 uint errors = global.startGagging(); 264 xeq.semantic3(xeq._scope); 265 if (global.endGagging(errors)) 266 xeq = xerreq; 267 } 268 269 if (xcmp && 270 xcmp._scope && 271 xcmp.semanticRun < PASS.semantic3done) 272 { 273 uint errors = global.startGagging(); 274 xcmp.semantic3(xcmp._scope); 275 if (global.endGagging(errors)) 276 xcmp = xerrcmp; 277 } 278 279 FuncDeclaration ftostr = search_toString(this); 280 if (ftostr && 281 ftostr._scope && 282 ftostr.semanticRun < PASS.semantic3done) 283 { 284 ftostr.semantic3(ftostr._scope); 285 } 286 287 if (xhash && 288 xhash._scope && 289 xhash.semanticRun < PASS.semantic3done) 290 { 291 xhash.semantic3(xhash._scope); 292 } 293 294 if (postblit && 295 postblit._scope && 296 postblit.semanticRun < PASS.semantic3done) 297 { 298 postblit.semantic3(postblit._scope); 299 } 300 301 if (dtor && 302 dtor._scope && 303 dtor.semanticRun < PASS.semantic3done) 304 { 305 dtor.semantic3(dtor._scope); 306 } 307 } 308 309 override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) 310 { 311 //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags); 312 if (_scope && !symtab) 313 dsymbolSemantic(this, _scope); 314 315 if (!members || !symtab) // opaque or semantic() is not yet called 316 { 317 // .stringof is always defined (but may be hidden by some other symbol) 318 if(ident != Id.stringof) 319 error("is forward referenced when looking for `%s`", ident.toChars()); 320 return null; 321 } 322 323 return ScopeDsymbol.search(loc, ident, flags); 324 } 325 326 override const(char)* kind() const 327 { 328 return "struct"; 329 } 330 331 override final void finalizeSize() 332 { 333 //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok); 334 assert(sizeok != Sizeok.done); 335 336 if (sizeok == Sizeok.inProcess) 337 { 338 return; 339 } 340 sizeok = Sizeok.inProcess; 341 342 //printf("+StructDeclaration::finalizeSize() %s, fields.dim = %d, sizeok = %d\n", toChars(), fields.dim, sizeok); 343 344 fields.setDim(0); // workaround 345 346 // Set the offsets of the fields and determine the size of the struct 347 uint offset = 0; 348 bool isunion = isUnionDeclaration() !is null; 349 for (size_t i = 0; i < members.dim; i++) 350 { 351 Dsymbol s = (*members)[i]; 352 s.setFieldOffset(this, &offset, isunion); 353 } 354 if (type.ty == Terror) 355 { 356 errors = true; 357 return; 358 } 359 360 // 0 sized struct's are set to 1 byte 361 if (structsize == 0) 362 { 363 hasNoFields = true; 364 structsize = 1; 365 alignsize = 1; 366 } 367 368 // Round struct size up to next alignsize boundary. 369 // This will ensure that arrays of structs will get their internals 370 // aligned properly. 371 if (alignment == STRUCTALIGN_DEFAULT) 372 structsize = (structsize + alignsize - 1) & ~(alignsize - 1); 373 else 374 structsize = (structsize + alignment - 1) & ~(alignment - 1); 375 376 sizeok = Sizeok.done; 377 378 //printf("-StructDeclaration::finalizeSize() %s, fields.dim = %d, structsize = %d\n", toChars(), fields.dim, structsize); 379 380 if (errors) 381 return; 382 383 // Calculate fields[i].overlapped 384 if (checkOverlappedFields()) 385 { 386 errors = true; 387 return; 388 } 389 390 // Determine if struct is all zeros or not 391 zeroInit = true; 392 foreach (vd; fields) 393 { 394 if (vd._init) 395 { 396 if (vd._init.isVoidInitializer()) 397 /* Treat as 0 for the purposes of putting the initializer 398 * in the BSS segment, or doing a mass set to 0 399 */ 400 continue; 401 402 // Zero size fields are zero initialized 403 if (vd.type.size(vd.loc) == 0) 404 continue; 405 406 // Examine init to see if it is all 0s. 407 auto exp = vd.getConstInitializer(); 408 if (!exp || !_isZeroInit(exp)) 409 { 410 zeroInit = false; 411 break; 412 } 413 } 414 else if (!vd.type.isZeroInit(loc)) 415 { 416 zeroInit = false; 417 break; 418 } 419 } 420 421 argTypes = target.toArgTypes(type); 422 } 423 424 /*************************************** 425 * Fit elements[] to the corresponding types of the struct's fields. 426 * 427 * Params: 428 * loc = location to use for error messages 429 * sc = context 430 * elements = explicit arguments used to construct object 431 * stype = the constructed object type. 432 * Returns: 433 * false if any errors occur, 434 * otherwise true and elements[] are rewritten for the output. 435 */ 436 final bool fit(const ref Loc loc, Scope* sc, Expressions* elements, Type stype) 437 { 438 if (!elements) 439 return true; 440 441 const nfields = nonHiddenFields(); 442 size_t offset = 0; 443 for (size_t i = 0; i < elements.dim; i++) 444 { 445 Expression e = (*elements)[i]; 446 if (!e) 447 continue; 448 449 e = resolveProperties(sc, e); 450 if (i >= nfields) 451 { 452 if (i <= fields.dim && e.op == TOK.null_) 453 { 454 // CTFE sometimes creates null as hidden pointer; we'll allow this. 455 continue; 456 } 457 .error(loc, "more initializers than fields (%zu) of `%s`", nfields, toChars()); 458 return false; 459 } 460 VarDeclaration v = fields[i]; 461 if (v.offset < offset) 462 { 463 .error(loc, "overlapping initialization for `%s`", v.toChars()); 464 if (!isUnionDeclaration()) 465 { 466 enum errorMsg = "`struct` initializers that contain anonymous unions" ~ 467 " must initialize only the first member of a `union`. All subsequent" ~ 468 " non-overlapping fields are default initialized"; 469 .errorSupplemental(loc, errorMsg); 470 } 471 return false; 472 } 473 offset = cast(uint)(v.offset + v.type.size()); 474 475 Type t = v.type; 476 if (stype) 477 t = t.addMod(stype.mod); 478 Type origType = t; 479 Type tb = t.toBasetype(); 480 481 const hasPointers = tb.hasPointers(); 482 if (hasPointers) 483 { 484 if ((stype.alignment() < target.ptrsize || 485 (v.offset & (target.ptrsize - 1))) && 486 (sc.func && sc.func.setUnsafe())) 487 { 488 .error(loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code", 489 toChars(), v.toChars()); 490 return false; 491 } 492 } 493 494 /* Look for case of initializing a static array with a too-short 495 * string literal, such as: 496 * char[5] foo = "abc"; 497 * Allow this by doing an explicit cast, which will lengthen the string 498 * literal. 499 */ 500 if (e.op == TOK.string_ && tb.ty == Tsarray) 501 { 502 StringExp se = cast(StringExp)e; 503 Type typeb = se.type.toBasetype(); 504 TY tynto = tb.nextOf().ty; 505 if (!se.committed && 506 (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar && 507 se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger()) 508 { 509 e = se.castTo(sc, t); 510 goto L1; 511 } 512 } 513 514 while (!e.implicitConvTo(t) && tb.ty == Tsarray) 515 { 516 /* Static array initialization, as in: 517 * T[3][5] = e; 518 */ 519 t = tb.nextOf(); 520 tb = t.toBasetype(); 521 } 522 if (!e.implicitConvTo(t)) 523 t = origType; // restore type for better diagnostic 524 525 e = e.implicitCastTo(sc, t); 526 L1: 527 if (e.op == TOK.error) 528 return false; 529 530 (*elements)[i] = doCopyOrMove(sc, e); 531 } 532 return true; 533 } 534 535 /*************************************** 536 * Determine if struct is POD (Plain Old Data). 537 * 538 * POD is defined as: 539 * $(OL 540 * $(LI not nested) 541 * $(LI no postblits, destructors, or assignment operators) 542 * $(LI no `ref` fields or fields that are themselves non-POD) 543 * ) 544 * The idea being these are compatible with C structs. 545 * 546 * Returns: 547 * true if struct is POD 548 */ 549 final bool isPOD() 550 { 551 // If we've already determined whether this struct is POD. 552 if (ispod != StructPOD.fwd) 553 return (ispod == StructPOD.yes); 554 555 ispod = StructPOD.yes; 556 557 if (enclosing || postblit || dtor || hasCopyCtor) 558 { 559 ispod = StructPOD.no; 560 return false; 561 } 562 563 // Recursively check all fields are POD. 564 for (size_t i = 0; i < fields.dim; i++) 565 { 566 VarDeclaration v = fields[i]; 567 if (v.storage_class & STC.ref_) 568 { 569 ispod = StructPOD.no; 570 return false; 571 } 572 573 Type tv = v.type.baseElemOf(); 574 if (tv.ty == Tstruct) 575 { 576 TypeStruct ts = cast(TypeStruct)tv; 577 StructDeclaration sd = ts.sym; 578 if (!sd.isPOD()) 579 { 580 ispod = StructPOD.no; 581 return false; 582 } 583 } 584 } 585 586 return (ispod == StructPOD.yes); 587 } 588 589 override final inout(StructDeclaration) isStructDeclaration() inout 590 { 591 return this; 592 } 593 594 override void accept(Visitor v) 595 { 596 v.visit(this); 597 } 598 599 final uint numArgTypes() const 600 { 601 return argTypes && argTypes.arguments ? cast(uint) argTypes.arguments.dim : 0; 602 } 603 604 final Type argType(uint index) 605 { 606 return index < numArgTypes() ? (*argTypes.arguments)[index].type : null; 607 } 608 609 610 /*************************************** 611 * Verifies whether the struct declaration has a 612 * constructor that is not a copy constructor. 613 * Optionally, it can check whether the struct 614 * declaration has a regular constructor, that 615 * is not disabled. 616 * 617 * Params: 618 * checkDisabled = if the struct has a regular 619 non-disabled constructor 620 * Returns: 621 * true, if the struct has a regular (optionally, 622 * not disabled) constructor, false otherwise. 623 */ 624 final bool hasRegularCtor(bool checkDisabled = false) 625 { 626 if (!ctor) 627 return false; 628 629 bool result; 630 overloadApply(ctor, (Dsymbol s) 631 { 632 if (auto td = s.isTemplateDeclaration()) 633 { 634 if (checkDisabled && td.onemember) 635 { 636 if (auto ctorDecl = td.onemember.isCtorDeclaration()) 637 { 638 if (ctorDecl.storage_class & STC.disable) 639 return 0; 640 } 641 } 642 result = true; 643 return 1; 644 } 645 if (auto ctorDecl = s.isCtorDeclaration()) 646 { 647 if (!ctorDecl.isCpCtor && (!checkDisabled || !(ctorDecl.storage_class & STC.disable))) 648 { 649 result = true; 650 return 1; 651 } 652 } 653 return 0; 654 }); 655 return result; 656 } 657 } 658 659 /********************************** 660 * Determine if exp is all binary zeros. 661 * Params: 662 * exp = expression to check 663 * Returns: 664 * true if it's all binary 0 665 */ 666 private bool _isZeroInit(Expression exp) 667 { 668 switch (exp.op) 669 { 670 case TOK.int64: 671 return exp.toInteger() == 0; 672 673 case TOK.null_: 674 case TOK.false_: 675 return true; 676 677 case TOK.structLiteral: 678 { 679 auto sle = cast(StructLiteralExp) exp; 680 foreach (i; 0 .. sle.sd.fields.dim) 681 { 682 auto field = sle.sd.fields[i]; 683 if (field.type.size(field.loc)) 684 { 685 auto e = (*sle.elements)[i]; 686 if (e ? !_isZeroInit(e) 687 : !field.type.isZeroInit(field.loc)) 688 return false; 689 } 690 } 691 return true; 692 } 693 694 case TOK.arrayLiteral: 695 { 696 auto ale = cast(ArrayLiteralExp)exp; 697 698 const dim = ale.elements ? ale.elements.dim : 0; 699 700 if (ale.type.toBasetype().ty == Tarray) // if initializing a dynamic array 701 return dim == 0; 702 703 foreach (i; 0 .. dim) 704 { 705 if (!_isZeroInit(ale[i])) 706 return false; 707 } 708 709 /* Note that true is returned for all T[0] 710 */ 711 return true; 712 } 713 714 case TOK.string_: 715 { 716 StringExp se = cast(StringExp)exp; 717 718 if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array 719 return se.len == 0; 720 721 foreach (i; 0 .. se.len) 722 { 723 if (se.getCodeUnit(i)) 724 return false; 725 } 726 return true; 727 } 728 729 case TOK.vector: 730 { 731 auto ve = cast(VectorExp) exp; 732 return _isZeroInit(ve.e1); 733 } 734 735 case TOK.float64: 736 case TOK.complex80: 737 { 738 import dmd.root.ctfloat : CTFloat; 739 return (exp.toReal() is CTFloat.zero) && 740 (exp.toImaginary() is CTFloat.zero); 741 } 742 743 default: 744 return false; 745 } 746 } 747 748 /*********************************************************** 749 * Unions are a variation on structs. 750 */ 751 extern (C++) final class UnionDeclaration : StructDeclaration 752 { 753 extern (D) this(const ref Loc loc, Identifier id) 754 { 755 super(loc, id, false); 756 } 757 758 override UnionDeclaration syntaxCopy(Dsymbol s) 759 { 760 assert(!s); 761 auto ud = new UnionDeclaration(loc, ident); 762 StructDeclaration.syntaxCopy(ud); 763 return ud; 764 } 765 766 override const(char)* kind() const 767 { 768 return "union"; 769 } 770 771 override inout(UnionDeclaration) isUnionDeclaration() inout 772 { 773 return this; 774 } 775 776 override void accept(Visitor v) 777 { 778 v.visit(this); 779 } 780 }