1 /** 2 * Struct and union declarations. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions) 5 * 6 * Copyright: Copyright (C) 1999-2020 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 Dsymbol syntaxCopy(Dsymbol s) 249 { 250 StructDeclaration sd = 251 s ? cast(StructDeclaration)s 252 : new StructDeclaration(loc, ident, false); 253 return ScopeDsymbol.syntaxCopy(sd); 254 } 255 256 final void semanticTypeInfoMembers() 257 { 258 if (xeq && 259 xeq._scope && 260 xeq.semanticRun < PASS.semantic3done) 261 { 262 uint errors = global.startGagging(); 263 xeq.semantic3(xeq._scope); 264 if (global.endGagging(errors)) 265 xeq = xerreq; 266 } 267 268 if (xcmp && 269 xcmp._scope && 270 xcmp.semanticRun < PASS.semantic3done) 271 { 272 uint errors = global.startGagging(); 273 xcmp.semantic3(xcmp._scope); 274 if (global.endGagging(errors)) 275 xcmp = xerrcmp; 276 } 277 278 FuncDeclaration ftostr = search_toString(this); 279 if (ftostr && 280 ftostr._scope && 281 ftostr.semanticRun < PASS.semantic3done) 282 { 283 ftostr.semantic3(ftostr._scope); 284 } 285 286 if (xhash && 287 xhash._scope && 288 xhash.semanticRun < PASS.semantic3done) 289 { 290 xhash.semantic3(xhash._scope); 291 } 292 293 if (postblit && 294 postblit._scope && 295 postblit.semanticRun < PASS.semantic3done) 296 { 297 postblit.semantic3(postblit._scope); 298 } 299 300 if (dtor && 301 dtor._scope && 302 dtor.semanticRun < PASS.semantic3done) 303 { 304 dtor.semantic3(dtor._scope); 305 } 306 } 307 308 override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) 309 { 310 //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags); 311 if (_scope && !symtab) 312 dsymbolSemantic(this, _scope); 313 314 if (!members || !symtab) // opaque or semantic() is not yet called 315 { 316 // .stringof is always defined (but may be hidden by some other symbol) 317 if(ident != Id.stringof) 318 error("is forward referenced when looking for `%s`", ident.toChars()); 319 return null; 320 } 321 322 return ScopeDsymbol.search(loc, ident, flags); 323 } 324 325 override const(char)* kind() const 326 { 327 return "struct"; 328 } 329 330 override final void finalizeSize() 331 { 332 //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok); 333 assert(sizeok != Sizeok.done); 334 335 if (sizeok == Sizeok.inProcess) 336 { 337 return; 338 } 339 sizeok = Sizeok.inProcess; 340 341 //printf("+StructDeclaration::finalizeSize() %s, fields.dim = %d, sizeok = %d\n", toChars(), fields.dim, sizeok); 342 343 fields.setDim(0); // workaround 344 345 // Set the offsets of the fields and determine the size of the struct 346 uint offset = 0; 347 bool isunion = isUnionDeclaration() !is null; 348 for (size_t i = 0; i < members.dim; i++) 349 { 350 Dsymbol s = (*members)[i]; 351 s.setFieldOffset(this, &offset, isunion); 352 } 353 if (type.ty == Terror) 354 { 355 errors = true; 356 return; 357 } 358 359 // 0 sized struct's are set to 1 byte 360 if (structsize == 0) 361 { 362 hasNoFields = true; 363 structsize = 1; 364 alignsize = 1; 365 } 366 367 // Round struct size up to next alignsize boundary. 368 // This will ensure that arrays of structs will get their internals 369 // aligned properly. 370 if (alignment == STRUCTALIGN_DEFAULT) 371 structsize = (structsize + alignsize - 1) & ~(alignsize - 1); 372 else 373 structsize = (structsize + alignment - 1) & ~(alignment - 1); 374 375 sizeok = Sizeok.done; 376 377 //printf("-StructDeclaration::finalizeSize() %s, fields.dim = %d, structsize = %d\n", toChars(), fields.dim, structsize); 378 379 if (errors) 380 return; 381 382 // Calculate fields[i].overlapped 383 if (checkOverlappedFields()) 384 { 385 errors = true; 386 return; 387 } 388 389 // Determine if struct is all zeros or not 390 zeroInit = true; 391 foreach (vd; fields) 392 { 393 if (vd._init) 394 { 395 if (vd._init.isVoidInitializer()) 396 /* Treat as 0 for the purposes of putting the initializer 397 * in the BSS segment, or doing a mass set to 0 398 */ 399 continue; 400 401 // Zero size fields are zero initialized 402 if (vd.type.size(vd.loc) == 0) 403 continue; 404 405 // Examine init to see if it is all 0s. 406 auto exp = vd.getConstInitializer(); 407 if (!exp || !_isZeroInit(exp)) 408 { 409 zeroInit = false; 410 break; 411 } 412 } 413 else if (!vd.type.isZeroInit(loc)) 414 { 415 zeroInit = false; 416 break; 417 } 418 } 419 420 argTypes = target.toArgTypes(type); 421 } 422 423 /*************************************** 424 * Fit elements[] to the corresponding types of the struct's fields. 425 * 426 * Params: 427 * loc = location to use for error messages 428 * sc = context 429 * elements = explicit arguments used to construct object 430 * stype = the constructed object type. 431 * Returns: 432 * false if any errors occur, 433 * otherwise true and elements[] are rewritten for the output. 434 */ 435 final bool fit(const ref Loc loc, Scope* sc, Expressions* elements, Type stype) 436 { 437 if (!elements) 438 return true; 439 440 const nfields = nonHiddenFields(); 441 size_t offset = 0; 442 for (size_t i = 0; i < elements.dim; i++) 443 { 444 Expression e = (*elements)[i]; 445 if (!e) 446 continue; 447 448 e = resolveProperties(sc, e); 449 if (i >= nfields) 450 { 451 if (i <= fields.dim && e.op == TOK.null_) 452 { 453 // CTFE sometimes creates null as hidden pointer; we'll allow this. 454 continue; 455 } 456 .error(loc, "more initializers than fields (%zu) of `%s`", nfields, toChars()); 457 return false; 458 } 459 VarDeclaration v = fields[i]; 460 if (v.offset < offset) 461 { 462 .error(loc, "overlapping initialization for `%s`", v.toChars()); 463 if (!isUnionDeclaration()) 464 { 465 enum errorMsg = "`struct` initializers that contain anonymous unions" ~ 466 " must initialize only the first member of a `union`. All subsequent" ~ 467 " non-overlapping fields are default initialized"; 468 .errorSupplemental(loc, errorMsg); 469 } 470 return false; 471 } 472 offset = cast(uint)(v.offset + v.type.size()); 473 474 Type t = v.type; 475 if (stype) 476 t = t.addMod(stype.mod); 477 Type origType = t; 478 Type tb = t.toBasetype(); 479 480 const hasPointers = tb.hasPointers(); 481 if (hasPointers) 482 { 483 if ((stype.alignment() < target.ptrsize || 484 (v.offset & (target.ptrsize - 1))) && 485 (sc.func && sc.func.setUnsafe())) 486 { 487 .error(loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code", 488 toChars(), v.toChars()); 489 return false; 490 } 491 } 492 493 /* Look for case of initializing a static array with a too-short 494 * string literal, such as: 495 * char[5] foo = "abc"; 496 * Allow this by doing an explicit cast, which will lengthen the string 497 * literal. 498 */ 499 if (e.op == TOK.string_ && tb.ty == Tsarray) 500 { 501 StringExp se = cast(StringExp)e; 502 Type typeb = se.type.toBasetype(); 503 TY tynto = tb.nextOf().ty; 504 if (!se.committed && 505 (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar && 506 se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger()) 507 { 508 e = se.castTo(sc, t); 509 goto L1; 510 } 511 } 512 513 while (!e.implicitConvTo(t) && tb.ty == Tsarray) 514 { 515 /* Static array initialization, as in: 516 * T[3][5] = e; 517 */ 518 t = tb.nextOf(); 519 tb = t.toBasetype(); 520 } 521 if (!e.implicitConvTo(t)) 522 t = origType; // restore type for better diagnostic 523 524 e = e.implicitCastTo(sc, t); 525 L1: 526 if (e.op == TOK.error) 527 return false; 528 529 (*elements)[i] = doCopyOrMove(sc, e); 530 } 531 return true; 532 } 533 534 /*************************************** 535 * Determine if struct is POD (Plain Old Data). 536 * 537 * POD is defined as: 538 * $(OL 539 * $(LI not nested) 540 * $(LI no postblits, destructors, or assignment operators) 541 * $(LI no `ref` fields or fields that are themselves non-POD) 542 * ) 543 * The idea being these are compatible with C structs. 544 * 545 * Returns: 546 * true if struct is POD 547 */ 548 final bool isPOD() 549 { 550 // If we've already determined whether this struct is POD. 551 if (ispod != StructPOD.fwd) 552 return (ispod == StructPOD.yes); 553 554 ispod = StructPOD.yes; 555 556 if (enclosing || postblit || dtor || hasCopyCtor) 557 { 558 ispod = StructPOD.no; 559 return false; 560 } 561 562 // Recursively check all fields are POD. 563 for (size_t i = 0; i < fields.dim; i++) 564 { 565 VarDeclaration v = fields[i]; 566 if (v.storage_class & STC.ref_) 567 { 568 ispod = StructPOD.no; 569 return false; 570 } 571 572 Type tv = v.type.baseElemOf(); 573 if (tv.ty == Tstruct) 574 { 575 TypeStruct ts = cast(TypeStruct)tv; 576 StructDeclaration sd = ts.sym; 577 if (!sd.isPOD()) 578 { 579 ispod = StructPOD.no; 580 return false; 581 } 582 } 583 } 584 585 return (ispod == StructPOD.yes); 586 } 587 588 override final inout(StructDeclaration) isStructDeclaration() inout 589 { 590 return this; 591 } 592 593 override void accept(Visitor v) 594 { 595 v.visit(this); 596 } 597 598 final uint numArgTypes() const 599 { 600 return argTypes && argTypes.arguments ? cast(uint) argTypes.arguments.dim : 0; 601 } 602 603 final Type argType(uint index) 604 { 605 return index < numArgTypes() ? (*argTypes.arguments)[index].type : null; 606 } 607 608 final bool hasNonDisabledCtor() 609 { 610 static extern (C++) class HasNonDisabledCtorVisitor : Visitor 611 { 612 bool result; 613 614 this() {} 615 616 alias visit = Visitor.visit; 617 618 override void visit(CtorDeclaration cd) 619 { 620 if (!(cd.storage_class & STC.disable)) 621 result = true; 622 } 623 624 override void visit(TemplateDeclaration td) 625 { 626 result = true; 627 } 628 629 override void visit(OverloadSet os) 630 { 631 for (size_t i = 0; i < os.a.dim; i++) 632 os.a[i].accept(this); 633 } 634 } 635 636 if (!ctor) 637 return false; 638 scope v = new HasNonDisabledCtorVisitor(); 639 ctor.accept(v); 640 return v.result; 641 } 642 } 643 644 /********************************** 645 * Determine if exp is all binary zeros. 646 * Params: 647 * exp = expression to check 648 * Returns: 649 * true if it's all binary 0 650 */ 651 private bool _isZeroInit(Expression exp) 652 { 653 switch (exp.op) 654 { 655 case TOK.int64: 656 return exp.toInteger() == 0; 657 658 case TOK.null_: 659 case TOK.false_: 660 return true; 661 662 case TOK.structLiteral: 663 { 664 auto sle = cast(StructLiteralExp) exp; 665 foreach (i; 0 .. sle.sd.fields.dim) 666 { 667 auto field = sle.sd.fields[i]; 668 if (field.type.size(field.loc)) 669 { 670 auto e = (*sle.elements)[i]; 671 if (e ? !_isZeroInit(e) 672 : !field.type.isZeroInit(field.loc)) 673 return false; 674 } 675 } 676 return true; 677 } 678 679 case TOK.arrayLiteral: 680 { 681 auto ale = cast(ArrayLiteralExp)exp; 682 683 const dim = ale.elements ? ale.elements.dim : 0; 684 685 if (ale.type.toBasetype().ty == Tarray) // if initializing a dynamic array 686 return dim == 0; 687 688 foreach (i; 0 .. dim) 689 { 690 if (!_isZeroInit(ale[i])) 691 return false; 692 } 693 694 /* Note that true is returned for all T[0] 695 */ 696 return true; 697 } 698 699 case TOK.string_: 700 { 701 StringExp se = cast(StringExp)exp; 702 703 if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array 704 return se.len == 0; 705 706 foreach (i; 0 .. se.len) 707 { 708 if (se.getCodeUnit(i)) 709 return false; 710 } 711 return true; 712 } 713 714 case TOK.vector: 715 { 716 auto ve = cast(VectorExp) exp; 717 return _isZeroInit(ve.e1); 718 } 719 720 case TOK.float64: 721 case TOK.complex80: 722 { 723 import dmd.root.ctfloat : CTFloat; 724 return (exp.toReal() is CTFloat.zero) && 725 (exp.toImaginary() is CTFloat.zero); 726 } 727 728 default: 729 return false; 730 } 731 } 732 733 /*********************************************************** 734 * Unions are a variation on structs. 735 */ 736 extern (C++) final class UnionDeclaration : StructDeclaration 737 { 738 extern (D) this(const ref Loc loc, Identifier id) 739 { 740 super(loc, id, false); 741 } 742 743 override Dsymbol syntaxCopy(Dsymbol s) 744 { 745 assert(!s); 746 auto ud = new UnionDeclaration(loc, ident); 747 return StructDeclaration.syntaxCopy(ud); 748 } 749 750 override const(char)* kind() const 751 { 752 return "union"; 753 } 754 755 override inout(UnionDeclaration) isUnionDeclaration() inout 756 { 757 return this; 758 } 759 760 override void accept(Visitor v) 761 { 762 v.visit(this); 763 } 764 }