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