1 /** 2 * Builds struct member functions if needed and not defined by the user. 3 * Includes `opEquals`, `opAssign`, post blit, copy constructor and destructor. 4 * 5 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 6 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/clone.d, _clone.d) 9 * Documentation: https://dlang.org/phobos/dmd_clone.html 10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/clone.d 11 */ 12 13 module dmd.clone; 14 15 import core.stdc.stdio; 16 import dmd.aggregate; 17 import dmd.arraytypes; 18 import dmd.astenums; 19 import dmd.dclass; 20 import dmd.declaration; 21 import dmd.dscope; 22 import dmd.dstruct; 23 import dmd.dsymbol; 24 import dmd.dsymbolsem; 25 import dmd.dtemplate; 26 import dmd.errors; 27 import dmd.expression; 28 import dmd.expressionsem; 29 import dmd.func; 30 import dmd.globals; 31 import dmd.id; 32 import dmd.identifier; 33 import dmd.init; 34 import dmd.location; 35 import dmd.mtype; 36 import dmd.opover; 37 import dmd.semantic2; 38 import dmd.semantic3; 39 import dmd.statement; 40 import dmd.target; 41 import dmd.typesem; 42 import dmd.tokens; 43 44 /******************************************* 45 * Merge function attributes pure, nothrow, @safe, @nogc, and @disable 46 * from f into s1. 47 * Params: 48 * s1 = storage class to merge into 49 * f = function 50 * Returns: 51 * merged storage class 52 */ 53 StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure 54 { 55 if (!f) 56 return s1; 57 StorageClass s2 = (f.storage_class & STC.disable); 58 59 TypeFunction tf = cast(TypeFunction)f.type; 60 if (tf.trust == TRUST.safe) 61 s2 |= STC.safe; 62 else if (tf.trust == TRUST.system) 63 s2 |= STC.system; 64 else if (tf.trust == TRUST.trusted) 65 s2 |= STC.trusted; 66 67 if (tf.purity != PURE.impure) 68 s2 |= STC.pure_; 69 if (tf.isnothrow) 70 s2 |= STC.nothrow_; 71 if (tf.isnogc) 72 s2 |= STC.nogc; 73 74 const sa = s1 & s2; 75 const so = s1 | s2; 76 77 StorageClass stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable); 78 79 if (so & STC.system) 80 stc |= STC.system; 81 else if (sa & STC.trusted) 82 stc |= STC.trusted; 83 else if ((so & (STC.trusted | STC.safe)) == (STC.trusted | STC.safe)) 84 stc |= STC.trusted; 85 else if (sa & STC.safe) 86 stc |= STC.safe; 87 88 return stc; 89 } 90 91 /******************************************* 92 * Check given aggregate actually has an identity opAssign or not. 93 * Params: 94 * ad = struct or class 95 * sc = current scope 96 * Returns: 97 * if found, returns FuncDeclaration of opAssign, otherwise null 98 */ 99 FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) 100 { 101 Dsymbol assign = search_function(ad, Id.assign); 102 if (assign) 103 { 104 /* check identity opAssign exists 105 */ 106 scope er = new NullExp(ad.loc, ad.type); // dummy rvalue 107 scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue 108 el.type = ad.type; 109 auto a = Expressions(1); 110 const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. 111 sc = sc.push(); 112 sc.tinst = null; 113 sc.minst = null; 114 115 a[0] = er; 116 auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(&a), FuncResolveFlag.quiet); 117 if (!f) 118 { 119 a[0] = el; 120 f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(&a), FuncResolveFlag.quiet); 121 } 122 123 sc = sc.pop(); 124 global.endGagging(errors); 125 if (f) 126 { 127 if (f.errors) 128 return null; 129 auto fparams = f.getParameterList(); 130 if (fparams.length) 131 { 132 auto fparam0 = fparams[0]; 133 if (fparam0.type.toDsymbol(null) != ad) 134 f = null; 135 } 136 } 137 // BUGS: This detection mechanism cannot find some opAssign-s like follows: 138 // struct S { void opAssign(ref immutable S) const; } 139 return f; 140 } 141 return null; 142 } 143 144 /******************************************* 145 * We need an opAssign for the struct if 146 * it has a destructor or a postblit. 147 * We need to generate one if a user-specified one does not exist. 148 */ 149 private bool needOpAssign(StructDeclaration sd) 150 { 151 //printf("StructDeclaration::needOpAssign() %s\n", sd.toChars()); 152 153 static bool isNeeded() 154 { 155 //printf("\tneed\n"); 156 return true; 157 } 158 159 if (sd.isUnionDeclaration()) 160 return !isNeeded(); 161 162 if (sd.hasIdentityAssign || // because has identity==elaborate opAssign 163 sd.dtor || 164 sd.postblit) 165 return isNeeded(); 166 167 /* If any of the fields need an opAssign, then we 168 * need it too. 169 */ 170 foreach (v; sd.fields) 171 { 172 if (v.storage_class & STC.ref_) 173 continue; 174 if (v.overlapped) // if field of a union 175 continue; // user must handle it themselves 176 Type tv = v.type.baseElemOf(); 177 if (tv.ty == Tstruct) 178 { 179 TypeStruct ts = cast(TypeStruct)tv; 180 if (ts.sym.isUnionDeclaration()) 181 continue; 182 if (needOpAssign(ts.sym)) 183 return isNeeded(); 184 } 185 } 186 return !isNeeded(); 187 } 188 189 /****************************************** 190 * Build opAssign for a `struct`. 191 * 192 * The generated `opAssign` function has the following signature: 193 *--- 194 *ref S opAssign(S s) // S is the name of the `struct` 195 *--- 196 * 197 * The opAssign function will be built for a struct `S` if the 198 * following constraints are met: 199 * 200 * 1. `S` does not have an identity `opAssign` defined. 201 * 202 * 2. `S` has at least one of the following members: a postblit (user-defined or 203 * generated for fields that have a defined postblit), a destructor 204 * (user-defined or generated for fields that have a defined destructor) 205 * or at least one field that has a defined `opAssign`. 206 * 207 * 3. `S` does not have any non-mutable fields. 208 * 209 * If `S` has a disabled destructor or at least one field that has a disabled 210 * `opAssign`, `S.opAssign` is going to be generated, but marked with `@disable` 211 * 212 * If `S` defines a destructor, the generated code for `opAssign` is: 213 * 214 *--- 215 *S __swap = void; 216 *__swap = this; // bit copy 217 *this = s; // bit copy 218 *__swap.dtor(); 219 *--- 220 * 221 * Otherwise, if `S` defines a postblit, the generated code for `opAssign` is: 222 * 223 *--- 224 *this = s; 225 *--- 226 * 227 * Note that the parameter to the generated `opAssign` is passed by value, which means 228 * that the postblit is going to be called (if it is defined) in both of the above 229 * situations before entering the body of `opAssign`. The assignments in the above generated 230 * function bodies are blit expressions, so they can be regarded as `memcpy`s 231 * (`opAssign` is not called as this will result in an infinite recursion; the postblit 232 * is not called because it has already been called when the parameter was passed by value). 233 * 234 * If `S` does not have a postblit or a destructor, but contains at least one field that defines 235 * an `opAssign` function (which is not disabled), then the body will make member-wise 236 * assignments: 237 * 238 *--- 239 *this.field1 = s.field1; 240 *this.field2 = s.field2; 241 *...; 242 *--- 243 * 244 * In this situation, the assignemnts are actual assign expressions (`opAssign` is used 245 * if defined). 246 * 247 * References: 248 * https://dlang.org/spec/struct.html#assign-overload 249 * Params: 250 * sd = struct to generate opAssign for 251 * sc = context 252 * Returns: 253 * generated `opAssign` function 254 */ 255 FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) 256 { 257 if (FuncDeclaration f = hasIdentityOpAssign(sd, sc)) 258 { 259 sd.hasIdentityAssign = true; 260 return f; 261 } 262 // Even if non-identity opAssign is defined, built-in identity opAssign 263 // will be defined. 264 if (!needOpAssign(sd)) 265 return null; 266 267 //printf("StructDeclaration::buildOpAssign() %s\n", sd.toChars()); 268 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 269 Loc declLoc = sd.loc; 270 Loc loc; // internal code should have no loc to prevent coverage 271 272 // One of our sub-field might have `@disable opAssign` so we need to 273 // check for it. 274 // In this event, it will be reflected by having `stc` (opAssign's 275 // storage class) include `STC.disabled`. 276 foreach (v; sd.fields) 277 { 278 if (v.storage_class & STC.ref_) 279 continue; 280 if (v.overlapped) 281 continue; 282 Type tv = v.type.baseElemOf(); 283 if (tv.ty != Tstruct) 284 continue; 285 StructDeclaration sdv = (cast(TypeStruct)tv).sym; 286 stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); 287 } 288 289 if (sd.dtor || sd.postblit) 290 { 291 // if the type is not assignable, we cannot generate opAssign 292 if (!sd.type.isAssignable()) // https://issues.dlang.org/show_bug.cgi?id=13044 293 return null; 294 stc = mergeFuncAttrs(stc, sd.dtor); 295 if (stc & STC.safe) 296 stc = (stc & ~STC.safe) | STC.trusted; 297 } 298 299 auto fparams = new Parameters(); 300 fparams.push(new Parameter(STC.nodtor, sd.type, Id.p, null, null)); 301 auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_); 302 auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf); 303 fop.storage_class |= STC.inference; 304 fop.isGenerated = true; 305 Expression e; 306 if (stc & STC.disable) 307 { 308 e = null; 309 } 310 /* Do swap this and rhs. 311 * __swap = this; this = s; __swap.dtor(); 312 */ 313 else if (sd.dtor) 314 { 315 //printf("\tswap copy\n"); 316 TypeFunction tdtor = cast(TypeFunction)sd.dtor.type; 317 assert(tdtor.ty == Tfunction); 318 319 auto idswap = Identifier.generateId("__swap"); 320 auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc)); 321 swap.storage_class |= STC.nodtor | STC.temp | STC.ctfe; 322 if (tdtor.isScopeQual) 323 swap.storage_class |= STC.scope_; 324 auto e1 = new DeclarationExp(loc, swap); 325 326 auto e2 = new BlitExp(loc, new VarExp(loc, swap), new ThisExp(loc)); 327 auto e3 = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); 328 329 /* Instead of running the destructor on s, run it 330 * on swap. This avoids needing to copy swap back in to s. 331 */ 332 auto e4 = new CallExp(loc, new DotVarExp(loc, new VarExp(loc, swap), sd.dtor, false)); 333 334 e = Expression.combine(e1, e2, e3, e4); 335 } 336 /* postblit was called when the value was passed to opAssign, we just need to blit the result */ 337 else if (sd.postblit) 338 { 339 e = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); 340 sd.hasBlitAssign = true; 341 } 342 else 343 { 344 /* Do memberwise copy. 345 * 346 * If sd is a nested struct, its vthis field assignment is: 347 * 1. If it's nested in a class, it's a rebind of class reference. 348 * 2. If it's nested in a function or struct, it's an update of void*. 349 * In both cases, it will change the parent context. 350 */ 351 //printf("\tmemberwise copy\n"); 352 e = null; 353 foreach (v; sd.fields) 354 { 355 // this.v = s.v; 356 auto ec = new AssignExp(loc, 357 new DotVarExp(loc, new ThisExp(loc), v), 358 new DotVarExp(loc, new IdentifierExp(loc, Id.p), v)); 359 e = Expression.combine(e, ec); 360 } 361 } 362 if (e) 363 { 364 Statement s1 = new ExpStatement(loc, e); 365 /* Add: 366 * return this; 367 */ 368 auto er = new ThisExp(loc); 369 Statement s2 = new ReturnStatement(loc, er); 370 fop.fbody = new CompoundStatement(loc, s1, s2); 371 tf.isreturn = true; 372 } 373 sd.members.push(fop); 374 fop.addMember(sc, sd); 375 sd.hasIdentityAssign = true; // temporary mark identity assignable 376 const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. 377 Scope* sc2 = sc.push(); 378 sc2.stc = 0; 379 sc2.linkage = LINK.d; 380 fop.dsymbolSemantic(sc2); 381 fop.semantic2(sc2); 382 // https://issues.dlang.org/show_bug.cgi?id=15044 383 //semantic3(fop, sc2); // isn't run here for lazy forward reference resolution. 384 385 sc2.pop(); 386 if (global.endGagging(errors)) // if errors happened 387 { 388 // Disable generated opAssign, because some members forbid identity assignment. 389 fop.storage_class |= STC.disable; 390 fop.fbody = null; // remove fbody which contains the error 391 } 392 393 //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd.toChars(), (fop.storage_class & STC.disable) != 0); 394 //printf("fop.type: %s\n", fop.type.toPrettyChars()); 395 return fop; 396 } 397 398 /******************************************* 399 * We need an opEquals for the struct if 400 * any fields has an opEquals. 401 * Generate one if a user-specified one does not exist. 402 */ 403 bool needOpEquals(StructDeclaration sd) 404 { 405 //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars()); 406 if (sd.isUnionDeclaration()) 407 { 408 /* If a union has only one field, treat it like a struct 409 */ 410 if (sd.fields.length != 1) 411 goto Ldontneed; 412 } 413 if (sd.hasIdentityEquals) 414 goto Lneed; 415 /* If any of the fields has an opEquals, then we 416 * need it too. 417 */ 418 foreach (VarDeclaration v; sd.fields) 419 { 420 if (v.storage_class & STC.ref_) 421 continue; 422 if (v.overlapped) 423 continue; 424 Type tv = v.type.toBasetype(); 425 auto tvbase = tv.baseElemOf(); 426 if (tvbase.ty == Tstruct) 427 { 428 TypeStruct ts = cast(TypeStruct)tvbase; 429 if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1) 430 continue; 431 if (needOpEquals(ts.sym)) 432 goto Lneed; 433 } 434 if (tvbase.isfloating()) 435 { 436 // This is necessray for: 437 // 1. comparison of +0.0 and -0.0 should be true. 438 // 2. comparison of NANs should be false always. 439 goto Lneed; 440 } 441 if (tvbase.ty == Tarray) 442 goto Lneed; 443 if (tvbase.ty == Taarray) 444 goto Lneed; 445 if (tvbase.ty == Tclass) 446 goto Lneed; 447 } 448 Ldontneed: 449 //printf("\tdontneed\n"); 450 return false; 451 Lneed: 452 //printf("\tneed\n"); 453 return true; 454 } 455 456 /******************************************* 457 * Check given aggregate actually has an identity opEquals or not. 458 */ 459 private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) 460 { 461 FuncDeclaration f; 462 if (Dsymbol eq = search_function(ad, Id.eq)) 463 { 464 /* check identity opEquals exists 465 */ 466 scope er = new NullExp(ad.loc, null); // dummy rvalue 467 scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue 468 auto a = Expressions(1); 469 470 bool hasIt(Type tthis) 471 { 472 const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it 473 sc = sc.push(); 474 sc.tinst = null; 475 sc.minst = null; 476 477 FuncDeclaration rfc(Expression e) 478 { 479 a[0] = e; 480 a[0].type = tthis; 481 return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(&a), FuncResolveFlag.quiet); 482 } 483 484 f = rfc(er); 485 if (!f) 486 f = rfc(el); 487 488 sc = sc.pop(); 489 global.endGagging(errors); 490 491 return f !is null; 492 } 493 494 if (hasIt(ad.type) || 495 hasIt(ad.type.constOf()) || 496 hasIt(ad.type.immutableOf()) || 497 hasIt(ad.type.sharedOf()) || 498 hasIt(ad.type.sharedConstOf())) 499 { 500 if (f.errors) 501 return null; 502 } 503 } 504 return f; 505 } 506 507 /****************************************** 508 * Build opEquals for struct. 509 * const bool opEquals(const S s) { ... } 510 * 511 * By fixing https://issues.dlang.org/show_bug.cgi?id=3789 512 * opEquals is changed to be never implicitly generated. 513 * Now, struct objects comparison s1 == s2 is translated to: 514 * s1.tupleof == s2.tupleof 515 * to calculate structural equality. See EqualExp.op_overload. 516 */ 517 FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc) 518 { 519 if (hasIdentityOpEquals(sd, sc)) 520 { 521 sd.hasIdentityEquals = true; 522 } 523 return null; 524 } 525 526 /****************************************** 527 * Build __xopEquals for TypeInfo_Struct 528 * bool __xopEquals(ref const S p) const 529 * { 530 * return this == p; 531 * } 532 * 533 * This is called by TypeInfo.equals(p1, p2). If the struct does not support 534 * const objects comparison, it will throw "not implemented" Error in runtime. 535 */ 536 FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) 537 { 538 if (!needOpEquals(sd)) 539 return null; // bitwise comparison would work 540 541 //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars()); 542 if (Dsymbol eq = search_function(sd, Id.eq)) 543 { 544 if (FuncDeclaration fd = eq.isFuncDeclaration()) 545 { 546 TypeFunction tfeqptr; 547 { 548 Scope scx; 549 /* const bool opEquals(ref const S s); 550 */ 551 auto parameters = new Parameters(); 552 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null)); 553 tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d); 554 tfeqptr.mod = MODFlags.const_; 555 tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx); 556 } 557 fd = fd.overloadExactMatch(tfeqptr); 558 if (fd) 559 return fd; 560 } 561 } 562 if (!sd.xerreq) 563 { 564 // object._xopEquals 565 Identifier id = Identifier.idPool("_xopEquals"); 566 Expression e = new IdentifierExp(sd.loc, Id.empty); 567 e = new DotIdExp(sd.loc, e, Id.object); 568 e = new DotIdExp(sd.loc, e, id); 569 e = e.expressionSemantic(sc); 570 if (!e.isErrorExp()) 571 { 572 Dsymbol s = getDsymbol(e); 573 assert(s); 574 sd.xerreq = s.isFuncDeclaration(); 575 } 576 } 577 Loc declLoc; // loc is unnecessary so __xopEquals is never called directly 578 Loc loc; // loc is unnecessary so errors are gagged 579 auto parameters = new Parameters(); 580 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); 581 auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_); 582 tf = tf.addSTC(STC.const_).toTypeFunction(); 583 Identifier id = Id.xopEquals; 584 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); 585 fop.isGenerated = true; 586 fop.parent = sd; 587 Expression e1 = new IdentifierExp(loc, Id.This); 588 Expression e2 = new IdentifierExp(loc, Id.p); 589 Expression e = new EqualExp(EXP.equal, loc, e1, e2); 590 fop.fbody = new ReturnStatement(loc, e); 591 uint errors = global.startGagging(); // Do not report errors 592 Scope* sc2 = sc.push(); 593 sc2.stc = 0; 594 sc2.linkage = LINK.d; 595 fop.dsymbolSemantic(sc2); 596 fop.semantic2(sc2); 597 sc2.pop(); 598 if (global.endGagging(errors)) // if errors happened 599 fop = sd.xerreq; 600 return fop; 601 } 602 603 /****************************************** 604 * Build __xopCmp for TypeInfo_Struct 605 * int __xopCmp(ref const S p) const 606 * { 607 * return this.opCmp(p); 608 * } 609 * 610 * This is called by TypeInfo.compare(p1, p2). If the struct does not support 611 * const objects comparison, it will throw "not implemented" Error in runtime. 612 */ 613 FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) 614 { 615 //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); 616 if (Dsymbol cmp = search_function(sd, Id.cmp)) 617 { 618 if (FuncDeclaration fd = cmp.isFuncDeclaration()) 619 { 620 TypeFunction tfcmpptr; 621 { 622 Scope scx; 623 /* const int opCmp(ref const S s); 624 */ 625 auto parameters = new Parameters(); 626 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null)); 627 tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d); 628 tfcmpptr.mod = MODFlags.const_; 629 tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx); 630 } 631 fd = fd.overloadExactMatch(tfcmpptr); 632 if (fd) 633 return fd; 634 } 635 } 636 else 637 { 638 version (none) // FIXME: doesn't work for recursive alias this 639 { 640 /* Check opCmp member exists. 641 * Consider 'alias this', but except opDispatch. 642 */ 643 Expression e = new DsymbolExp(sd.loc, sd); 644 e = new DotIdExp(sd.loc, e, Id.cmp); 645 Scope* sc2 = sc.push(); 646 e = e.trySemantic(sc2); 647 sc2.pop(); 648 if (e) 649 { 650 Dsymbol s = null; 651 switch (e.op) 652 { 653 case EXP.overloadSet: 654 s = e.isOverExp().vars; 655 break; 656 case EXP.scope_: 657 s = e.isScopeExp().sds; 658 break; 659 case EXP.variable: 660 s = e.isVarExp().var; 661 break; 662 default: 663 break; 664 } 665 if (!s || s.ident != Id.cmp) 666 e = null; // there's no valid member 'opCmp' 667 } 668 if (!e) 669 return null; // bitwise comparison would work 670 /* Essentially, a struct which does not define opCmp is not comparable. 671 * At this time, typeid(S).compare might be correct that throwing "not implement" Error. 672 * But implementing it would break existing code, such as: 673 * 674 * struct S { int value; } // no opCmp 675 * int[S] aa; // Currently AA key uses bitwise comparison 676 * // (It's default behavior of TypeInfo_Strust.compare). 677 * 678 * Not sure we should fix this inconsistency, so just keep current behavior. 679 */ 680 } 681 else 682 { 683 return null; 684 } 685 } 686 if (!sd.xerrcmp) 687 { 688 // object._xopCmp 689 Identifier id = Identifier.idPool("_xopCmp"); 690 Expression e = new IdentifierExp(sd.loc, Id.empty); 691 e = new DotIdExp(sd.loc, e, Id.object); 692 e = new DotIdExp(sd.loc, e, id); 693 e = e.expressionSemantic(sc); 694 if (!e.isErrorExp()) 695 { 696 Dsymbol s = getDsymbol(e); 697 assert(s); 698 sd.xerrcmp = s.isFuncDeclaration(); 699 } 700 } 701 Loc declLoc; // loc is unnecessary so __xopCmp is never called directly 702 Loc loc; // loc is unnecessary so errors are gagged 703 auto parameters = new Parameters(); 704 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); 705 auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_); 706 tf = tf.addSTC(STC.const_).toTypeFunction(); 707 Identifier id = Id.xopCmp; 708 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); 709 fop.isGenerated = true; 710 fop.parent = sd; 711 Expression e1 = new IdentifierExp(loc, Id.This); 712 Expression e2 = new IdentifierExp(loc, Id.p); 713 Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2); 714 fop.fbody = new ReturnStatement(loc, e); 715 uint errors = global.startGagging(); // Do not report errors 716 Scope* sc2 = sc.push(); 717 sc2.stc = 0; 718 sc2.linkage = LINK.d; 719 fop.dsymbolSemantic(sc2); 720 fop.semantic2(sc2); 721 sc2.pop(); 722 if (global.endGagging(errors)) // if errors happened 723 fop = sd.xerrcmp; 724 return fop; 725 } 726 727 /******************************************* 728 * We need a toHash for the struct if 729 * any fields has a toHash. 730 * Generate one if a user-specified one does not exist. 731 */ 732 private bool needToHash(StructDeclaration sd) 733 { 734 //printf("StructDeclaration::needToHash() %s\n", sd.toChars()); 735 if (sd.isUnionDeclaration()) 736 goto Ldontneed; 737 if (sd.xhash) 738 goto Lneed; 739 740 /* If any of the fields has an toHash, then we 741 * need it too. 742 */ 743 foreach (VarDeclaration v; sd.fields) 744 { 745 if (v.storage_class & STC.ref_) 746 continue; 747 if (v.overlapped) 748 continue; 749 Type tv = v.type.toBasetype(); 750 auto tvbase = tv.baseElemOf(); 751 if (tvbase.ty == Tstruct) 752 { 753 TypeStruct ts = cast(TypeStruct)tvbase; 754 if (ts.sym.isUnionDeclaration()) 755 continue; 756 if (needToHash(ts.sym)) 757 goto Lneed; 758 } 759 if (tvbase.isfloating()) 760 { 761 /* This is necessary because comparison of +0.0 and -0.0 should be true, 762 * i.e. not a bit compare. 763 */ 764 goto Lneed; 765 } 766 if (tvbase.ty == Tarray) 767 goto Lneed; 768 if (tvbase.ty == Taarray) 769 goto Lneed; 770 if (tvbase.ty == Tclass) 771 goto Lneed; 772 } 773 Ldontneed: 774 //printf("\tdontneed\n"); 775 return false; 776 Lneed: 777 //printf("\tneed\n"); 778 return true; 779 } 780 781 /****************************************** 782 * Build __xtoHash for non-bitwise hashing 783 * static hash_t xtoHash(ref const S p) nothrow @trusted; 784 */ 785 FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) 786 { 787 if (Dsymbol s = search_function(sd, Id.tohash)) 788 { 789 __gshared TypeFunction tftohash; 790 if (!tftohash) 791 { 792 tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d); 793 tftohash.mod = MODFlags.const_; 794 tftohash = cast(TypeFunction)tftohash.merge(); 795 } 796 if (FuncDeclaration fd = s.isFuncDeclaration()) 797 { 798 fd = fd.overloadExactMatch(tftohash); 799 if (fd) 800 return fd; 801 } 802 } 803 if (!needToHash(sd)) 804 return null; 805 806 /* The trouble is that the following code relies on .tupleof, but .tupleof 807 * is not allowed for C files. If we allow it for C files, then that turns on 808 * the other D properties, too, such as .dup which will then conflict with allowed 809 * field names. 810 * One way to fix it is to replace the following foreach and .tupleof with C 811 * statements and expressions. 812 * But, it's debatable whether C structs should even need toHash(). 813 * Note that it would only be necessary if it has floating point fields. 814 * For now, we'll just not generate a toHash() for C files. 815 */ 816 if (sc.flags & SCOPE.Cfile) 817 return null; 818 819 //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); 820 Loc declLoc; // loc is unnecessary so __xtoHash is never called directly 821 Loc loc; // internal code should have no loc to prevent coverage 822 auto parameters = new Parameters(); 823 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null)); 824 auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted); 825 Identifier id = Id.xtoHash; 826 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); 827 fop.isGenerated = true; 828 829 /* Do memberwise hashing. 830 * 831 * If sd is a nested struct, and if it's nested in a class, the calculated 832 * hash value will also contain the result of parent class's toHash(). 833 */ 834 const(char)[] code = 835 ".object.size_t h = 0;" ~ 836 "foreach (i, T; typeof(p.tupleof))" ~ 837 // workaround https://issues.dlang.org/show_bug.cgi?id=17968 838 " static if(is(T* : const(.object.Object)*)) " ~ 839 " h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~ 840 " else " ~ 841 " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~ 842 "return h;"; 843 fop.fbody = new MixinStatement(loc, new StringExp(loc, code)); 844 Scope* sc2 = sc.push(); 845 sc2.stc = 0; 846 sc2.linkage = LINK.d; 847 fop.dsymbolSemantic(sc2); 848 fop.semantic2(sc2); 849 sc2.pop(); 850 851 //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars()); 852 return fop; 853 } 854 855 /***************************************** 856 * Create aggregate destructor for struct/class by aggregating 857 * all the destructors in userDtors[] with the destructors for 858 * all the members. 859 * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields. 860 * Params: 861 * ad = struct or class to build destructor for 862 * sc = context 863 * Note: 864 * Close similarity with StructDeclaration::buildPostBlit(), 865 * and the ordering changes (runs backward instead of forwards). 866 */ 867 void buildDtors(AggregateDeclaration ad, Scope* sc) 868 { 869 //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars()); 870 if (ad.isUnionDeclaration()) 871 return; // unions don't have destructors 872 873 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 874 Loc declLoc = ad.userDtors.length ? ad.userDtors[0].loc : ad.loc; 875 Loc loc; // internal code should have no loc to prevent coverage 876 FuncDeclaration xdtor_fwd = null; 877 878 // Build the field destructor (`ad.fieldDtor`), if needed. 879 // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building. 880 const bool dtorIsCppPrototype = ad.userDtors.length && ad.userDtors[0]._linkage == LINK.cpp && !ad.userDtors[0].fbody; 881 if (!dtorIsCppPrototype) 882 { 883 Expression e = null; 884 for (size_t i = 0; i < ad.fields.length; i++) 885 { 886 auto v = ad.fields[i]; 887 if (v.storage_class & STC.ref_) 888 continue; 889 if (v.overlapped) 890 continue; 891 auto tv = v.type.baseElemOf(); 892 if (tv.ty != Tstruct) 893 continue; 894 auto sdv = (cast(TypeStruct)tv).sym; 895 if (!sdv.dtor) 896 continue; 897 898 // fix: https://issues.dlang.org/show_bug.cgi?id=17257 899 // braces for shrink wrapping scope of a 900 { 901 xdtor_fwd = sdv.dtor; // this dtor is temporary it could be anything 902 auto a = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor_fwd); 903 a.addMember(sc, ad); // temporarily add to symbol table 904 } 905 906 sdv.dtor.functionSemantic(); 907 908 stc = mergeFuncAttrs(stc, sdv.dtor); 909 if (stc & STC.disable) 910 { 911 e = null; 912 break; 913 } 914 915 Expression ex; 916 tv = v.type.toBasetype(); 917 if (tv.ty == Tstruct) 918 { 919 // this.v.__xdtor() 920 921 ex = new ThisExp(loc); 922 ex = new DotVarExp(loc, ex, v); 923 924 // This is a hack so we can call destructors on const/immutable objects. 925 // Do it as a type 'paint', `cast()` 926 ex = new CastExp(loc, ex, MODFlags.none); 927 if (stc & STC.safe) 928 stc = (stc & ~STC.safe) | STC.trusted; 929 930 ex = new DotVarExp(loc, ex, sdv.dtor, false); 931 ex = new CallExp(loc, ex); 932 } 933 else 934 { 935 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) 936 937 const n = tv.numberOfElems(loc); 938 if (n == 0) 939 continue; 940 941 ex = new ThisExp(loc); 942 ex = new DotVarExp(loc, ex, v); 943 944 // This is a hack so we can call destructors on const/immutable objects. 945 ex = new DotIdExp(loc, ex, Id.ptr); 946 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 947 if (stc & STC.safe) 948 stc = (stc & ~STC.safe) | STC.trusted; 949 950 SliceExp se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 951 new IntegerExp(loc, n, Type.tsize_t)); 952 // Prevent redundant bounds check 953 se.upperIsInBounds = true; 954 se.lowerIsLessThanUpper = true; 955 956 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); 957 } 958 e = Expression.combine(ex, e); // combine in reverse order 959 } 960 961 if (e || (stc & STC.disable)) 962 { 963 //printf("Building __fieldDtor(), %s\n", e.toChars()); 964 auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor); 965 dd.isGenerated = true; 966 dd.storage_class |= STC.inference; 967 dd.fbody = new ExpStatement(loc, e); 968 ad.members.push(dd); 969 dd.dsymbolSemantic(sc); 970 ad.fieldDtor = dd; 971 } 972 } 973 974 // Generate list of dtors to call in that order 975 DtorDeclarations dtors; 976 foreach_reverse (userDtor; ad.userDtors[]) 977 dtors.push(userDtor); 978 if (ad.fieldDtor) 979 dtors.push(ad.fieldDtor); 980 if (!dtorIsCppPrototype) 981 { 982 // extern(C++) destructors call into super to destruct the full hierarchy 983 ClassDeclaration cldec = ad.isClassDeclaration(); 984 if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor) 985 dtors.push(cldec.baseClass.aggrDtor); 986 } 987 988 // Set/build `ad.aggrDtor` 989 switch (dtors.length) 990 { 991 case 0: 992 break; 993 994 case 1: 995 // Use the single existing dtor directly as aggregate dtor. 996 // Note that this might be `cldec.baseClass.aggrDtor`. 997 ad.aggrDtor = dtors[0]; 998 break; 999 1000 default: 1001 // Build the aggregate destructor, calling all dtors in order. 1002 assert(!dtorIsCppPrototype); 1003 Expression e = null; 1004 e = null; 1005 stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1006 foreach (FuncDeclaration fd; dtors) 1007 { 1008 stc = mergeFuncAttrs(stc, fd); 1009 if (stc & STC.disable) 1010 { 1011 e = null; 1012 break; 1013 } 1014 Expression ex = new ThisExp(loc); 1015 ex = new DotVarExp(loc, ex, fd, false); 1016 CallExp ce = new CallExp(loc, ex); 1017 ce.directcall = true; 1018 e = Expression.combine(e, ce); 1019 } 1020 auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor); 1021 dd.isGenerated = true; 1022 dd.storage_class |= STC.inference; 1023 dd.fbody = new ExpStatement(loc, e); 1024 ad.members.push(dd); 1025 dd.dsymbolSemantic(sc); 1026 ad.aggrDtor = dd; 1027 break; 1028 } 1029 1030 // Set/build `ad.dtor`. 1031 // On Windows, the dtor in the vtable is a shim with different signature. 1032 ad.dtor = (ad.aggrDtor && ad.aggrDtor._linkage == LINK.cpp && !target.cpp.twoDtorInVtable) 1033 ? buildWindowsCppDtor(ad, ad.aggrDtor, sc) 1034 : ad.aggrDtor; 1035 1036 // Add an __xdtor alias to make `ad.dtor` accessible 1037 if (ad.dtor) 1038 { 1039 auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, ad.dtor); 1040 _alias.dsymbolSemantic(sc); 1041 ad.members.push(_alias); 1042 if (xdtor_fwd) 1043 ad.symtab.update(_alias); // update forward dtor to correct one 1044 else 1045 _alias.addMember(sc, ad); // add to symbol table 1046 } 1047 1048 // Set/build `ad.tidtor` 1049 ad.tidtor = buildExternDDtor(ad, sc); 1050 } 1051 1052 /** 1053 * build a shim function around the compound dtor that accepts an argument 1054 * that is used to implement the deleting C++ destructor 1055 * 1056 * Params: 1057 * ad = the aggregate that contains the destructor to wrap 1058 * dtor = the destructor to wrap 1059 * sc = the scope in which to analyze the new function 1060 * 1061 * Returns: 1062 * the shim destructor, semantically analyzed and added to the class as a member 1063 */ 1064 private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclaration dtor, Scope* sc) 1065 { 1066 auto cldec = ad.isClassDeclaration(); 1067 if (!cldec || cldec.cppDtorVtblIndex == -1) // scalar deleting dtor not built for non-virtual dtors 1068 return dtor; // perhaps also do this if STC.scope_ is set 1069 1070 // generate deleting C++ destructor corresponding to: 1071 // void* C::~C(int del) 1072 // { 1073 // this->~C(); 1074 // // TODO: if (del) delete (char*)this; 1075 // return (void*) this; 1076 // } 1077 Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); 1078 Parameters* params = new Parameters; 1079 params.push(delparam); 1080 const stc = dtor.storage_class & ~STC.scope_; // because we add the `return this;` later 1081 auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, stc); 1082 auto func = new DtorDeclaration(dtor.loc, dtor.loc, stc, Id.cppdtor); 1083 func.type = ftype; 1084 1085 // Always generate the function with body, because it is not exported from DLLs. 1086 const loc = dtor.loc; 1087 auto stmts = new Statements; 1088 auto call = new CallExp(loc, dtor, null); 1089 call.directcall = true; 1090 stmts.push(new ExpStatement(loc, call)); 1091 stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr))); 1092 func.fbody = new CompoundStatement(loc, stmts); 1093 func.isGenerated = true; 1094 1095 auto sc2 = sc.push(); 1096 sc2.stc &= ~STC.static_; // not a static destructor 1097 sc2.linkage = LINK.cpp; 1098 1099 ad.members.push(func); 1100 func.addMember(sc2, ad); 1101 func.dsymbolSemantic(sc2); 1102 1103 sc2.pop(); 1104 return func; 1105 } 1106 1107 /** 1108 * build a shim function around the aggregate dtor that translates 1109 * a C++ destructor to a destructor with extern(D) calling convention 1110 * 1111 * Params: 1112 * ad = the aggregate that contains the destructor to wrap 1113 * sc = the scope in which to analyze the new function 1114 * 1115 * Returns: 1116 * the shim destructor, semantically analyzed and added to the class as a member 1117 */ 1118 private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) 1119 { 1120 auto dtor = ad.aggrDtor; 1121 if (!dtor) 1122 return null; 1123 1124 // Don't try to call `@disable`d dtors 1125 if (dtor.storage_class & STC.disable) 1126 return null; 1127 1128 // Generate shim only when ABI incompatible on target platform 1129 if (ad.classKind != ClassKind.cpp || !target.cpp.wrapDtorInExternD) 1130 return dtor; 1131 1132 // generate member function that adjusts calling convention 1133 // (EAX used for 'this' instead of ECX on Windows/stack on others): 1134 // extern(D) void __ticppdtor() 1135 // { 1136 // Class.__dtor(); 1137 // } 1138 auto ftype = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dtor.storage_class); 1139 auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.ticppdtor); 1140 func.type = ftype; 1141 1142 auto call = new CallExp(dtor.loc, dtor, null); 1143 call.directcall = true; // non-virtual call Class.__dtor(); 1144 func.fbody = new ExpStatement(dtor.loc, call); 1145 func.isGenerated = true; 1146 func.storage_class |= STC.inference; 1147 1148 auto sc2 = sc.push(); 1149 sc2.stc &= ~STC.static_; // not a static destructor 1150 sc2.linkage = LINK.d; 1151 1152 ad.members.push(func); 1153 func.addMember(sc2, ad); 1154 func.dsymbolSemantic(sc2); 1155 func.functionSemantic(); // to infer attributes 1156 1157 sc2.pop(); 1158 return func; 1159 } 1160 1161 /****************************************** 1162 * Create inclusive invariant for struct/class by aggregating 1163 * all the invariants in invs[]. 1164 * --- 1165 * void __invariant() const [pure nothrow @trusted] 1166 * { 1167 * invs[0](), invs[1](), ...; 1168 * } 1169 * --- 1170 */ 1171 FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) 1172 { 1173 switch (ad.invs.length) 1174 { 1175 case 0: 1176 return null; 1177 1178 case 1: 1179 // Don't return invs[0] so it has uniquely generated name. 1180 goto default; 1181 1182 default: 1183 Expression e = null; 1184 StorageClass stcx = 0; 1185 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1186 foreach (i, inv; ad.invs) 1187 { 1188 stc = mergeFuncAttrs(stc, inv); 1189 if (stc & STC.disable) 1190 { 1191 // What should do? 1192 } 1193 const stcy = (inv.storage_class & STC.synchronized_) | 1194 (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0); 1195 if (i == 0) 1196 stcx = stcy; 1197 else if (stcx ^ stcy) 1198 { 1199 version (all) 1200 { 1201 // currently rejects 1202 ad.error(inv.loc, "mixing invariants with different `shared`/`synchronized` qualifiers is not supported"); 1203 e = null; 1204 break; 1205 } 1206 } 1207 e = Expression.combine(e, new CallExp(Loc.initial, new VarExp(Loc.initial, inv, false))); 1208 } 1209 auto inv = new InvariantDeclaration(ad.loc, Loc.initial, stc | stcx, 1210 Id.classInvariant, new ExpStatement(Loc.initial, e)); 1211 ad.members.push(inv); 1212 inv.dsymbolSemantic(sc); 1213 return inv; 1214 } 1215 } 1216 1217 /***************************************** 1218 * Create inclusive postblit for struct by aggregating 1219 * all the postblits in postblits[] with the postblits for 1220 * all the members. 1221 * Note the close similarity with AggregateDeclaration::buildDtor(), 1222 * and the ordering changes (runs forward instead of backwards). 1223 */ 1224 FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) 1225 { 1226 //printf("buildPostBlit() %s\n", sd.toChars()); 1227 if (sd.isUnionDeclaration()) 1228 return null; 1229 1230 const hasUserDefinedPosblit = sd.postblits.length && !sd.postblits[0].isDisabled ? true : false; 1231 1232 // by default, the storage class of the created postblit 1233 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1234 Loc declLoc = sd.postblits.length ? sd.postblits[0].loc : sd.loc; 1235 Loc loc; // internal code should have no loc to prevent coverage 1236 1237 // if any of the postblits are disabled, then the generated postblit 1238 // will be disabled 1239 foreach (postblit; sd.postblits) 1240 stc |= postblit.storage_class & STC.disable; 1241 1242 VarDeclaration[] fieldsToDestroy; 1243 auto postblitCalls = new Statements(); 1244 // iterate through all the struct fields that are not disabled 1245 for (size_t i = 0; i < sd.fields.length && !(stc & STC.disable); i++) 1246 { 1247 auto structField = sd.fields[i]; 1248 if (structField.storage_class & STC.ref_) 1249 continue; 1250 if (structField.overlapped) 1251 continue; 1252 // if it's a struct declaration or an array of structs 1253 Type tv = structField.type.baseElemOf(); 1254 if (tv.ty != Tstruct) 1255 continue; 1256 auto sdv = (cast(TypeStruct)tv).sym; 1257 // which has a postblit declaration 1258 if (!sdv.postblit) 1259 continue; 1260 assert(!sdv.isUnionDeclaration()); 1261 1262 // if this field's postblit is not `nothrow`, add a `scope(failure)` 1263 // block to destroy any prior successfully postblitted fields should 1264 // this field's postblit fail. 1265 // Don't generate it for betterC code since it cannot throw exceptions. 1266 if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && !global.params.betterC) 1267 { 1268 // create a list of destructors that need to be called 1269 Expression[] dtorCalls; 1270 foreach(sf; fieldsToDestroy) 1271 { 1272 Expression ex; 1273 tv = sf.type.toBasetype(); 1274 if (tv.ty == Tstruct) 1275 { 1276 // this.v.__xdtor() 1277 1278 ex = new ThisExp(loc); 1279 ex = new DotVarExp(loc, ex, sf); 1280 1281 // This is a hack so we can call destructors on const/immutable objects. 1282 ex = new AddrExp(loc, ex); 1283 ex = new CastExp(loc, ex, sf.type.mutableOf().pointerTo()); 1284 ex = new PtrExp(loc, ex); 1285 if (stc & STC.safe) 1286 stc = (stc & ~STC.safe) | STC.trusted; 1287 1288 auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym; 1289 1290 ex = new DotVarExp(loc, ex, sfv.dtor, false); 1291 ex = new CallExp(loc, ex); 1292 1293 dtorCalls ~= ex; 1294 } 1295 else 1296 { 1297 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) 1298 1299 const length = tv.numberOfElems(loc); 1300 1301 ex = new ThisExp(loc); 1302 ex = new DotVarExp(loc, ex, sf); 1303 1304 // This is a hack so we can call destructors on const/immutable objects. 1305 ex = new DotIdExp(loc, ex, Id.ptr); 1306 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 1307 if (stc & STC.safe) 1308 stc = (stc & ~STC.safe) | STC.trusted; 1309 1310 auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 1311 new IntegerExp(loc, length, Type.tsize_t)); 1312 // Prevent redundant bounds check 1313 se.upperIsInBounds = true; 1314 se.lowerIsLessThanUpper = true; 1315 1316 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); 1317 1318 dtorCalls ~= ex; 1319 } 1320 } 1321 fieldsToDestroy = []; 1322 1323 // aggregate the destructor calls 1324 auto dtors = new Statements(); 1325 foreach_reverse(dc; dtorCalls) 1326 { 1327 dtors.push(new ExpStatement(loc, dc)); 1328 } 1329 1330 // put destructor calls in a `scope(failure)` block 1331 postblitCalls.push(new ScopeGuardStatement(loc, TOK.onScopeFailure, new CompoundStatement(loc, dtors))); 1332 } 1333 1334 // perform semantic on the member postblit in order to 1335 // be able to aggregate it later on with the rest of the 1336 // postblits 1337 sdv.postblit.functionSemantic(); 1338 1339 stc = mergeFuncAttrs(stc, sdv.postblit); 1340 stc = mergeFuncAttrs(stc, sdv.dtor); 1341 1342 // if any of the struct member fields has disabled 1343 // its postblit, then `sd` is not copyable, so no 1344 // postblit is generated 1345 if (stc & STC.disable) 1346 { 1347 postblitCalls.setDim(0); 1348 break; 1349 } 1350 1351 Expression ex; 1352 tv = structField.type.toBasetype(); 1353 if (tv.ty == Tstruct) 1354 { 1355 // this.v.__xpostblit() 1356 1357 ex = new ThisExp(loc); 1358 ex = new DotVarExp(loc, ex, structField); 1359 1360 // This is a hack so we can call postblits on const/immutable objects. 1361 ex = new AddrExp(loc, ex); 1362 ex = new CastExp(loc, ex, structField.type.mutableOf().pointerTo()); 1363 ex = new PtrExp(loc, ex); 1364 if (stc & STC.safe) 1365 stc = (stc & ~STC.safe) | STC.trusted; 1366 1367 ex = new DotVarExp(loc, ex, sdv.postblit, false); 1368 ex = new CallExp(loc, ex); 1369 } 1370 else 1371 { 1372 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n]) 1373 1374 const length = tv.numberOfElems(loc); 1375 if (length == 0) 1376 continue; 1377 1378 ex = new ThisExp(loc); 1379 ex = new DotVarExp(loc, ex, structField); 1380 1381 // This is a hack so we can call postblits on const/immutable objects. 1382 ex = new DotIdExp(loc, ex, Id.ptr); 1383 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 1384 if (stc & STC.safe) 1385 stc = (stc & ~STC.safe) | STC.trusted; 1386 1387 auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 1388 new IntegerExp(loc, length, Type.tsize_t)); 1389 // Prevent redundant bounds check 1390 se.upperIsInBounds = true; 1391 se.lowerIsLessThanUpper = true; 1392 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayPostblit), se); 1393 } 1394 postblitCalls.push(new ExpStatement(loc, ex)); // combine in forward order 1395 1396 /* https://issues.dlang.org/show_bug.cgi?id=10972 1397 * When subsequent field postblit calls fail, 1398 * this field should be destructed for Exception Safety. 1399 */ 1400 if (sdv.dtor) 1401 { 1402 sdv.dtor.functionSemantic(); 1403 1404 // keep a list of fields that need to be destroyed in case 1405 // of a future postblit failure 1406 fieldsToDestroy ~= structField; 1407 } 1408 } 1409 1410 void checkShared() 1411 { 1412 if (sd.type.isShared()) 1413 stc |= STC.shared_; 1414 } 1415 1416 // Build our own "postblit" which executes a, but only if needed. 1417 if (postblitCalls.length || (stc & STC.disable)) 1418 { 1419 //printf("Building __fieldPostBlit()\n"); 1420 checkShared(); 1421 auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__fieldPostblit); 1422 dd.isGenerated = true; 1423 dd.storage_class |= STC.inference | STC.scope_; 1424 dd.fbody = (stc & STC.disable) ? null : new CompoundStatement(loc, postblitCalls); 1425 sd.postblits.shift(dd); 1426 sd.members.push(dd); 1427 dd.dsymbolSemantic(sc); 1428 } 1429 1430 // create __xpostblit, which is the generated postblit 1431 FuncDeclaration xpostblit = null; 1432 switch (sd.postblits.length) 1433 { 1434 case 0: 1435 break; 1436 1437 case 1: 1438 xpostblit = sd.postblits[0]; 1439 break; 1440 1441 default: 1442 Expression e = null; 1443 stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1444 foreach (fd; sd.postblits) 1445 { 1446 stc = mergeFuncAttrs(stc, fd); 1447 if (stc & STC.disable) 1448 { 1449 e = null; 1450 break; 1451 } 1452 Expression ex = new ThisExp(loc); 1453 ex = new DotVarExp(loc, ex, fd, false); 1454 ex = new CallExp(loc, ex); 1455 e = Expression.combine(e, ex); 1456 } 1457 1458 checkShared(); 1459 auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__aggrPostblit); 1460 dd.isGenerated = true; 1461 dd.storage_class |= STC.inference; 1462 dd.fbody = new ExpStatement(loc, e); 1463 sd.members.push(dd); 1464 dd.dsymbolSemantic(sc); 1465 xpostblit = dd; 1466 break; 1467 } 1468 1469 // Add an __xpostblit alias to make the inclusive postblit accessible 1470 if (xpostblit) 1471 { 1472 auto _alias = new AliasDeclaration(Loc.initial, Id.__xpostblit, xpostblit); 1473 _alias.dsymbolSemantic(sc); 1474 sd.members.push(_alias); 1475 _alias.addMember(sc, sd); // add to symbol table 1476 } 1477 1478 if (sd.hasCopyCtor) 1479 { 1480 // we have user defined postblit, so we prioritize it 1481 if (hasUserDefinedPosblit) 1482 { 1483 sd.hasCopyCtor = false; 1484 return xpostblit; 1485 } 1486 // we have fields with postblits, so print deprecations 1487 if (xpostblit && !xpostblit.isDisabled()) 1488 { 1489 deprecation(sd.loc, "`struct %s` implicitly-generated postblit hides copy constructor.", sd.toChars); 1490 deprecationSupplemental(sd.loc, "The field postblit will have priority over the copy constructor."); 1491 deprecationSupplemental(sd.loc, "To change this, the postblit should be disabled for `struct %s`", sd.toChars()); 1492 sd.hasCopyCtor = false; 1493 } 1494 else 1495 xpostblit = null; 1496 } 1497 1498 return xpostblit; 1499 } 1500 1501 /** 1502 * Generates a copy constructor declaration with the specified storage 1503 * class for the parameter and the function. 1504 * 1505 * Params: 1506 * sd = the `struct` that contains the copy constructor 1507 * paramStc = the storage class of the copy constructor parameter 1508 * funcStc = the storage class for the copy constructor declaration 1509 * 1510 * Returns: 1511 * The copy constructor declaration for struct `sd`. 1512 */ 1513 private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc) 1514 { 1515 auto fparams = new Parameters(); 1516 auto structType = sd.type; 1517 fparams.push(new Parameter(paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null)); 1518 ParameterList pList = ParameterList(fparams); 1519 auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_); 1520 auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true); 1521 ccd.storage_class |= funcStc; 1522 ccd.storage_class |= STC.inference; 1523 ccd.isGenerated = true; 1524 return ccd; 1525 } 1526 1527 /** 1528 * Generates a trivial copy constructor body that simply does memberwise 1529 * initialization: 1530 * 1531 * this.field1 = rhs.field1; 1532 * this.field2 = rhs.field2; 1533 * ... 1534 * 1535 * Params: 1536 * sd = the `struct` declaration that contains the copy constructor 1537 * 1538 * Returns: 1539 * A `CompoundStatement` containing the body of the copy constructor. 1540 */ 1541 private Statement generateCopyCtorBody(StructDeclaration sd) 1542 { 1543 Loc loc; 1544 Expression e; 1545 foreach (v; sd.fields) 1546 { 1547 auto ec = new AssignExp(loc, 1548 new DotVarExp(loc, new ThisExp(loc), v), 1549 new DotVarExp(loc, new IdentifierExp(loc, Id.p), v)); 1550 e = Expression.combine(e, ec); 1551 //printf("e.toChars = %s\n", e.toChars()); 1552 } 1553 Statement s1 = new ExpStatement(loc, e); 1554 return new CompoundStatement(loc, s1); 1555 } 1556 1557 /** 1558 * Determine if a copy constructor is needed for struct sd, 1559 * if the following conditions are met: 1560 * 1561 * 1. sd does not define a copy constructor 1562 * 2. at least one field of sd defines a copy constructor 1563 * 1564 * Params: 1565 * sd = the `struct` for which the copy constructor is generated 1566 * hasCpCtor = set to true if a copy constructor is already present 1567 * 1568 * Returns: 1569 * `true` if one needs to be generated 1570 * `false` otherwise 1571 */ 1572 private bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) 1573 { 1574 if (global.errors) 1575 return false; 1576 1577 auto ctor = sd.search(sd.loc, Id.ctor); 1578 if (ctor) 1579 { 1580 if (ctor.isOverloadSet()) 1581 return false; 1582 if (auto td = ctor.isTemplateDeclaration()) 1583 ctor = td.funcroot; 1584 } 1585 1586 CtorDeclaration cpCtor; 1587 CtorDeclaration rvalueCtor; 1588 1589 if (!ctor) 1590 goto LcheckFields; 1591 1592 overloadApply(ctor, (Dsymbol s) 1593 { 1594 if (s.isTemplateDeclaration()) 1595 return 0; 1596 auto ctorDecl = s.isCtorDeclaration(); 1597 assert(ctorDecl); 1598 if (ctorDecl.isCpCtor) 1599 { 1600 if (!cpCtor) 1601 cpCtor = ctorDecl; 1602 return 0; 1603 } 1604 1605 auto tf = ctorDecl.type.toTypeFunction(); 1606 const dim = tf.parameterList.length; 1607 if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) 1608 { 1609 auto param = tf.parameterList[0]; 1610 if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) 1611 { 1612 rvalueCtor = ctorDecl; 1613 } 1614 } 1615 return 0; 1616 }); 1617 1618 if (cpCtor) 1619 { 1620 if (rvalueCtor) 1621 { 1622 .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars()); 1623 errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); 1624 errorSupplemental(cpCtor.loc, "copy constructor defined here"); 1625 } 1626 hasCpCtor = true; 1627 return false; 1628 } 1629 1630 LcheckFields: 1631 VarDeclaration fieldWithCpCtor; 1632 // see if any struct members define a copy constructor 1633 foreach (v; sd.fields) 1634 { 1635 if (v.storage_class & STC.ref_) 1636 continue; 1637 if (v.overlapped) 1638 continue; 1639 1640 auto ts = v.type.baseElemOf().isTypeStruct(); 1641 if (!ts) 1642 continue; 1643 if (ts.sym.hasCopyCtor) 1644 { 1645 fieldWithCpCtor = v; 1646 break; 1647 } 1648 } 1649 1650 if (fieldWithCpCtor && rvalueCtor) 1651 { 1652 .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars()); 1653 errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); 1654 errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here"); 1655 return false; 1656 } 1657 else if (!fieldWithCpCtor) 1658 return false; 1659 return true; 1660 } 1661 1662 /** 1663 * Generates a copy constructor if needCopyCtor() returns true. 1664 * The generated copy constructor will be of the form: 1665 * this(ref return scope inout(S) rhs) inout 1666 * { 1667 * this.field1 = rhs.field1; 1668 * this.field2 = rhs.field2; 1669 * ... 1670 * } 1671 * 1672 * Params: 1673 * sd = the `struct` for which the copy constructor is generated 1674 * sc = the scope where the copy constructor is generated 1675 * 1676 * Returns: 1677 * `true` if `struct` sd defines a copy constructor (explicitly or generated), 1678 * `false` otherwise. 1679 */ 1680 bool buildCopyCtor(StructDeclaration sd, Scope* sc) 1681 { 1682 bool hasCpCtor; 1683 if (!needCopyCtor(sd, hasCpCtor)) 1684 return hasCpCtor; 1685 1686 //printf("generating copy constructor for %s\n", sd.toChars()); 1687 const MOD paramMod = MODFlags.wild; 1688 const MOD funcMod = MODFlags.wild; 1689 auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod)); 1690 auto copyCtorBody = generateCopyCtorBody(sd); 1691 ccd.fbody = copyCtorBody; 1692 sd.members.push(ccd); 1693 ccd.addMember(sc, sd); 1694 const errors = global.startGagging(); 1695 Scope* sc2 = sc.push(); 1696 sc2.stc = 0; 1697 sc2.linkage = LINK.d; 1698 ccd.dsymbolSemantic(sc2); 1699 ccd.semantic2(sc2); 1700 ccd.semantic3(sc2); 1701 //printf("ccd semantic: %s\n", ccd.type.toChars()); 1702 sc2.pop(); 1703 if (global.endGagging(errors) || sd.isUnionDeclaration()) 1704 { 1705 ccd.storage_class |= STC.disable; 1706 ccd.fbody = null; 1707 } 1708 return true; 1709 }