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 = new 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(loc, 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 = new 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 scx.eSink = sc.eSink; 550 /* const bool opEquals(ref const S s); 551 */ 552 auto parameters = new Parameters(); 553 parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); 554 tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d); 555 tfeqptr.mod = MODFlags.const_; 556 tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx); 557 } 558 fd = fd.overloadExactMatch(tfeqptr); 559 if (fd) 560 return fd; 561 } 562 } 563 if (!sd.xerreq) 564 { 565 // object._xopEquals 566 Identifier id = Identifier.idPool("_xopEquals"); 567 Expression e = new IdentifierExp(sd.loc, Id.empty); 568 e = new DotIdExp(sd.loc, e, Id.object); 569 e = new DotIdExp(sd.loc, e, id); 570 e = e.expressionSemantic(sc); 571 if (!e.isErrorExp()) 572 { 573 Dsymbol s = getDsymbol(e); 574 assert(s); 575 sd.xerreq = s.isFuncDeclaration(); 576 } 577 } 578 Loc declLoc; // loc is unnecessary so __xopEquals is never called directly 579 Loc loc; // loc is unnecessary so errors are gagged 580 auto parameters = new Parameters(); 581 parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); 582 auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_); 583 tf = tf.addSTC(STC.const_).toTypeFunction(); 584 Identifier id = Id.xopEquals; 585 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); 586 fop.isGenerated = true; 587 fop.parent = sd; 588 Expression e1 = new IdentifierExp(loc, Id.This); 589 Expression e2 = new IdentifierExp(loc, Id.p); 590 Expression e = new EqualExp(EXP.equal, loc, e1, e2); 591 fop.fbody = new ReturnStatement(loc, e); 592 uint errors = global.startGagging(); // Do not report errors 593 Scope* sc2 = sc.push(); 594 sc2.stc = 0; 595 sc2.linkage = LINK.d; 596 fop.dsymbolSemantic(sc2); 597 fop.semantic2(sc2); 598 sc2.pop(); 599 if (global.endGagging(errors)) // if errors happened 600 fop = sd.xerreq; 601 return fop; 602 } 603 604 /****************************************** 605 * Build __xopCmp for TypeInfo_Struct 606 * int __xopCmp(ref const S p) const 607 * { 608 * return this.opCmp(p); 609 * } 610 * 611 * This is called by TypeInfo.compare(p1, p2). If the struct does not support 612 * const objects comparison, it will throw "not implemented" Error in runtime. 613 */ 614 FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) 615 { 616 //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); 617 if (Dsymbol cmp = search_function(sd, Id.cmp)) 618 { 619 if (FuncDeclaration fd = cmp.isFuncDeclaration()) 620 { 621 TypeFunction tfcmpptr; 622 { 623 Scope scx; 624 scx.eSink = sc.eSink; 625 /* const int opCmp(ref const S s); 626 */ 627 auto parameters = new Parameters(); 628 parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); 629 tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d); 630 tfcmpptr.mod = MODFlags.const_; 631 tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx); 632 } 633 fd = fd.overloadExactMatch(tfcmpptr); 634 if (fd) 635 return fd; 636 } 637 } 638 else 639 { 640 version (none) // FIXME: doesn't work for recursive alias this 641 { 642 /* Check opCmp member exists. 643 * Consider 'alias this', but except opDispatch. 644 */ 645 Expression e = new DsymbolExp(sd.loc, sd); 646 e = new DotIdExp(sd.loc, e, Id.cmp); 647 Scope* sc2 = sc.push(); 648 e = e.trySemantic(sc2); 649 sc2.pop(); 650 if (e) 651 { 652 Dsymbol s = null; 653 switch (e.op) 654 { 655 case EXP.overloadSet: 656 s = e.isOverExp().vars; 657 break; 658 case EXP.scope_: 659 s = e.isScopeExp().sds; 660 break; 661 case EXP.variable: 662 s = e.isVarExp().var; 663 break; 664 default: 665 break; 666 } 667 if (!s || s.ident != Id.cmp) 668 e = null; // there's no valid member 'opCmp' 669 } 670 if (!e) 671 return null; // bitwise comparison would work 672 /* Essentially, a struct which does not define opCmp is not comparable. 673 * At this time, typeid(S).compare might be correct that throwing "not implement" Error. 674 * But implementing it would break existing code, such as: 675 * 676 * struct S { int value; } // no opCmp 677 * int[S] aa; // Currently AA key uses bitwise comparison 678 * // (It's default behavior of TypeInfo_Strust.compare). 679 * 680 * Not sure we should fix this inconsistency, so just keep current behavior. 681 */ 682 } 683 else 684 { 685 return null; 686 } 687 } 688 if (!sd.xerrcmp) 689 { 690 // object._xopCmp 691 Identifier id = Identifier.idPool("_xopCmp"); 692 Expression e = new IdentifierExp(sd.loc, Id.empty); 693 e = new DotIdExp(sd.loc, e, Id.object); 694 e = new DotIdExp(sd.loc, e, id); 695 e = e.expressionSemantic(sc); 696 if (!e.isErrorExp()) 697 { 698 Dsymbol s = getDsymbol(e); 699 assert(s); 700 sd.xerrcmp = s.isFuncDeclaration(); 701 } 702 } 703 Loc declLoc; // loc is unnecessary so __xopCmp is never called directly 704 Loc loc; // loc is unnecessary so errors are gagged 705 auto parameters = new Parameters(); 706 parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); 707 auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_); 708 tf = tf.addSTC(STC.const_).toTypeFunction(); 709 Identifier id = Id.xopCmp; 710 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); 711 fop.isGenerated = true; 712 fop.parent = sd; 713 Expression e1 = new IdentifierExp(loc, Id.This); 714 Expression e2 = new IdentifierExp(loc, Id.p); 715 Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2); 716 fop.fbody = new ReturnStatement(loc, e); 717 uint errors = global.startGagging(); // Do not report errors 718 Scope* sc2 = sc.push(); 719 sc2.stc = 0; 720 sc2.linkage = LINK.d; 721 fop.dsymbolSemantic(sc2); 722 fop.semantic2(sc2); 723 sc2.pop(); 724 if (global.endGagging(errors)) // if errors happened 725 fop = sd.xerrcmp; 726 return fop; 727 } 728 729 /******************************************* 730 * We need a toHash for the struct if 731 * any fields has a toHash. 732 * Generate one if a user-specified one does not exist. 733 */ 734 private bool needToHash(StructDeclaration sd) 735 { 736 //printf("StructDeclaration::needToHash() %s\n", sd.toChars()); 737 if (sd.isUnionDeclaration()) 738 goto Ldontneed; 739 if (sd.xhash) 740 goto Lneed; 741 742 /* If any of the fields has an toHash, then we 743 * need it too. 744 */ 745 foreach (VarDeclaration v; sd.fields) 746 { 747 if (v.storage_class & STC.ref_) 748 continue; 749 if (v.overlapped) 750 continue; 751 Type tv = v.type.toBasetype(); 752 auto tvbase = tv.baseElemOf(); 753 if (tvbase.ty == Tstruct) 754 { 755 TypeStruct ts = cast(TypeStruct)tvbase; 756 if (ts.sym.isUnionDeclaration()) 757 continue; 758 if (needToHash(ts.sym)) 759 goto Lneed; 760 } 761 if (tvbase.isfloating()) 762 { 763 /* This is necessary because comparison of +0.0 and -0.0 should be true, 764 * i.e. not a bit compare. 765 */ 766 goto Lneed; 767 } 768 if (tvbase.ty == Tarray) 769 goto Lneed; 770 if (tvbase.ty == Taarray) 771 goto Lneed; 772 if (tvbase.ty == Tclass) 773 goto Lneed; 774 } 775 Ldontneed: 776 //printf("\tdontneed\n"); 777 return false; 778 Lneed: 779 //printf("\tneed\n"); 780 return true; 781 } 782 783 /****************************************** 784 * Build __xtoHash for non-bitwise hashing 785 * static hash_t xtoHash(ref const S p) nothrow @trusted; 786 */ 787 FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) 788 { 789 if (Dsymbol s = search_function(sd, Id.tohash)) 790 { 791 __gshared TypeFunction tftohash; 792 if (!tftohash) 793 { 794 tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d); 795 tftohash.mod = MODFlags.const_; 796 tftohash = cast(TypeFunction)tftohash.merge(); 797 } 798 if (FuncDeclaration fd = s.isFuncDeclaration()) 799 { 800 fd = fd.overloadExactMatch(tftohash); 801 if (fd) 802 return fd; 803 } 804 } 805 if (!needToHash(sd)) 806 return null; 807 808 /* The trouble is that the following code relies on .tupleof, but .tupleof 809 * is not allowed for C files. If we allow it for C files, then that turns on 810 * the other D properties, too, such as .dup which will then conflict with allowed 811 * field names. 812 * One way to fix it is to replace the following foreach and .tupleof with C 813 * statements and expressions. 814 * But, it's debatable whether C structs should even need toHash(). 815 * Note that it would only be necessary if it has floating point fields. 816 * For now, we'll just not generate a toHash() for C files. 817 */ 818 if (sc.flags & SCOPE.Cfile) 819 return null; 820 821 //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); 822 Loc declLoc; // loc is unnecessary so __xtoHash is never called directly 823 Loc loc; // internal code should have no loc to prevent coverage 824 auto parameters = new Parameters(); 825 parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null)); 826 auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted); 827 Identifier id = Id.xtoHash; 828 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf); 829 fop.isGenerated = true; 830 831 /* Do memberwise hashing. 832 * 833 * If sd is a nested struct, and if it's nested in a class, the calculated 834 * hash value will also contain the result of parent class's toHash(). 835 */ 836 const(char)[] code = 837 ".object.size_t h = 0;" ~ 838 "foreach (i, T; typeof(p.tupleof))" ~ 839 // workaround https://issues.dlang.org/show_bug.cgi?id=17968 840 " static if(is(T* : const(.object.Object)*)) " ~ 841 " h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~ 842 " else " ~ 843 " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~ 844 "return h;"; 845 fop.fbody = new MixinStatement(loc, new StringExp(loc, code)); 846 Scope* sc2 = sc.push(); 847 sc2.stc = 0; 848 sc2.linkage = LINK.d; 849 fop.dsymbolSemantic(sc2); 850 fop.semantic2(sc2); 851 sc2.pop(); 852 853 //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars()); 854 return fop; 855 } 856 857 /***************************************** 858 * Create aggregate destructor for struct/class by aggregating 859 * all the destructors in userDtors[] with the destructors for 860 * all the members. 861 * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields. 862 * Params: 863 * ad = struct or class to build destructor for 864 * sc = context 865 * Note: 866 * Close similarity with StructDeclaration::buildPostBlit(), 867 * and the ordering changes (runs backward instead of forwards). 868 */ 869 void buildDtors(AggregateDeclaration ad, Scope* sc) 870 { 871 //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars()); 872 if (ad.isUnionDeclaration()) 873 return; // unions don't have destructors 874 875 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 876 Loc declLoc = ad.userDtors.length ? ad.userDtors[0].loc : ad.loc; 877 Loc loc; // internal code should have no loc to prevent coverage 878 FuncDeclaration xdtor_fwd = null; 879 880 // Build the field destructor (`ad.fieldDtor`), if needed. 881 // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building. 882 const bool dtorIsCppPrototype = ad.userDtors.length && ad.userDtors[0]._linkage == LINK.cpp && !ad.userDtors[0].fbody; 883 if (!dtorIsCppPrototype) 884 { 885 Expression e = null; 886 for (size_t i = 0; i < ad.fields.length; i++) 887 { 888 auto v = ad.fields[i]; 889 if (v.storage_class & STC.ref_) 890 continue; 891 if (v.overlapped) 892 continue; 893 auto tv = v.type.baseElemOf(); 894 if (tv.ty != Tstruct) 895 continue; 896 auto sdv = (cast(TypeStruct)tv).sym; 897 if (!sdv.dtor) 898 continue; 899 900 // fix: https://issues.dlang.org/show_bug.cgi?id=17257 901 // braces for shrink wrapping scope of a 902 { 903 xdtor_fwd = sdv.dtor; // this dtor is temporary it could be anything 904 auto a = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor_fwd); 905 a.addMember(sc, ad); // temporarily add to symbol table 906 } 907 908 sdv.dtor.functionSemantic(); 909 910 stc = mergeFuncAttrs(stc, sdv.dtor); 911 if (stc & STC.disable) 912 { 913 e = null; 914 break; 915 } 916 917 Expression ex; 918 tv = v.type.toBasetype(); 919 if (tv.ty == Tstruct) 920 { 921 // this.v.__xdtor() 922 923 ex = new ThisExp(loc); 924 ex = new DotVarExp(loc, ex, v); 925 926 // This is a hack so we can call destructors on const/immutable objects. 927 // Do it as a type 'paint', `cast()` 928 ex = new CastExp(loc, ex, MODFlags.none); 929 if (stc & STC.safe) 930 stc = (stc & ~STC.safe) | STC.trusted; 931 932 ex = new DotVarExp(loc, ex, sdv.dtor, false); 933 ex = new CallExp(loc, ex); 934 } 935 else 936 { 937 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) 938 939 const n = tv.numberOfElems(loc); 940 if (n == 0) 941 continue; 942 943 ex = new ThisExp(loc); 944 ex = new DotVarExp(loc, ex, v); 945 946 // This is a hack so we can call destructors on const/immutable objects. 947 ex = new DotIdExp(loc, ex, Id.ptr); 948 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 949 if (stc & STC.safe) 950 stc = (stc & ~STC.safe) | STC.trusted; 951 952 SliceExp se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 953 new IntegerExp(loc, n, Type.tsize_t)); 954 // Prevent redundant bounds check 955 se.upperIsInBounds = true; 956 se.lowerIsLessThanUpper = true; 957 958 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); 959 } 960 e = Expression.combine(ex, e); // combine in reverse order 961 } 962 963 if (e || (stc & STC.disable)) 964 { 965 //printf("Building __fieldDtor(), %s\n", e.toChars()); 966 auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor); 967 dd.isGenerated = true; 968 dd.storage_class |= STC.inference; 969 dd.fbody = new ExpStatement(loc, e); 970 ad.members.push(dd); 971 dd.dsymbolSemantic(sc); 972 ad.fieldDtor = dd; 973 } 974 } 975 976 // Generate list of dtors to call in that order 977 DtorDeclarations dtors; 978 foreach_reverse (userDtor; ad.userDtors[]) 979 dtors.push(userDtor); 980 if (ad.fieldDtor) 981 dtors.push(ad.fieldDtor); 982 if (!dtorIsCppPrototype) 983 { 984 // extern(C++) destructors call into super to destruct the full hierarchy 985 ClassDeclaration cldec = ad.isClassDeclaration(); 986 if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor) 987 dtors.push(cldec.baseClass.aggrDtor); 988 } 989 990 // Set/build `ad.aggrDtor` 991 switch (dtors.length) 992 { 993 case 0: 994 break; 995 996 case 1: 997 // Use the single existing dtor directly as aggregate dtor. 998 // Note that this might be `cldec.baseClass.aggrDtor`. 999 ad.aggrDtor = dtors[0]; 1000 break; 1001 1002 default: 1003 // Build the aggregate destructor, calling all dtors in order. 1004 assert(!dtorIsCppPrototype); 1005 Expression e = null; 1006 e = null; 1007 stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1008 foreach (FuncDeclaration fd; dtors) 1009 { 1010 stc = mergeFuncAttrs(stc, fd); 1011 if (stc & STC.disable) 1012 { 1013 e = null; 1014 break; 1015 } 1016 Expression ex = new ThisExp(loc); 1017 ex = new DotVarExp(loc, ex, fd, false); 1018 CallExp ce = new CallExp(loc, ex); 1019 ce.directcall = true; 1020 e = Expression.combine(e, ce); 1021 } 1022 auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor); 1023 dd.isGenerated = true; 1024 dd.storage_class |= STC.inference; 1025 dd.fbody = new ExpStatement(loc, e); 1026 ad.members.push(dd); 1027 dd.dsymbolSemantic(sc); 1028 ad.aggrDtor = dd; 1029 break; 1030 } 1031 1032 // Set/build `ad.dtor`. 1033 // On Windows, the dtor in the vtable is a shim with different signature. 1034 ad.dtor = (ad.aggrDtor && ad.aggrDtor._linkage == LINK.cpp && !target.cpp.twoDtorInVtable) 1035 ? buildWindowsCppDtor(ad, ad.aggrDtor, sc) 1036 : ad.aggrDtor; 1037 1038 // Add an __xdtor alias to make `ad.dtor` accessible 1039 if (ad.dtor) 1040 { 1041 auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, ad.dtor); 1042 _alias.dsymbolSemantic(sc); 1043 ad.members.push(_alias); 1044 if (xdtor_fwd) 1045 ad.symtab.update(_alias); // update forward dtor to correct one 1046 else 1047 _alias.addMember(sc, ad); // add to symbol table 1048 } 1049 1050 // Set/build `ad.tidtor` 1051 ad.tidtor = buildExternDDtor(ad, sc); 1052 } 1053 1054 /** 1055 * build a shim function around the compound dtor that accepts an argument 1056 * that is used to implement the deleting C++ destructor 1057 * 1058 * Params: 1059 * ad = the aggregate that contains the destructor to wrap 1060 * dtor = the destructor to wrap 1061 * sc = the scope in which to analyze the new function 1062 * 1063 * Returns: 1064 * the shim destructor, semantically analyzed and added to the class as a member 1065 */ 1066 private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclaration dtor, Scope* sc) 1067 { 1068 auto cldec = ad.isClassDeclaration(); 1069 if (!cldec || cldec.cppDtorVtblIndex == -1) // scalar deleting dtor not built for non-virtual dtors 1070 return dtor; // perhaps also do this if STC.scope_ is set 1071 1072 // generate deleting C++ destructor corresponding to: 1073 // void* C::~C(int del) 1074 // { 1075 // this->~C(); 1076 // // TODO: if (del) delete (char*)this; 1077 // return (void*) this; 1078 // } 1079 Parameter delparam = new Parameter(Loc.initial, STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); 1080 Parameters* params = new Parameters; 1081 params.push(delparam); 1082 const stc = dtor.storage_class & ~STC.scope_; // because we add the `return this;` later 1083 auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, stc); 1084 auto func = new DtorDeclaration(dtor.loc, dtor.loc, stc, Id.cppdtor); 1085 func.type = ftype; 1086 1087 // Always generate the function with body, because it is not exported from DLLs. 1088 const loc = dtor.loc; 1089 auto stmts = new Statements; 1090 auto call = new CallExp(loc, dtor, null); 1091 call.directcall = true; 1092 stmts.push(new ExpStatement(loc, call)); 1093 stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr))); 1094 func.fbody = new CompoundStatement(loc, stmts); 1095 func.isGenerated = true; 1096 1097 auto sc2 = sc.push(); 1098 sc2.stc &= ~STC.static_; // not a static destructor 1099 sc2.linkage = LINK.cpp; 1100 1101 ad.members.push(func); 1102 func.addMember(sc2, ad); 1103 func.dsymbolSemantic(sc2); 1104 1105 sc2.pop(); 1106 return func; 1107 } 1108 1109 /** 1110 * build a shim function around the aggregate dtor that translates 1111 * a C++ destructor to a destructor with extern(D) calling convention 1112 * 1113 * Params: 1114 * ad = the aggregate that contains the destructor to wrap 1115 * sc = the scope in which to analyze the new function 1116 * 1117 * Returns: 1118 * the shim destructor, semantically analyzed and added to the class as a member 1119 */ 1120 private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) 1121 { 1122 auto dtor = ad.aggrDtor; 1123 if (!dtor) 1124 return null; 1125 1126 // Don't try to call `@disable`d dtors 1127 if (dtor.storage_class & STC.disable) 1128 return null; 1129 1130 // Generate shim only when ABI incompatible on target platform 1131 if (dtor._linkage != LINK.cpp || !target.cpp.wrapDtorInExternD) 1132 return dtor; 1133 1134 // generate member function that adjusts calling convention 1135 // (EAX used for 'this' instead of ECX on Windows/stack on others): 1136 // extern(D) void __ticppdtor() 1137 // { 1138 // Class.__dtor(); 1139 // } 1140 auto ftype = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dtor.storage_class); 1141 auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.ticppdtor); 1142 func.type = ftype; 1143 1144 auto call = new CallExp(dtor.loc, dtor, null); 1145 call.directcall = true; // non-virtual call Class.__dtor(); 1146 func.fbody = new ExpStatement(dtor.loc, call); 1147 func.isGenerated = true; 1148 func.storage_class |= STC.inference; 1149 1150 auto sc2 = sc.push(); 1151 sc2.stc &= ~STC.static_; // not a static destructor 1152 sc2.linkage = LINK.d; 1153 1154 ad.members.push(func); 1155 func.addMember(sc2, ad); 1156 func.dsymbolSemantic(sc2); 1157 func.functionSemantic(); // to infer attributes 1158 1159 sc2.pop(); 1160 return func; 1161 } 1162 1163 /****************************************** 1164 * Create inclusive invariant for struct/class by aggregating 1165 * all the invariants in invs[]. 1166 * --- 1167 * void __invariant() const [pure nothrow @trusted] 1168 * { 1169 * invs[0](), invs[1](), ...; 1170 * } 1171 * --- 1172 */ 1173 FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) 1174 { 1175 switch (ad.invs.length) 1176 { 1177 case 0: 1178 return null; 1179 1180 case 1: 1181 // Don't return invs[0] so it has uniquely generated name. 1182 goto default; 1183 1184 default: 1185 Expression e = null; 1186 StorageClass stcx = 0; 1187 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1188 foreach (i, inv; ad.invs) 1189 { 1190 stc = mergeFuncAttrs(stc, inv); 1191 if (stc & STC.disable) 1192 { 1193 // What should do? 1194 } 1195 const stcy = (inv.storage_class & STC.synchronized_) | 1196 (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0); 1197 if (i == 0) 1198 stcx = stcy; 1199 else if (stcx ^ stcy) 1200 { 1201 version (all) 1202 { 1203 // currently rejects 1204 .error(inv.loc, "%s `%s` mixing invariants with different `shared`/`synchronized` qualifiers is not supported", ad.kind(), ad.toPrettyChars()); 1205 e = null; 1206 break; 1207 } 1208 } 1209 e = Expression.combine(e, new CallExp(Loc.initial, new VarExp(Loc.initial, inv, false))); 1210 } 1211 auto inv = new InvariantDeclaration(ad.loc, Loc.initial, stc | stcx, 1212 Id.classInvariant, new ExpStatement(Loc.initial, e)); 1213 ad.members.push(inv); 1214 inv.dsymbolSemantic(sc); 1215 return inv; 1216 } 1217 } 1218 1219 /***************************************** 1220 * Create inclusive postblit for struct by aggregating 1221 * all the postblits in postblits[] with the postblits for 1222 * all the members. 1223 * Note the close similarity with AggregateDeclaration::buildDtor(), 1224 * and the ordering changes (runs forward instead of backwards). 1225 */ 1226 FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) 1227 { 1228 //printf("buildPostBlit() %s\n", sd.toChars()); 1229 if (sd.isUnionDeclaration()) 1230 return null; 1231 1232 const hasUserDefinedPosblit = sd.postblits.length && !sd.postblits[0].isDisabled ? true : false; 1233 1234 // by default, the storage class of the created postblit 1235 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1236 Loc declLoc = sd.postblits.length ? sd.postblits[0].loc : sd.loc; 1237 Loc loc; // internal code should have no loc to prevent coverage 1238 1239 // if any of the postblits are disabled, then the generated postblit 1240 // will be disabled 1241 foreach (postblit; sd.postblits) 1242 stc |= postblit.storage_class & STC.disable; 1243 1244 VarDeclaration[] fieldsToDestroy; 1245 auto postblitCalls = new Statements(); 1246 // iterate through all the struct fields that are not disabled 1247 for (size_t i = 0; i < sd.fields.length && !(stc & STC.disable); i++) 1248 { 1249 auto structField = sd.fields[i]; 1250 if (structField.storage_class & STC.ref_) 1251 continue; 1252 if (structField.overlapped) 1253 continue; 1254 // if it's a struct declaration or an array of structs 1255 Type tv = structField.type.baseElemOf(); 1256 if (tv.ty != Tstruct) 1257 continue; 1258 auto sdv = (cast(TypeStruct)tv).sym; 1259 // which has a postblit declaration 1260 if (!sdv.postblit) 1261 continue; 1262 assert(!sdv.isUnionDeclaration()); 1263 1264 // if this field's postblit is not `nothrow`, add a `scope(failure)` 1265 // block to destroy any prior successfully postblitted fields should 1266 // this field's postblit fail. 1267 // Don't generate it for betterC code since it cannot throw exceptions. 1268 if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && global.params.useExceptions) 1269 { 1270 // create a list of destructors that need to be called 1271 Expression[] dtorCalls; 1272 foreach(sf; fieldsToDestroy) 1273 { 1274 Expression ex; 1275 tv = sf.type.toBasetype(); 1276 if (tv.ty == Tstruct) 1277 { 1278 // this.v.__xdtor() 1279 1280 ex = new ThisExp(loc); 1281 ex = new DotVarExp(loc, ex, sf); 1282 1283 // This is a hack so we can call destructors on const/immutable objects. 1284 ex = new AddrExp(loc, ex); 1285 ex = new CastExp(loc, ex, sf.type.mutableOf().pointerTo()); 1286 ex = new PtrExp(loc, ex); 1287 if (stc & STC.safe) 1288 stc = (stc & ~STC.safe) | STC.trusted; 1289 1290 auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym; 1291 1292 ex = new DotVarExp(loc, ex, sfv.dtor, false); 1293 ex = new CallExp(loc, ex); 1294 1295 dtorCalls ~= ex; 1296 } 1297 else 1298 { 1299 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) 1300 1301 const length = tv.numberOfElems(loc); 1302 1303 ex = new ThisExp(loc); 1304 ex = new DotVarExp(loc, ex, sf); 1305 1306 // This is a hack so we can call destructors on const/immutable objects. 1307 ex = new DotIdExp(loc, ex, Id.ptr); 1308 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 1309 if (stc & STC.safe) 1310 stc = (stc & ~STC.safe) | STC.trusted; 1311 1312 auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 1313 new IntegerExp(loc, length, Type.tsize_t)); 1314 // Prevent redundant bounds check 1315 se.upperIsInBounds = true; 1316 se.lowerIsLessThanUpper = true; 1317 1318 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); 1319 1320 dtorCalls ~= ex; 1321 } 1322 } 1323 fieldsToDestroy = []; 1324 1325 // aggregate the destructor calls 1326 auto dtors = new Statements(); 1327 foreach_reverse(dc; dtorCalls) 1328 { 1329 dtors.push(new ExpStatement(loc, dc)); 1330 } 1331 1332 // put destructor calls in a `scope(failure)` block 1333 postblitCalls.push(new ScopeGuardStatement(loc, TOK.onScopeFailure, new CompoundStatement(loc, dtors))); 1334 } 1335 1336 // perform semantic on the member postblit in order to 1337 // be able to aggregate it later on with the rest of the 1338 // postblits 1339 sdv.postblit.functionSemantic(); 1340 1341 stc = mergeFuncAttrs(stc, sdv.postblit); 1342 stc = mergeFuncAttrs(stc, sdv.dtor); 1343 1344 // if any of the struct member fields has disabled 1345 // its postblit, then `sd` is not copyable, so no 1346 // postblit is generated 1347 if (stc & STC.disable) 1348 { 1349 postblitCalls.setDim(0); 1350 break; 1351 } 1352 1353 Expression ex; 1354 tv = structField.type.toBasetype(); 1355 if (tv.ty == Tstruct) 1356 { 1357 // this.v.__xpostblit() 1358 1359 ex = new ThisExp(loc); 1360 ex = new DotVarExp(loc, ex, structField); 1361 1362 // This is a hack so we can call postblits on const/immutable objects. 1363 ex = new AddrExp(loc, ex); 1364 ex = new CastExp(loc, ex, structField.type.mutableOf().pointerTo()); 1365 ex = new PtrExp(loc, ex); 1366 if (stc & STC.safe) 1367 stc = (stc & ~STC.safe) | STC.trusted; 1368 1369 ex = new DotVarExp(loc, ex, sdv.postblit, false); 1370 ex = new CallExp(loc, ex); 1371 } 1372 else 1373 { 1374 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n]) 1375 1376 const length = tv.numberOfElems(loc); 1377 if (length == 0) 1378 continue; 1379 1380 ex = new ThisExp(loc); 1381 ex = new DotVarExp(loc, ex, structField); 1382 1383 // This is a hack so we can call postblits on const/immutable objects. 1384 ex = new DotIdExp(loc, ex, Id.ptr); 1385 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 1386 if (stc & STC.safe) 1387 stc = (stc & ~STC.safe) | STC.trusted; 1388 1389 auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 1390 new IntegerExp(loc, length, Type.tsize_t)); 1391 // Prevent redundant bounds check 1392 se.upperIsInBounds = true; 1393 se.lowerIsLessThanUpper = true; 1394 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayPostblit), se); 1395 } 1396 postblitCalls.push(new ExpStatement(loc, ex)); // combine in forward order 1397 1398 /* https://issues.dlang.org/show_bug.cgi?id=10972 1399 * When subsequent field postblit calls fail, 1400 * this field should be destructed for Exception Safety. 1401 */ 1402 if (sdv.dtor) 1403 { 1404 sdv.dtor.functionSemantic(); 1405 1406 // keep a list of fields that need to be destroyed in case 1407 // of a future postblit failure 1408 fieldsToDestroy ~= structField; 1409 } 1410 } 1411 1412 void checkShared() 1413 { 1414 if (sd.type.isShared()) 1415 stc |= STC.shared_; 1416 } 1417 1418 // Build our own "postblit" which executes a, but only if needed. 1419 if (postblitCalls.length || (stc & STC.disable)) 1420 { 1421 //printf("Building __fieldPostBlit()\n"); 1422 checkShared(); 1423 auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__fieldPostblit); 1424 dd.isGenerated = true; 1425 dd.storage_class |= STC.inference | STC.scope_; 1426 dd.fbody = (stc & STC.disable) ? null : new CompoundStatement(loc, postblitCalls); 1427 sd.postblits.shift(dd); 1428 sd.members.push(dd); 1429 dd.dsymbolSemantic(sc); 1430 } 1431 1432 // create __xpostblit, which is the generated postblit 1433 FuncDeclaration xpostblit = null; 1434 switch (sd.postblits.length) 1435 { 1436 case 0: 1437 break; 1438 1439 case 1: 1440 xpostblit = sd.postblits[0]; 1441 break; 1442 1443 default: 1444 Expression e = null; 1445 stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; 1446 foreach (fd; sd.postblits) 1447 { 1448 stc = mergeFuncAttrs(stc, fd); 1449 if (stc & STC.disable) 1450 { 1451 e = null; 1452 break; 1453 } 1454 Expression ex = new ThisExp(loc); 1455 ex = new DotVarExp(loc, ex, fd, false); 1456 ex = new CallExp(loc, ex); 1457 e = Expression.combine(e, ex); 1458 } 1459 1460 checkShared(); 1461 auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__aggrPostblit); 1462 dd.isGenerated = true; 1463 dd.storage_class |= STC.inference; 1464 dd.fbody = new ExpStatement(loc, e); 1465 sd.members.push(dd); 1466 dd.dsymbolSemantic(sc); 1467 xpostblit = dd; 1468 break; 1469 } 1470 1471 // Add an __xpostblit alias to make the inclusive postblit accessible 1472 if (xpostblit) 1473 { 1474 auto _alias = new AliasDeclaration(Loc.initial, Id.__xpostblit, xpostblit); 1475 _alias.dsymbolSemantic(sc); 1476 sd.members.push(_alias); 1477 _alias.addMember(sc, sd); // add to symbol table 1478 } 1479 1480 if (sd.hasCopyCtor) 1481 { 1482 // we have user defined postblit, so we prioritize it 1483 if (hasUserDefinedPosblit) 1484 { 1485 sd.hasCopyCtor = false; 1486 return xpostblit; 1487 } 1488 // we have fields with postblits, so print deprecations 1489 if (xpostblit && !xpostblit.isDisabled()) 1490 { 1491 deprecation(sd.loc, "`struct %s` implicitly-generated postblit hides copy constructor.", sd.toChars); 1492 deprecationSupplemental(sd.loc, "The field postblit will have priority over the copy constructor."); 1493 deprecationSupplemental(sd.loc, "To change this, the postblit should be disabled for `struct %s`", sd.toChars()); 1494 sd.hasCopyCtor = false; 1495 } 1496 else 1497 xpostblit = null; 1498 } 1499 1500 return xpostblit; 1501 } 1502 1503 /** 1504 * Generates a copy constructor declaration with the specified storage 1505 * class for the parameter and the function. 1506 * 1507 * Params: 1508 * sd = the `struct` that contains the copy constructor 1509 * paramStc = the storage class of the copy constructor parameter 1510 * funcStc = the storage class for the copy constructor declaration 1511 * 1512 * Returns: 1513 * The copy constructor declaration for struct `sd`. 1514 */ 1515 private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc) 1516 { 1517 auto fparams = new Parameters(); 1518 auto structType = sd.type; 1519 fparams.push(new Parameter(Loc.initial, paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null)); 1520 ParameterList pList = ParameterList(fparams); 1521 auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_); 1522 auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true); 1523 ccd.storage_class |= funcStc; 1524 ccd.storage_class |= STC.inference; 1525 ccd.isGenerated = true; 1526 return ccd; 1527 } 1528 1529 /** 1530 * Generates a trivial copy constructor body that simply does memberwise 1531 * initialization: 1532 * 1533 * this.field1 = rhs.field1; 1534 * this.field2 = rhs.field2; 1535 * ... 1536 * 1537 * Params: 1538 * sd = the `struct` declaration that contains the copy constructor 1539 * 1540 * Returns: 1541 * A `CompoundStatement` containing the body of the copy constructor. 1542 */ 1543 private Statement generateCopyCtorBody(StructDeclaration sd) 1544 { 1545 Loc loc; 1546 Expression e; 1547 foreach (v; sd.fields) 1548 { 1549 auto ec = new AssignExp(loc, 1550 new DotVarExp(loc, new ThisExp(loc), v), 1551 new DotVarExp(loc, new IdentifierExp(loc, Id.p), v)); 1552 e = Expression.combine(e, ec); 1553 //printf("e.toChars = %s\n", e.toChars()); 1554 } 1555 Statement s1 = new ExpStatement(loc, e); 1556 return new CompoundStatement(loc, s1); 1557 } 1558 1559 /** 1560 * Determine if a copy constructor is needed for struct sd, 1561 * if the following conditions are met: 1562 * 1563 * 1. sd does not define a copy constructor 1564 * 2. at least one field of sd defines a copy constructor 1565 * 1566 * Params: 1567 * sd = the `struct` for which the copy constructor is generated 1568 * hasCpCtor = set to true if a copy constructor is already present 1569 * 1570 * Returns: 1571 * `true` if one needs to be generated 1572 * `false` otherwise 1573 */ 1574 private bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) 1575 { 1576 if (global.errors) 1577 return false; 1578 1579 auto ctor = sd.search(sd.loc, Id.ctor); 1580 if (ctor) 1581 { 1582 if (ctor.isOverloadSet()) 1583 return false; 1584 if (auto td = ctor.isTemplateDeclaration()) 1585 ctor = td.funcroot; 1586 } 1587 1588 CtorDeclaration cpCtor; 1589 CtorDeclaration rvalueCtor; 1590 1591 if (!ctor) 1592 goto LcheckFields; 1593 1594 overloadApply(ctor, (Dsymbol s) 1595 { 1596 if (s.isTemplateDeclaration()) 1597 return 0; 1598 auto ctorDecl = s.isCtorDeclaration(); 1599 assert(ctorDecl); 1600 if (ctorDecl.isCpCtor) 1601 { 1602 if (!cpCtor) 1603 cpCtor = ctorDecl; 1604 return 0; 1605 } 1606 1607 auto tf = ctorDecl.type.toTypeFunction(); 1608 const dim = tf.parameterList.length; 1609 if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) 1610 { 1611 auto param = tf.parameterList[0]; 1612 if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) 1613 { 1614 rvalueCtor = ctorDecl; 1615 } 1616 } 1617 return 0; 1618 }); 1619 1620 if (cpCtor) 1621 { 1622 if (rvalueCtor) 1623 { 1624 .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars()); 1625 errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); 1626 errorSupplemental(cpCtor.loc, "copy constructor defined here"); 1627 } 1628 hasCpCtor = true; 1629 return false; 1630 } 1631 1632 LcheckFields: 1633 VarDeclaration fieldWithCpCtor; 1634 // see if any struct members define a copy constructor 1635 foreach (v; sd.fields) 1636 { 1637 if (v.storage_class & STC.ref_) 1638 continue; 1639 if (v.overlapped) 1640 continue; 1641 1642 auto ts = v.type.baseElemOf().isTypeStruct(); 1643 if (!ts) 1644 continue; 1645 if (ts.sym.hasCopyCtor) 1646 { 1647 fieldWithCpCtor = v; 1648 break; 1649 } 1650 } 1651 1652 if (fieldWithCpCtor && rvalueCtor) 1653 { 1654 .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars()); 1655 errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); 1656 errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here"); 1657 return false; 1658 } 1659 else if (!fieldWithCpCtor) 1660 return false; 1661 return true; 1662 } 1663 1664 /** 1665 * Generates a copy constructor if needCopyCtor() returns true. 1666 * The generated copy constructor will be of the form: 1667 * this(ref return scope inout(S) rhs) inout 1668 * { 1669 * this.field1 = rhs.field1; 1670 * this.field2 = rhs.field2; 1671 * ... 1672 * } 1673 * 1674 * Params: 1675 * sd = the `struct` for which the copy constructor is generated 1676 * sc = the scope where the copy constructor is generated 1677 * 1678 * Returns: 1679 * `true` if `struct` sd defines a copy constructor (explicitly or generated), 1680 * `false` otherwise. 1681 */ 1682 bool buildCopyCtor(StructDeclaration sd, Scope* sc) 1683 { 1684 bool hasCpCtor; 1685 if (!needCopyCtor(sd, hasCpCtor)) 1686 return hasCpCtor; 1687 1688 //printf("generating copy constructor for %s\n", sd.toChars()); 1689 const MOD paramMod = MODFlags.wild; 1690 const MOD funcMod = MODFlags.wild; 1691 auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod)); 1692 auto copyCtorBody = generateCopyCtorBody(sd); 1693 ccd.fbody = copyCtorBody; 1694 sd.members.push(ccd); 1695 ccd.addMember(sc, sd); 1696 const errors = global.startGagging(); 1697 Scope* sc2 = sc.push(); 1698 sc2.stc = 0; 1699 sc2.linkage = LINK.d; 1700 ccd.dsymbolSemantic(sc2); 1701 ccd.semantic2(sc2); 1702 ccd.semantic3(sc2); 1703 //printf("ccd semantic: %s\n", ccd.type.toChars()); 1704 sc2.pop(); 1705 if (global.endGagging(errors) || sd.isUnionDeclaration()) 1706 { 1707 ccd.storage_class |= STC.disable; 1708 ccd.fbody = null; 1709 } 1710 return true; 1711 }