1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _clone.d) 9 */ 10 11 module ddmd.clone; 12 13 import core.stdc.stdio; 14 import ddmd.aggregate; 15 import ddmd.arraytypes; 16 import ddmd.declaration; 17 import ddmd.dscope; 18 import ddmd.dstruct; 19 import ddmd.dsymbol; 20 import ddmd.dtemplate; 21 import ddmd.expression; 22 import ddmd.func; 23 import ddmd.globals; 24 import ddmd.id; 25 import ddmd.identifier; 26 import ddmd.init; 27 import ddmd.mtype; 28 import ddmd.opover; 29 import ddmd.statement; 30 import ddmd.tokens; 31 32 /******************************************* 33 * Merge function attributes pure, nothrow, @safe, @nogc, and @disable 34 */ 35 extern (C++) StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration f) 36 { 37 if (!f) 38 return s1; 39 StorageClass s2 = (f.storage_class & STCdisable); 40 TypeFunction tf = cast(TypeFunction)f.type; 41 if (tf.trust == TRUSTsafe) 42 s2 |= STCsafe; 43 else if (tf.trust == TRUSTsystem) 44 s2 |= STCsystem; 45 else if (tf.trust == TRUSTtrusted) 46 s2 |= STCtrusted; 47 if (tf.purity != PUREimpure) 48 s2 |= STCpure; 49 if (tf.isnothrow) 50 s2 |= STCnothrow; 51 if (tf.isnogc) 52 s2 |= STCnogc; 53 StorageClass stc = 0; 54 StorageClass sa = s1 & s2; 55 StorageClass so = s1 | s2; 56 if (so & STCsystem) 57 stc |= STCsystem; 58 else if (sa & STCtrusted) 59 stc |= STCtrusted; 60 else if ((so & (STCtrusted | STCsafe)) == (STCtrusted | STCsafe)) 61 stc |= STCtrusted; 62 else if (sa & STCsafe) 63 stc |= STCsafe; 64 if (sa & STCpure) 65 stc |= STCpure; 66 if (sa & STCnothrow) 67 stc |= STCnothrow; 68 if (sa & STCnogc) 69 stc |= STCnogc; 70 if (so & STCdisable) 71 stc |= STCdisable; 72 return stc; 73 } 74 75 /******************************************* 76 * Check given aggregate actually has an identity opAssign or not. 77 * Params: 78 * ad = struct or class 79 * sc = current scope 80 * Returns: 81 * if found, returns FuncDeclaration of opAssign, otherwise null 82 */ 83 extern (C++) FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) 84 { 85 Dsymbol assign = search_function(ad, Id.assign); 86 if (assign) 87 { 88 /* check identity opAssign exists 89 */ 90 scope er = new NullExp(ad.loc, ad.type); // dummy rvalue 91 scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue 92 el.type = ad.type; 93 Expressions a; 94 a.setDim(1); 95 const errors = global.startGagging(); // Do not report errors, even if the 96 sc = sc.push(); 97 sc.tinst = null; 98 sc.minst = null; 99 100 a[0] = er; 101 auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, &a, 1); 102 if (!f) 103 { 104 a[0] = el; 105 f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, &a, 1); 106 } 107 108 sc = sc.pop(); 109 global.endGagging(errors); 110 if (f) 111 { 112 if (f.errors) 113 return null; 114 int varargs; 115 auto fparams = f.getParameters(&varargs); 116 if (fparams.dim >= 1) 117 { 118 auto fparam0 = Parameter.getNth(fparams, 0); 119 if (fparam0.type.toDsymbol(null) != ad) 120 f = null; 121 } 122 } 123 // BUGS: This detection mechanism cannot find some opAssign-s like follows: 124 // struct S { void opAssign(ref immutable S) const; } 125 return f; 126 } 127 return null; 128 } 129 130 /******************************************* 131 * We need an opAssign for the struct if 132 * it has a destructor or a postblit. 133 * We need to generate one if a user-specified one does not exist. 134 */ 135 private bool needOpAssign(StructDeclaration sd) 136 { 137 //printf("StructDeclaration::needOpAssign() %s\n", sd.toChars()); 138 if (sd.isUnionDeclaration()) 139 return false; 140 141 if (sd.hasIdentityAssign) 142 goto Lneed; // because has identity==elaborate opAssign 143 144 if (sd.dtor || sd.postblit) 145 goto Lneed; 146 /* If any of the fields need an opAssign, then we 147 * need it too. 148 */ 149 for (size_t i = 0; i < sd.fields.dim; i++) 150 { 151 VarDeclaration v = sd.fields[i]; 152 if (v.storage_class & STCref) 153 continue; 154 if (v.overlapped) // if field of a union 155 continue; // user must handle it themselves 156 Type tv = v.type.baseElemOf(); 157 if (tv.ty == Tstruct) 158 { 159 TypeStruct ts = cast(TypeStruct)tv; 160 if (ts.sym.isUnionDeclaration()) 161 continue; 162 if (needOpAssign(ts.sym)) 163 goto Lneed; 164 } 165 } 166 //printf("\tdontneed\n"); 167 return false; 168 Lneed: 169 //printf("\tneed\n"); 170 return true; 171 } 172 173 /****************************************** 174 * Build opAssign for struct. 175 * ref S opAssign(S s) { ... } 176 * 177 * Note that s will be constructed onto the stack, and probably 178 * copy-constructed in caller site. 179 * 180 * If S has copy copy construction and/or destructor, 181 * the body will make bit-wise object swap: 182 * S __swap = this; // bit copy 183 * this = s; // bit copy 184 * __swap.dtor(); 185 * Instead of running the destructor on s, run it on tmp instead. 186 * 187 * Otherwise, the body will make member-wise assignments: 188 * Then, the body is: 189 * this.field1 = s.field1; 190 * this.field2 = s.field2; 191 * ...; 192 */ 193 extern (C++) FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) 194 { 195 if (FuncDeclaration f = hasIdentityOpAssign(sd, sc)) 196 { 197 sd.hasIdentityAssign = true; 198 return f; 199 } 200 // Even if non-identity opAssign is defined, built-in identity opAssign 201 // will be defined. 202 if (!needOpAssign(sd)) 203 return null; 204 205 //printf("StructDeclaration::buildOpAssign() %s\n", sd.toChars()); 206 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; 207 Loc declLoc = sd.loc; 208 Loc loc = Loc(); // internal code should have no loc to prevent coverage 209 210 // One of our sub-field might have `@disable opAssign` so we need to 211 // check for it. 212 // In this event, it will be reflected by having `stc` (opAssign's 213 // storage class) include `STCdisabled`. 214 for (size_t i = 0; i < sd.fields.dim; i++) 215 { 216 VarDeclaration v = sd.fields[i]; 217 if (v.storage_class & STCref) 218 continue; 219 if (v.overlapped) 220 continue; 221 Type tv = v.type.baseElemOf(); 222 if (tv.ty != Tstruct) 223 continue; 224 StructDeclaration sdv = (cast(TypeStruct)tv).sym; 225 stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); 226 } 227 228 if (sd.dtor || sd.postblit) 229 { 230 if (!sd.type.isAssignable()) // Bugzilla 13044 231 return null; 232 stc = mergeFuncAttrs(stc, sd.dtor); 233 if (stc & STCsafe) 234 stc = (stc & ~STCsafe) | STCtrusted; 235 } 236 237 auto fparams = new Parameters(); 238 fparams.push(new Parameter(STCnodtor, sd.type, Id.p, null)); 239 auto tf = new TypeFunction(fparams, sd.handleType(), 0, LINKd, stc | STCref); 240 auto fop = new FuncDeclaration(declLoc, Loc(), Id.assign, stc, tf); 241 fop.storage_class |= STCinference; 242 fop.generated = true; 243 Expression e = null; 244 if (stc & STCdisable) 245 { 246 } 247 else if (sd.dtor || sd.postblit) 248 { 249 /* Do swap this and rhs. 250 * __swap = this; this = s; __swap.dtor(); 251 */ 252 //printf("\tswap copy\n"); 253 Identifier idtmp = Identifier.generateId("__swap"); 254 VarDeclaration tmp = null; 255 AssignExp ec = null; 256 if (sd.dtor) 257 { 258 tmp = new VarDeclaration(loc, sd.type, idtmp, new VoidInitializer(loc)); 259 tmp.storage_class |= STCnodtor | STCtemp | STCctfe; 260 e = new DeclarationExp(loc, tmp); 261 ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc)); 262 e = Expression.combine(e, ec); 263 } 264 ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p)); 265 e = Expression.combine(e, ec); 266 if (sd.dtor) 267 { 268 /* Instead of running the destructor on s, run it 269 * on tmp. This avoids needing to copy tmp back in to s. 270 */ 271 Expression ec2 = new DotVarExp(loc, new VarExp(loc, tmp), sd.dtor, false); 272 ec2 = new CallExp(loc, ec2); 273 e = Expression.combine(e, ec2); 274 } 275 } 276 else 277 { 278 /* Do memberwise copy. 279 * 280 * If sd is a nested struct, its vthis field assignment is: 281 * 1. If it's nested in a class, it's a rebind of class reference. 282 * 2. If it's nested in a function or struct, it's an update of void*. 283 * In both cases, it will change the parent context. 284 */ 285 //printf("\tmemberwise copy\n"); 286 for (size_t i = 0; i < sd.fields.dim; i++) 287 { 288 VarDeclaration v = sd.fields[i]; 289 // this.v = s.v; 290 auto ec = new AssignExp(loc, 291 new DotVarExp(loc, new ThisExp(loc), v), 292 new DotVarExp(loc, new IdentifierExp(loc, Id.p), v)); 293 e = Expression.combine(e, ec); 294 } 295 } 296 if (e) 297 { 298 Statement s1 = new ExpStatement(loc, e); 299 /* Add: 300 * return this; 301 */ 302 e = new ThisExp(loc); 303 Statement s2 = new ReturnStatement(loc, e); 304 fop.fbody = new CompoundStatement(loc, s1, s2); 305 tf.isreturn = true; 306 } 307 sd.members.push(fop); 308 fop.addMember(sc, sd); 309 sd.hasIdentityAssign = true; // temporary mark identity assignable 310 uint errors = global.startGagging(); // Do not report errors, even if the 311 Scope* sc2 = sc.push(); 312 sc2.stc = 0; 313 sc2.linkage = LINKd; 314 fop.semantic(sc2); 315 fop.semantic2(sc2); 316 // Bugzilla 15044: fop.semantic3 isn't run here for lazy forward reference resolution. 317 318 sc2.pop(); 319 if (global.endGagging(errors)) // if errors happened 320 { 321 // Disable generated opAssign, because some members forbid identity assignment. 322 fop.storage_class |= STCdisable; 323 fop.fbody = null; // remove fbody which contains the error 324 } 325 326 //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd.toChars(), (fop.storage_class & STCdisable) != 0); 327 return fop; 328 } 329 330 /******************************************* 331 * We need an opEquals for the struct if 332 * any fields has an opEquals. 333 * Generate one if a user-specified one does not exist. 334 */ 335 extern (C++) bool needOpEquals(StructDeclaration sd) 336 { 337 //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars()); 338 if (sd.isUnionDeclaration()) 339 goto Ldontneed; 340 if (sd.hasIdentityEquals) 341 goto Lneed; 342 /* If any of the fields has an opEquals, then we 343 * need it too. 344 */ 345 for (size_t i = 0; i < sd.fields.dim; i++) 346 { 347 VarDeclaration v = sd.fields[i]; 348 if (v.storage_class & STCref) 349 continue; 350 if (v.overlapped) 351 continue; 352 Type tv = v.type.toBasetype(); 353 auto tvbase = tv.baseElemOf(); 354 if (tvbase.ty == Tstruct) 355 { 356 TypeStruct ts = cast(TypeStruct)tvbase; 357 if (ts.sym.isUnionDeclaration()) 358 continue; 359 if (needOpEquals(ts.sym)) 360 goto Lneed; 361 if (ts.sym.aliasthis) // Bugzilla 14806 362 goto Lneed; 363 } 364 if (tv.isfloating()) 365 { 366 // This is necessray for: 367 // 1. comparison of +0.0 and -0.0 should be true. 368 // 2. comparison of NANs should be false always. 369 goto Lneed; 370 } 371 if (tv.ty == Tarray) 372 goto Lneed; 373 if (tv.ty == Taarray) 374 goto Lneed; 375 if (tv.ty == Tclass) 376 goto Lneed; 377 } 378 Ldontneed: 379 //printf("\tdontneed\n"); 380 return false; 381 Lneed: 382 //printf("\tneed\n"); 383 return true; 384 } 385 386 /******************************************* 387 * Check given aggregate actually has an identity opEquals or not. 388 */ 389 extern (C++) FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) 390 { 391 Dsymbol eq = search_function(ad, Id.eq); 392 if (eq) 393 { 394 /* check identity opEquals exists 395 */ 396 scope er = new NullExp(ad.loc, null); // dummy rvalue 397 scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue 398 Expressions a; 399 a.setDim(1); 400 foreach (i; 0 .. 5) 401 { 402 Type tthis = null; // dead-store to prevent spurious warning 403 final switch (i) 404 { 405 case 0: tthis = ad.type; break; 406 case 1: tthis = ad.type.constOf(); break; 407 case 2: tthis = ad.type.immutableOf(); break; 408 case 3: tthis = ad.type.sharedOf(); break; 409 case 4: tthis = ad.type.sharedConstOf(); break; 410 } 411 FuncDeclaration f = null; 412 const errors = global.startGagging(); // Do not report errors, even if the 413 sc = sc.push(); 414 sc.tinst = null; 415 sc.minst = null; 416 foreach (j; 0 .. 2) 417 { 418 a[0] = (j == 0 ? er : el); 419 a[0].type = tthis; 420 f = resolveFuncCall(ad.loc, sc, eq, null, tthis, &a, 1); 421 if (f) 422 break; 423 } 424 sc = sc.pop(); 425 global.endGagging(errors); 426 if (f) 427 { 428 if (f.errors) 429 return null; 430 return f; 431 } 432 } 433 } 434 return null; 435 } 436 437 /****************************************** 438 * Build opEquals for struct. 439 * const bool opEquals(const S s) { ... } 440 * 441 * By fixing bugzilla 3789, opEquals is changed to be never implicitly generated. 442 * Now, struct objects comparison s1 == s2 is translated to: 443 * s1.tupleof == s2.tupleof 444 * to calculate structural equality. See EqualExp.op_overload. 445 */ 446 extern (C++) FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc) 447 { 448 if (hasIdentityOpEquals(sd, sc)) 449 { 450 sd.hasIdentityEquals = true; 451 } 452 return null; 453 } 454 455 /****************************************** 456 * Build __xopEquals for TypeInfo_Struct 457 * static bool __xopEquals(ref const S p, ref const S q) 458 * { 459 * return p == q; 460 * } 461 * 462 * This is called by TypeInfo.equals(p1, p2). If the struct does not support 463 * const objects comparison, it will throw "not implemented" Error in runtime. 464 */ 465 extern (C++) FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) 466 { 467 if (!needOpEquals(sd)) 468 return null; // bitwise comparison would work 469 470 //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars()); 471 if (Dsymbol eq = search_function(sd, Id.eq)) 472 { 473 if (FuncDeclaration fd = eq.isFuncDeclaration()) 474 { 475 TypeFunction tfeqptr; 476 { 477 Scope scx; 478 /* const bool opEquals(ref const S s); 479 */ 480 auto parameters = new Parameters(); 481 parameters.push(new Parameter(STCref | STCconst, sd.type, null, null)); 482 tfeqptr = new TypeFunction(parameters, Type.tbool, 0, LINKd); 483 tfeqptr.mod = MODconst; 484 tfeqptr = cast(TypeFunction)tfeqptr.semantic(Loc(), &scx); 485 } 486 fd = fd.overloadExactMatch(tfeqptr); 487 if (fd) 488 return fd; 489 } 490 } 491 if (!sd.xerreq) 492 { 493 // object._xopEquals 494 Identifier id = Identifier.idPool("_xopEquals"); 495 Expression e = new IdentifierExp(sd.loc, Id.empty); 496 e = new DotIdExp(sd.loc, e, Id.object); 497 e = new DotIdExp(sd.loc, e, id); 498 e = e.semantic(sc); 499 Dsymbol s = getDsymbol(e); 500 assert(s); 501 sd.xerreq = s.isFuncDeclaration(); 502 } 503 Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly 504 Loc loc = Loc(); // loc is unnecessary so errors are gagged 505 auto parameters = new Parameters(); 506 parameters.push(new Parameter(STCref | STCconst, sd.type, Id.p, null)); 507 parameters.push(new Parameter(STCref | STCconst, sd.type, Id.q, null)); 508 auto tf = new TypeFunction(parameters, Type.tbool, 0, LINKd); 509 Identifier id = Id.xopEquals; 510 auto fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); 511 fop.generated = true; 512 Expression e1 = new IdentifierExp(loc, Id.p); 513 Expression e2 = new IdentifierExp(loc, Id.q); 514 Expression e = new EqualExp(TOKequal, loc, e1, e2); 515 fop.fbody = new ReturnStatement(loc, e); 516 uint errors = global.startGagging(); // Do not report errors 517 Scope* sc2 = sc.push(); 518 sc2.stc = 0; 519 sc2.linkage = LINKd; 520 fop.semantic(sc2); 521 fop.semantic2(sc2); 522 sc2.pop(); 523 if (global.endGagging(errors)) // if errors happened 524 fop = sd.xerreq; 525 return fop; 526 } 527 528 /****************************************** 529 * Build __xopCmp for TypeInfo_Struct 530 * static bool __xopCmp(ref const S p, ref const S q) 531 * { 532 * return p.opCmp(q); 533 * } 534 * 535 * This is called by TypeInfo.compare(p1, p2). If the struct does not support 536 * const objects comparison, it will throw "not implemented" Error in runtime. 537 */ 538 extern (C++) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) 539 { 540 //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); 541 if (Dsymbol cmp = search_function(sd, Id.cmp)) 542 { 543 if (FuncDeclaration fd = cmp.isFuncDeclaration()) 544 { 545 TypeFunction tfcmpptr; 546 { 547 Scope scx; 548 /* const int opCmp(ref const S s); 549 */ 550 auto parameters = new Parameters(); 551 parameters.push(new Parameter(STCref | STCconst, sd.type, null, null)); 552 tfcmpptr = new TypeFunction(parameters, Type.tint32, 0, LINKd); 553 tfcmpptr.mod = MODconst; 554 tfcmpptr = cast(TypeFunction)tfcmpptr.semantic(Loc(), &scx); 555 } 556 fd = fd.overloadExactMatch(tfcmpptr); 557 if (fd) 558 return fd; 559 } 560 } 561 else 562 { 563 version (none) // FIXME: doesn't work for recursive alias this 564 { 565 /* Check opCmp member exists. 566 * Consider 'alias this', but except opDispatch. 567 */ 568 Expression e = new DsymbolExp(sd.loc, sd); 569 e = new DotIdExp(sd.loc, e, Id.cmp); 570 Scope* sc2 = sc.push(); 571 e = e.trySemantic(sc2); 572 sc2.pop(); 573 if (e) 574 { 575 Dsymbol s = null; 576 switch (e.op) 577 { 578 case TOKoverloadset: 579 s = (cast(OverExp)e).vars; 580 break; 581 case TOKscope: 582 s = (cast(ScopeExp)e).sds; 583 break; 584 case TOKvar: 585 s = (cast(VarExp)e).var; 586 break; 587 default: 588 break; 589 } 590 if (!s || s.ident != Id.cmp) 591 e = null; // there's no valid member 'opCmp' 592 } 593 if (!e) 594 return null; // bitwise comparison would work 595 /* Essentially, a struct which does not define opCmp is not comparable. 596 * At this time, typeid(S).compare might be correct that throwing "not implement" Error. 597 * But implementing it would break existing code, such as: 598 * 599 * struct S { int value; } // no opCmp 600 * int[S] aa; // Currently AA key uses bitwise comparison 601 * // (It's default behavior of TypeInfo_Strust.compare). 602 * 603 * Not sure we should fix this inconsistency, so just keep current behavior. 604 */ 605 } 606 else 607 { 608 return null; 609 } 610 } 611 if (!sd.xerrcmp) 612 { 613 // object._xopCmp 614 Identifier id = Identifier.idPool("_xopCmp"); 615 Expression e = new IdentifierExp(sd.loc, Id.empty); 616 e = new DotIdExp(sd.loc, e, Id.object); 617 e = new DotIdExp(sd.loc, e, id); 618 e = e.semantic(sc); 619 Dsymbol s = getDsymbol(e); 620 assert(s); 621 sd.xerrcmp = s.isFuncDeclaration(); 622 } 623 Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly 624 Loc loc = Loc(); // loc is unnecessary so errors are gagged 625 auto parameters = new Parameters(); 626 parameters.push(new Parameter(STCref | STCconst, sd.type, Id.p, null)); 627 parameters.push(new Parameter(STCref | STCconst, sd.type, Id.q, null)); 628 auto tf = new TypeFunction(parameters, Type.tint32, 0, LINKd); 629 Identifier id = Id.xopCmp; 630 auto fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); 631 fop.generated = true; 632 Expression e1 = new IdentifierExp(loc, Id.p); 633 Expression e2 = new IdentifierExp(loc, Id.q); 634 Expression e = new CallExp(loc, new DotIdExp(loc, e2, Id.cmp), e1); 635 fop.fbody = new ReturnStatement(loc, e); 636 uint errors = global.startGagging(); // Do not report errors 637 Scope* sc2 = sc.push(); 638 sc2.stc = 0; 639 sc2.linkage = LINKd; 640 fop.semantic(sc2); 641 fop.semantic2(sc2); 642 sc2.pop(); 643 if (global.endGagging(errors)) // if errors happened 644 fop = sd.xerrcmp; 645 return fop; 646 } 647 648 /******************************************* 649 * We need a toHash for the struct if 650 * any fields has a toHash. 651 * Generate one if a user-specified one does not exist. 652 */ 653 private bool needToHash(StructDeclaration sd) 654 { 655 //printf("StructDeclaration::needToHash() %s\n", sd.toChars()); 656 if (sd.isUnionDeclaration()) 657 goto Ldontneed; 658 if (sd.xhash) 659 goto Lneed; 660 661 /* If any of the fields has an opEquals, then we 662 * need it too. 663 */ 664 for (size_t i = 0; i < sd.fields.dim; i++) 665 { 666 VarDeclaration v = sd.fields[i]; 667 if (v.storage_class & STCref) 668 continue; 669 if (v.overlapped) 670 continue; 671 Type tv = v.type.toBasetype(); 672 auto tvbase = tv.baseElemOf(); 673 if (tvbase.ty == Tstruct) 674 { 675 TypeStruct ts = cast(TypeStruct)tvbase; 676 if (ts.sym.isUnionDeclaration()) 677 continue; 678 if (needToHash(ts.sym)) 679 goto Lneed; 680 if (ts.sym.aliasthis) // Bugzilla 14948 681 goto Lneed; 682 } 683 if (tv.isfloating()) 684 { 685 // This is necessray for: 686 // 1. comparison of +0.0 and -0.0 should be true. 687 goto Lneed; 688 } 689 if (tv.ty == Tarray) 690 goto Lneed; 691 if (tv.ty == Taarray) 692 goto Lneed; 693 if (tv.ty == Tclass) 694 goto Lneed; 695 } 696 Ldontneed: 697 //printf("\tdontneed\n"); 698 return false; 699 Lneed: 700 //printf("\tneed\n"); 701 return true; 702 } 703 704 /****************************************** 705 * Build __xtoHash for non-bitwise hashing 706 * static hash_t xtoHash(ref const S p) nothrow @trusted; 707 */ 708 extern (C++) FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) 709 { 710 if (Dsymbol s = search_function(sd, Id.tohash)) 711 { 712 static __gshared TypeFunction tftohash; 713 if (!tftohash) 714 { 715 tftohash = new TypeFunction(null, Type.thash_t, 0, LINKd); 716 tftohash.mod = MODconst; 717 tftohash = cast(TypeFunction)tftohash.merge(); 718 } 719 if (FuncDeclaration fd = s.isFuncDeclaration()) 720 { 721 fd = fd.overloadExactMatch(tftohash); 722 if (fd) 723 return fd; 724 } 725 } 726 if (!needToHash(sd)) 727 return null; 728 729 //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); 730 Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly 731 Loc loc = Loc(); // internal code should have no loc to prevent coverage 732 auto parameters = new Parameters(); 733 parameters.push(new Parameter(STCref | STCconst, sd.type, Id.p, null)); 734 auto tf = new TypeFunction(parameters, Type.thash_t, 0, LINKd, STCnothrow | STCtrusted); 735 Identifier id = Id.xtoHash; 736 auto fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); 737 fop.generated = true; 738 739 /* Do memberwise hashing. 740 * 741 * If sd is a nested struct, and if it's nested in a class, the calculated 742 * hash value will also contain the result of parent class's toHash(). 743 */ 744 const(char)* code = 745 "size_t h = 0;" ~ 746 "foreach (i, T; typeof(p.tupleof))" ~ 747 " h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~ 748 "return h;"; 749 fop.fbody = new CompileStatement(loc, new StringExp(loc, cast(char*)code)); 750 Scope* sc2 = sc.push(); 751 sc2.stc = 0; 752 sc2.linkage = LINKd; 753 fop.semantic(sc2); 754 fop.semantic2(sc2); 755 sc2.pop(); 756 757 //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars()); 758 return fop; 759 } 760 761 /***************************************** 762 * Create inclusive postblit for struct by aggregating 763 * all the postblits in postblits[] with the postblits for 764 * all the members. 765 * Note the close similarity with AggregateDeclaration::buildDtor(), 766 * and the ordering changes (runs forward instead of backwards). 767 */ 768 extern (C++) FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) 769 { 770 //printf("StructDeclaration::buildPostBlit() %s\n", sd.toChars()); 771 if (sd.isUnionDeclaration()) 772 return null; 773 774 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; 775 Loc declLoc = sd.postblits.dim ? sd.postblits[0].loc : sd.loc; 776 Loc loc = Loc(); // internal code should have no loc to prevent coverage 777 778 for (size_t i = 0; i < sd.postblits.dim; i++) 779 { 780 stc |= sd.postblits[i].storage_class & STCdisable; 781 } 782 783 Statements* a = null; 784 for (size_t i = 0; i < sd.fields.dim && !(stc & STCdisable); i++) 785 { 786 auto v = sd.fields[i]; 787 if (v.storage_class & STCref) 788 continue; 789 if (v.overlapped) 790 continue; 791 Type tv = v.type.baseElemOf(); 792 if (tv.ty != Tstruct) 793 continue; 794 auto sdv = (cast(TypeStruct)tv).sym; 795 if (!sdv.postblit) 796 continue; 797 assert(!sdv.isUnionDeclaration()); 798 sdv.postblit.functionSemantic(); 799 800 stc = mergeFuncAttrs(stc, sdv.postblit); 801 stc = mergeFuncAttrs(stc, sdv.dtor); 802 if (stc & STCdisable) 803 { 804 a = null; 805 break; 806 } 807 if (!a) 808 a = new Statements(); 809 810 Expression ex; 811 tv = v.type.toBasetype(); 812 if (tv.ty == Tstruct) 813 { 814 // this.v.__xpostblit() 815 816 ex = new ThisExp(loc); 817 ex = new DotVarExp(loc, ex, v); 818 819 // This is a hack so we can call postblits on const/immutable objects. 820 ex = new AddrExp(loc, ex); 821 ex = new CastExp(loc, ex, v.type.mutableOf().pointerTo()); 822 ex = new PtrExp(loc, ex); 823 if (stc & STCsafe) 824 stc = (stc & ~STCsafe) | STCtrusted; 825 826 ex = new DotVarExp(loc, ex, sdv.postblit, false); 827 ex = new CallExp(loc, ex); 828 } 829 else 830 { 831 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n]) 832 833 uinteger_t n = 1; 834 while (tv.ty == Tsarray) 835 { 836 n *= (cast(TypeSArray)tv).dim.toUInteger(); 837 tv = tv.nextOf().toBasetype(); 838 } 839 if (n == 0) 840 continue; 841 842 ex = new ThisExp(loc); 843 ex = new DotVarExp(loc, ex, v); 844 845 // This is a hack so we can call postblits on const/immutable objects. 846 ex = new DotIdExp(loc, ex, Id.ptr); 847 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 848 if (stc & STCsafe) 849 stc = (stc & ~STCsafe) | STCtrusted; 850 851 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 852 new IntegerExp(loc, n, Type.tsize_t)); 853 // Prevent redundant bounds check 854 (cast(SliceExp)ex).upperIsInBounds = true; 855 (cast(SliceExp)ex).lowerIsLessThanUpper = true; 856 ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayPostblit), ex); 857 } 858 a.push(new ExpStatement(loc, ex)); // combine in forward order 859 860 /* Bugzilla 10972: When the following field postblit calls fail, 861 * this field should be destructed for Exception Safety. 862 */ 863 if (!sdv.dtor) 864 continue; 865 sdv.dtor.functionSemantic(); 866 867 tv = v.type.toBasetype(); 868 if (tv.ty == Tstruct) 869 { 870 // this.v.__xdtor() 871 872 ex = new ThisExp(loc); 873 ex = new DotVarExp(loc, ex, v); 874 875 // This is a hack so we can call destructors on const/immutable objects. 876 ex = new AddrExp(loc, ex); 877 ex = new CastExp(loc, ex, v.type.mutableOf().pointerTo()); 878 ex = new PtrExp(loc, ex); 879 if (stc & STCsafe) 880 stc = (stc & ~STCsafe) | STCtrusted; 881 882 ex = new DotVarExp(loc, ex, sdv.dtor, false); 883 ex = new CallExp(loc, ex); 884 } 885 else 886 { 887 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) 888 889 uinteger_t n = 1; 890 while (tv.ty == Tsarray) 891 { 892 n *= (cast(TypeSArray)tv).dim.toUInteger(); 893 tv = tv.nextOf().toBasetype(); 894 } 895 //if (n == 0) 896 // continue; 897 898 ex = new ThisExp(loc); 899 ex = new DotVarExp(loc, ex, v); 900 901 // This is a hack so we can call destructors on const/immutable objects. 902 ex = new DotIdExp(loc, ex, Id.ptr); 903 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 904 if (stc & STCsafe) 905 stc = (stc & ~STCsafe) | STCtrusted; 906 907 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 908 new IntegerExp(loc, n, Type.tsize_t)); 909 // Prevent redundant bounds check 910 (cast(SliceExp)ex).upperIsInBounds = true; 911 (cast(SliceExp)ex).lowerIsLessThanUpper = true; 912 913 ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), ex); 914 } 915 a.push(new OnScopeStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex))); 916 } 917 918 /* Build our own "postblit" which executes a 919 */ 920 if (a || (stc & STCdisable)) 921 { 922 //printf("Building __fieldPostBlit()\n"); 923 auto dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id.__fieldPostblit); 924 dd.storage_class |= STCinference; 925 dd.fbody = a ? new CompoundStatement(loc, a) : null; 926 sd.postblits.shift(dd); 927 sd.members.push(dd); 928 dd.semantic(sc); 929 } 930 931 FuncDeclaration xpostblit = null; 932 switch (sd.postblits.dim) 933 { 934 case 0: 935 break; 936 937 case 1: 938 xpostblit = sd.postblits[0]; 939 break; 940 941 default: 942 Expression e = null; 943 stc = STCsafe | STCnothrow | STCpure | STCnogc; 944 for (size_t i = 0; i < sd.postblits.dim; i++) 945 { 946 auto fd = sd.postblits[i]; 947 stc = mergeFuncAttrs(stc, fd); 948 if (stc & STCdisable) 949 { 950 e = null; 951 break; 952 } 953 Expression ex = new ThisExp(loc); 954 ex = new DotVarExp(loc, ex, fd, false); 955 ex = new CallExp(loc, ex); 956 e = Expression.combine(e, ex); 957 } 958 auto dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id.__aggrPostblit); 959 dd.generated = true; 960 dd.storage_class |= STCinference; 961 dd.fbody = new ExpStatement(loc, e); 962 sd.members.push(dd); 963 dd.semantic(sc); 964 xpostblit = dd; 965 break; 966 } 967 968 // Add an __xpostblit alias to make the inclusive postblit accessible 969 if (xpostblit) 970 { 971 auto _alias = new AliasDeclaration(Loc(), Id.__xpostblit, xpostblit); 972 _alias.semantic(sc); 973 sd.members.push(_alias); 974 _alias.addMember(sc, sd); // add to symbol table 975 } 976 return xpostblit; 977 } 978 979 /***************************************** 980 * Create inclusive destructor for struct/class by aggregating 981 * all the destructors in dtors[] with the destructors for 982 * all the members. 983 * Note the close similarity with StructDeclaration::buildPostBlit(), 984 * and the ordering changes (runs backward instead of forwards). 985 */ 986 extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) 987 { 988 //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars()); 989 if (ad.isUnionDeclaration()) 990 return null; 991 992 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; 993 Loc declLoc = ad.dtors.dim ? ad.dtors[0].loc : ad.loc; 994 Loc loc = Loc(); // internal code should have no loc to prevent coverage 995 996 Expression e = null; 997 for (size_t i = 0; i < ad.fields.dim; i++) 998 { 999 auto v = ad.fields[i]; 1000 if (v.storage_class & STCref) 1001 continue; 1002 if (v.overlapped) 1003 continue; 1004 auto tv = v.type.baseElemOf(); 1005 if (tv.ty != Tstruct) 1006 continue; 1007 auto sdv = (cast(TypeStruct)tv).sym; 1008 if (!sdv.dtor) 1009 continue; 1010 sdv.dtor.functionSemantic(); 1011 1012 stc = mergeFuncAttrs(stc, sdv.dtor); 1013 if (stc & STCdisable) 1014 { 1015 e = null; 1016 break; 1017 } 1018 1019 Expression ex; 1020 tv = v.type.toBasetype(); 1021 if (tv.ty == Tstruct) 1022 { 1023 // this.v.__xdtor() 1024 1025 ex = new ThisExp(loc); 1026 ex = new DotVarExp(loc, ex, v); 1027 1028 // This is a hack so we can call destructors on const/immutable objects. 1029 ex = new AddrExp(loc, ex); 1030 ex = new CastExp(loc, ex, v.type.mutableOf().pointerTo()); 1031 ex = new PtrExp(loc, ex); 1032 if (stc & STCsafe) 1033 stc = (stc & ~STCsafe) | STCtrusted; 1034 1035 ex = new DotVarExp(loc, ex, sdv.dtor, false); 1036 ex = new CallExp(loc, ex); 1037 } 1038 else 1039 { 1040 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) 1041 1042 uinteger_t n = 1; 1043 while (tv.ty == Tsarray) 1044 { 1045 n *= (cast(TypeSArray)tv).dim.toUInteger(); 1046 tv = tv.nextOf().toBasetype(); 1047 } 1048 if (n == 0) 1049 continue; 1050 1051 ex = new ThisExp(loc); 1052 ex = new DotVarExp(loc, ex, v); 1053 1054 // This is a hack so we can call destructors on const/immutable objects. 1055 ex = new DotIdExp(loc, ex, Id.ptr); 1056 ex = new CastExp(loc, ex, sdv.type.pointerTo()); 1057 if (stc & STCsafe) 1058 stc = (stc & ~STCsafe) | STCtrusted; 1059 1060 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t), 1061 new IntegerExp(loc, n, Type.tsize_t)); 1062 // Prevent redundant bounds check 1063 (cast(SliceExp)ex).upperIsInBounds = true; 1064 (cast(SliceExp)ex).lowerIsLessThanUpper = true; 1065 1066 ex = new CallExp(loc, new IdentifierExp(loc, Id._ArrayDtor), ex); 1067 } 1068 e = Expression.combine(ex, e); // combine in reverse order 1069 } 1070 1071 /* Build our own "destructor" which executes e 1072 */ 1073 if (e || (stc & STCdisable)) 1074 { 1075 //printf("Building __fieldDtor()\n"); 1076 auto dd = new DtorDeclaration(declLoc, Loc(), stc, Id.__fieldDtor); 1077 dd.storage_class |= STCinference; 1078 dd.fbody = new ExpStatement(loc, e); 1079 ad.dtors.shift(dd); 1080 ad.members.push(dd); 1081 dd.semantic(sc); 1082 } 1083 1084 FuncDeclaration xdtor = null; 1085 switch (ad.dtors.dim) 1086 { 1087 case 0: 1088 break; 1089 1090 case 1: 1091 xdtor = ad.dtors[0]; 1092 break; 1093 1094 default: 1095 e = null; 1096 stc = STCsafe | STCnothrow | STCpure | STCnogc; 1097 for (size_t i = 0; i < ad.dtors.dim; i++) 1098 { 1099 FuncDeclaration fd = ad.dtors[i]; 1100 stc = mergeFuncAttrs(stc, fd); 1101 if (stc & STCdisable) 1102 { 1103 e = null; 1104 break; 1105 } 1106 Expression ex = new ThisExp(loc); 1107 ex = new DotVarExp(loc, ex, fd, false); 1108 ex = new CallExp(loc, ex); 1109 e = Expression.combine(ex, e); 1110 } 1111 auto dd = new DtorDeclaration(declLoc, Loc(), stc, Id.__aggrDtor); 1112 dd.storage_class |= STCinference; 1113 dd.fbody = new ExpStatement(loc, e); 1114 ad.members.push(dd); 1115 dd.semantic(sc); 1116 xdtor = dd; 1117 break; 1118 } 1119 1120 // Add an __xdtor alias to make the inclusive dtor accessible 1121 if (xdtor) 1122 { 1123 auto _alias = new AliasDeclaration(Loc(), Id.__xdtor, xdtor); 1124 _alias.semantic(sc); 1125 ad.members.push(_alias); 1126 _alias.addMember(sc, ad); // add to symbol table 1127 } 1128 return xdtor; 1129 } 1130 1131 /****************************************** 1132 * Create inclusive invariant for struct/class by aggregating 1133 * all the invariants in invs[]. 1134 * void __invariant() const [pure nothrow @trusted] 1135 * { 1136 * invs[0](), invs[1](), ...; 1137 * } 1138 */ 1139 extern (C++) FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) 1140 { 1141 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; 1142 Loc declLoc = ad.loc; 1143 Loc loc = Loc(); // internal code should have no loc to prevent coverage 1144 switch (ad.invs.dim) 1145 { 1146 case 0: 1147 return null; 1148 case 1: 1149 // Don't return invs[0] so it has uniquely generated name. 1150 /* fall through */ 1151 default: 1152 Expression e = null; 1153 StorageClass stcx = 0; 1154 for (size_t i = 0; i < ad.invs.dim; i++) 1155 { 1156 stc = mergeFuncAttrs(stc, ad.invs[i]); 1157 if (stc & STCdisable) 1158 { 1159 // What should do? 1160 } 1161 StorageClass stcy = (ad.invs[i].storage_class & STCsynchronized) | (ad.invs[i].type.mod & MODshared ? STCshared : 0); 1162 if (i == 0) 1163 stcx = stcy; 1164 else if (stcx ^ stcy) 1165 { 1166 version (all) 1167 { 1168 // currently rejects 1169 ad.error(ad.invs[i].loc, "mixing invariants with shared/synchronized differene is not supported"); 1170 e = null; 1171 break; 1172 } 1173 } 1174 e = Expression.combine(e, new CallExp(loc, new VarExp(loc, ad.invs[i], false))); 1175 } 1176 auto inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id.classInvariant, new ExpStatement(loc, e)); 1177 ad.members.push(inv); 1178 inv.semantic(sc); 1179 return inv; 1180 } 1181 }