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