Merge dmd upstream 6243fa6d2
[official-gcc.git] / gcc / d / dmd / clone.c
blob4384dc8064f9d755ac03b9897b257ca97d47fb46
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2018 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 */
11 #include "root/dsystem.h"
12 #include "root/root.h"
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"
26 Expression *semantic(Expression *e, Scope *sc);
28 /*******************************************
29 * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
31 StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f)
33 if (!f)
34 return s1;
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;
51 StorageClass stc = 0;
52 StorageClass sa = s1 & s2;
53 StorageClass so = s1 | s2;
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;
64 if (sa & STCpure)
65 stc |= STCpure;
67 if (sa & STCnothrow)
68 stc |= STCnothrow;
70 if (sa & STCnogc)
71 stc |= STCnogc;
73 if (so & STCdisable)
74 stc |= STCdisable;
76 return stc;
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
87 FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc)
89 Dsymbol *assign = search_function(ad, Id::assign);
90 if (assign)
92 /* check identity opAssign exists
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);
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;
105 a[0] = er.exp();
106 FuncDeclaration *f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, &a, 1);
107 if (!f)
109 a[0] = el.exp();
110 f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, &a, 1);
113 sc = sc->pop();
114 global.endGagging(errors);
116 if (f)
118 if (f->errors)
119 return NULL;
120 int varargs;
121 Parameters *fparams = f->getParameters(&varargs);
122 if (fparams->dim >= 1)
124 Parameter *fparam0 = Parameter::getNth(fparams, 0);
125 if (fparam0->type->toDsymbol(NULL) != ad)
126 f = NULL;
129 // BUGS: This detection mechanism cannot find some opAssign-s like follows:
130 // struct S { void opAssign(ref immutable S) const; }
131 return f;
133 return NULL;
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.
141 bool needOpAssign(StructDeclaration *sd)
143 //printf("StructDeclaration::needOpAssign() %s\n", sd->toChars());
144 if (sd->isUnionDeclaration())
145 return false;
147 if (sd->hasIdentityAssign)
148 goto Lneed; // because has identity==elaborate opAssign
150 if (sd->dtor || sd->postblit)
151 goto Lneed;
153 /* If any of the fields need an opAssign, then we
154 * need it too.
156 for (size_t i = 0; i < sd->fields.dim; i++)
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)
166 TypeStruct *ts = (TypeStruct *)tv;
167 if (ts->sym->isUnionDeclaration())
168 continue;
169 if (needOpAssign(ts->sym))
170 goto Lneed;
173 //printf("\tdontneed\n");
174 return false;
176 Lneed:
177 //printf("\tneed\n");
178 return true;
181 /******************************************
182 * Build opAssign for struct.
183 * ref S opAssign(S s) { ... }
185 * Note that s will be constructed onto the stack, and probably
186 * copy-constructed in caller site.
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.
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 * ...;
201 FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc)
203 if (FuncDeclaration *f = hasIdentityOpAssign(sd, sc))
205 sd->hasIdentityAssign = true;
206 return f;
208 // Even if non-identity opAssign is defined, built-in identity opAssign
209 // will be defined.
211 if (!needOpAssign(sd))
212 return NULL;
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
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++)
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;
234 StructDeclaration *sdv = ((TypeStruct *)tv)->sym;
235 stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc));
238 if (sd->dtor || sd->postblit)
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;
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);
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)
258 else if (sd->dtor || sd->postblit)
260 /* Do swap this and rhs.
261 * __swap = this; this = s; __swap.dtor();
263 //printf("\tswap copy\n");
264 Identifier *idtmp = Identifier::generateId("__swap");
265 VarDeclaration *tmp = NULL;
266 AssignExp *ec = NULL;
267 if (sd->dtor)
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);
275 ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p));
276 e = Expression::combine(e, ec);
277 if (sd->dtor)
279 /* Instead of running the destructor on s, run it
280 * on tmp. This avoids needing to copy tmp back in to s.
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);
287 else
289 /* Do memberwise copy.
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.
296 //printf("\tmemberwise copy\n");
297 for (size_t i = 0; i < sd->fields.dim; i++)
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);
307 if (e)
309 Statement *s1 = new ExpStatement(loc, e);
311 /* Add:
312 * return this;
314 e = new ThisExp(loc);
315 Statement *s2 = new ReturnStatement(loc, e);
317 fop->fbody = new CompoundStatement(loc, s1, s2);
318 tf->isreturn = true;
321 sd->members->push(fop);
322 fop->addMember(sc, sd);
323 sd->hasIdentityAssign = true; // temporary mark identity assignable
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;
330 fop->semantic(sc2);
331 fop->semantic2(sc2);
332 // Bugzilla 15044: fop->semantic3 isn't run here for lazy forward reference resolution.
334 sc2->pop();
335 if (global.endGagging(errors)) // if errors happened
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
342 //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd->toChars(), (fop->storage_class & STCdisable) != 0);
344 return fop;
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.
352 bool needOpEquals(StructDeclaration *sd)
354 //printf("StructDeclaration::needOpEquals() %s\n", sd->toChars());
355 if (sd->isUnionDeclaration())
356 goto Ldontneed;
358 if (sd->hasIdentityEquals)
359 goto Lneed;
361 /* If any of the fields has an opEquals, then we
362 * need it too.
364 for (size_t i = 0; i < sd->fields.dim; i++)
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)
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;
383 if (tv->isfloating())
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;
390 if (tv->ty == Tarray)
391 goto Lneed;
392 if (tv->ty == Taarray)
393 goto Lneed;
394 if (tv->ty == Tclass)
395 goto Lneed;
397 Ldontneed:
398 //printf("\tdontneed\n");
399 return false;
401 Lneed:
402 //printf("\tneed\n");
403 return true;
406 /*******************************************
407 * Check given aggregate actually has an identity opEquals or not.
409 FuncDeclaration *hasIdentityOpEquals(AggregateDeclaration *ad, Scope *sc)
411 Dsymbol *eq = search_function(ad, Id::eq);
412 if (eq)
414 /* check identity opEquals exists
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++)
422 Type *tthis = NULL; // dead-store to prevent spurious warning
423 switch (i)
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);
432 FuncDeclaration *f = NULL;
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;
439 for (size_t j = 0; j < 2; j++)
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;
448 sc = sc->pop();
449 global.endGagging(errors);
451 if (f)
453 if (f->errors)
454 return NULL;
455 return f;
459 return NULL;
462 /******************************************
463 * Build opEquals for struct.
464 * const bool opEquals(const S s) { ... }
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.
471 FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc)
473 if (hasIdentityOpEquals(sd, sc))
475 sd->hasIdentityEquals = true;
477 return NULL;
480 /******************************************
481 * Build __xopEquals for TypeInfo_Struct
482 * static bool __xopEquals(ref const S p, ref const S q)
484 * return p == q;
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.
490 FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc)
492 if (!needOpEquals(sd))
493 return NULL; // bitwise comparison would work
495 //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars());
496 if (Dsymbol *eq = search_function(sd, Id::eq))
498 if (FuncDeclaration *fd = eq->isFuncDeclaration())
500 TypeFunction *tfeqptr;
502 Scope scx;
504 /* const bool opEquals(ref const S s);
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);
512 fd = fd->overloadExactMatch(tfeqptr);
513 if (fd)
514 return fd;
518 if (!sd->xerreq)
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();
531 Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly
532 Loc loc = Loc(); // loc is unnecessary so errors are gagged
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);
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);
546 fop->fbody = new ReturnStatement(loc, e);
548 unsigned errors = global.startGagging(); // Do not report errors
549 Scope *sc2 = sc->push();
550 sc2->stc = 0;
551 sc2->linkage = LINKd;
553 fop->semantic(sc2);
554 fop->semantic2(sc2);
556 sc2->pop();
557 if (global.endGagging(errors)) // if errors happened
558 fop = sd->xerreq;
560 return fop;
563 /******************************************
564 * Build __xopCmp for TypeInfo_Struct
565 * static bool __xopCmp(ref const S p, ref const S q)
567 * return p.opCmp(q);
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.
573 FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc)
575 //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
576 if (Dsymbol *cmp = search_function(sd, Id::cmp))
578 if (FuncDeclaration *fd = cmp->isFuncDeclaration())
580 TypeFunction *tfcmpptr;
582 Scope scx;
584 /* const int opCmp(ref const S s);
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);
592 fd = fd->overloadExactMatch(tfcmpptr);
593 if (fd)
594 return fd;
597 else
599 // FIXME: doesn't work for recursive alias this
600 return NULL;
603 if (!sd->xerrcmp)
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();
616 Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly
617 Loc loc = Loc(); // loc is unnecessary so errors are gagged
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);
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
635 fop->fbody = new ReturnStatement(loc, e);
637 unsigned errors = global.startGagging(); // Do not report errors
638 Scope *sc2 = sc->push();
639 sc2->stc = 0;
640 sc2->linkage = LINKd;
642 fop->semantic(sc2);
643 fop->semantic2(sc2);
645 sc2->pop();
646 if (global.endGagging(errors)) // if errors happened
647 fop = sd->xerrcmp;
649 return fop;
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.
657 bool needToHash(StructDeclaration *sd)
659 //printf("StructDeclaration::needToHash() %s\n", sd->toChars());
660 if (sd->isUnionDeclaration())
661 goto Ldontneed;
663 if (sd->xhash)
664 goto Lneed;
666 /* If any of the fields has an opEquals, then we
667 * need it too.
669 for (size_t i = 0; i < sd->fields.dim; i++)
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)
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;
688 if (tv->isfloating())
690 // This is necessray for:
691 // 1. comparison of +0.0 and -0.0 should be true.
692 goto Lneed;
694 if (tv->ty == Tarray)
695 goto Lneed;
696 if (tv->ty == Taarray)
697 goto Lneed;
698 if (tv->ty == Tclass)
699 goto Lneed;
701 Ldontneed:
702 //printf("\tdontneed\n");
703 return false;
705 Lneed:
706 //printf("\tneed\n");
707 return true;
710 /******************************************
711 * Build __xtoHash for non-bitwise hashing
712 * static hash_t xtoHash(ref const S p) nothrow @trusted;
714 FuncDeclaration *buildXtoHash(StructDeclaration *sd, Scope *sc)
716 if (Dsymbol *s = search_function(sd, Id::tohash))
718 static TypeFunction *tftohash;
719 if (!tftohash)
721 tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd);
722 tftohash->mod = MODconst;
723 tftohash = (TypeFunction *)tftohash->merge();
726 if (FuncDeclaration *fd = s->isFuncDeclaration())
728 fd = fd->overloadExactMatch(tftohash);
729 if (fd)
730 return fd;
734 if (!needToHash(sd))
735 return NULL;
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
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);
745 Identifier *id = Id::xtoHash;
746 FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf);
747 fop->generated = true;
749 /* Do memberwise hashing.
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().
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)));
761 Scope *sc2 = sc->push();
762 sc2->stc = 0;
763 sc2->linkage = LINKd;
765 fop->semantic(sc2);
766 fop->semantic2(sc2);
768 sc2->pop();
770 //printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars());
771 return fop;
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).
781 FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc)
783 //printf("StructDeclaration::buildPostBlit() %s\n", sd->toChars());
784 if (sd->isUnionDeclaration())
785 return NULL;
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
791 for (size_t i = 0; i < sd->postblits.dim; i++)
793 stc |= sd->postblits[i]->storage_class & STCdisable;
796 Statements *a = new Statements();
797 for (size_t i = 0; i < sd->fields.dim && !(stc & STCdisable); i++)
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();
813 stc = mergeFuncAttrs(stc, sdv->postblit);
814 stc = mergeFuncAttrs(stc, sdv->dtor);
815 if (stc & STCdisable)
817 a->setDim(0);
818 break;
821 Expression *ex = NULL;
822 tv = v->type->toBasetype();
823 if (tv->ty == Tstruct)
825 // this.v.__xpostblit()
827 ex = new ThisExp(loc);
828 ex = new DotVarExp(loc, ex, v);
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;
837 ex = new DotVarExp(loc, ex, sdv->postblit, false);
838 ex = new CallExp(loc, ex);
840 else
842 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
844 uinteger_t n = 1;
845 while (tv->ty == Tsarray)
847 n *= ((TypeSArray *)tv)->dim->toUInteger();
848 tv = tv->nextOf()->toBasetype();
850 if (n == 0)
851 continue;
853 ex = new ThisExp(loc);
854 ex = new DotVarExp(loc, ex, v);
856 // This is a hack so we can call postblits on const/immutable objects.
857 ex = new DotIdExp(loc, ex, Id::ptr);
858 ex = new CastExp(loc, ex, sdv->type->pointerTo());
859 if (stc & STCsafe)
860 stc = (stc & ~STCsafe) | STCtrusted;
862 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
863 new IntegerExp(loc, n, Type::tsize_t));
864 // Prevent redundant bounds check
865 ((SliceExp *)ex)->upperIsInBounds = true;
866 ((SliceExp *)ex)->lowerIsLessThanUpper = true;
868 ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayPostblit), ex);
870 a->push(new ExpStatement(loc, ex)); // combine in forward order
872 /* Bugzilla 10972: When the following field postblit calls fail,
873 * this field should be destructed for Exception Safety.
875 if (!sdv->dtor)
876 continue;
877 sdv->dtor->functionSemantic();
879 tv = v->type->toBasetype();
880 if (v->type->toBasetype()->ty == Tstruct)
882 // this.v.__xdtor()
884 ex = new ThisExp(loc);
885 ex = new DotVarExp(loc, ex, v);
887 // This is a hack so we can call destructors on const/immutable objects.
888 ex = new AddrExp(loc, ex);
889 ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo());
890 ex = new PtrExp(loc, ex);
891 if (stc & STCsafe)
892 stc = (stc & ~STCsafe) | STCtrusted;
894 ex = new DotVarExp(loc, ex, sdv->dtor, false);
895 ex = new CallExp(loc, ex);
897 else
899 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
901 uinteger_t n = 1;
902 while (tv->ty == Tsarray)
904 n *= ((TypeSArray *)tv)->dim->toUInteger();
905 tv = tv->nextOf()->toBasetype();
907 //if (n == 0)
908 // continue;
910 ex = new ThisExp(loc);
911 ex = new DotVarExp(loc, ex, v);
913 // This is a hack so we can call destructors on const/immutable objects.
914 ex = new DotIdExp(loc, ex, Id::ptr);
915 ex = new CastExp(loc, ex, sdv->type->pointerTo());
916 if (stc & STCsafe)
917 stc = (stc & ~STCsafe) | STCtrusted;
919 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
920 new IntegerExp(loc, n, Type::tsize_t));
921 // Prevent redundant bounds check
922 ((SliceExp *)ex)->upperIsInBounds = true;
923 ((SliceExp *)ex)->lowerIsLessThanUpper = true;
925 ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayDtor), ex);
927 a->push(new OnScopeStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex)));
930 // Build our own "postblit" which executes a, but only if needed.
931 if (a->dim || (stc & STCdisable))
933 //printf("Building __fieldPostBlit()\n");
934 PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__fieldPostblit);
935 dd->generated = true;
936 dd->storage_class |= STCinference;
937 dd->fbody = (stc & STCdisable) ? NULL : new CompoundStatement(loc, a);
938 sd->postblits.shift(dd);
939 sd->members->push(dd);
940 dd->semantic(sc);
943 FuncDeclaration *xpostblit = NULL;
944 switch (sd->postblits.dim)
946 case 0:
947 break;
949 case 1:
950 xpostblit = sd->postblits[0];
951 break;
953 default:
954 Expression *e = NULL;
955 stc = STCsafe | STCnothrow | STCpure | STCnogc;
956 for (size_t i = 0; i < sd->postblits.dim; i++)
958 FuncDeclaration *fd = sd->postblits[i];
959 stc = mergeFuncAttrs(stc, fd);
960 if (stc & STCdisable)
962 e = NULL;
963 break;
965 Expression *ex = new ThisExp(loc);
966 ex = new DotVarExp(loc, ex, fd, false);
967 ex = new CallExp(loc, ex);
968 e = Expression::combine(e, ex);
970 PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__aggrPostblit);
971 dd->storage_class |= STCinference;
972 dd->fbody = new ExpStatement(loc, e);
973 sd->members->push(dd);
974 dd->semantic(sc);
975 xpostblit = dd;
976 break;
978 // Add an __xpostblit alias to make the inclusive postblit accessible
979 if (xpostblit)
981 AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xpostblit, xpostblit);
982 alias->semantic(sc);
983 sd->members->push(alias);
984 alias->addMember(sc, sd); // add to symbol table
986 return xpostblit;
989 /*****************************************
990 * Create inclusive destructor for struct/class by aggregating
991 * all the destructors in dtors[] with the destructors for
992 * all the members.
993 * Note the close similarity with StructDeclaration::buildPostBlit(),
994 * and the ordering changes (runs backward instead of forwards).
996 FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc)
998 //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars());
999 if (ad->isUnionDeclaration())
1000 return NULL;
1002 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
1003 Loc declLoc = ad->dtors.dim ? ad->dtors[0]->loc : ad->loc;
1004 Loc loc = Loc(); // internal code should have no loc to prevent coverage
1006 Expression *e = NULL;
1007 for (size_t i = 0; i < ad->fields.dim; i++)
1009 VarDeclaration *v = ad->fields[i];
1010 if (v->storage_class & STCref)
1011 continue;
1012 if (v->overlapped)
1013 continue;
1014 Type *tv = v->type->baseElemOf();
1015 if (tv->ty != Tstruct)
1016 continue;
1017 StructDeclaration *sdv = ((TypeStruct *)tv)->sym;
1018 if (!sdv->dtor)
1019 continue;
1020 sdv->dtor->functionSemantic();
1022 stc = mergeFuncAttrs(stc, sdv->dtor);
1023 if (stc & STCdisable)
1025 e = NULL;
1026 break;
1029 Expression *ex = NULL;
1030 tv = v->type->toBasetype();
1031 if (tv->ty == Tstruct)
1033 // this.v.__xdtor()
1035 ex = new ThisExp(loc);
1036 ex = new DotVarExp(loc, ex, v);
1038 // This is a hack so we can call destructors on const/immutable objects.
1039 ex = new AddrExp(loc, ex);
1040 ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo());
1041 ex = new PtrExp(loc, ex);
1042 if (stc & STCsafe)
1043 stc = (stc & ~STCsafe) | STCtrusted;
1045 ex = new DotVarExp(loc, ex, sdv->dtor, false);
1046 ex = new CallExp(loc, ex);
1048 else
1050 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1052 uinteger_t n = 1;
1053 while (tv->ty == Tsarray)
1055 n *= ((TypeSArray *)tv)->dim->toUInteger();
1056 tv = tv->nextOf()->toBasetype();
1058 if (n == 0)
1059 continue;
1061 ex = new ThisExp(loc);
1062 ex = new DotVarExp(loc, ex, v);
1064 // This is a hack so we can call destructors on const/immutable objects.
1065 ex = new DotIdExp(loc, ex, Id::ptr);
1066 ex = new CastExp(loc, ex, sdv->type->pointerTo());
1067 if (stc & STCsafe)
1068 stc = (stc & ~STCsafe) | STCtrusted;
1070 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
1071 new IntegerExp(loc, n, Type::tsize_t));
1072 // Prevent redundant bounds check
1073 ((SliceExp *)ex)->upperIsInBounds = true;
1074 ((SliceExp *)ex)->lowerIsLessThanUpper = true;
1076 ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayDtor), ex);
1078 e = Expression::combine(ex, e); // combine in reverse order
1081 /* Build our own "destructor" which executes e
1083 if (e || (stc & STCdisable))
1085 //printf("Building __fieldDtor()\n");
1086 DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__fieldDtor);
1087 dd->generated = true;
1088 dd->storage_class |= STCinference;
1089 dd->fbody = new ExpStatement(loc, e);
1090 ad->dtors.shift(dd);
1091 ad->members->push(dd);
1092 dd->semantic(sc);
1095 FuncDeclaration *xdtor = NULL;
1096 switch (ad->dtors.dim)
1098 case 0:
1099 break;
1101 case 1:
1102 xdtor = ad->dtors[0];
1103 break;
1105 default:
1106 e = NULL;
1107 stc = STCsafe | STCnothrow | STCpure | STCnogc;
1108 for (size_t i = 0; i < ad->dtors.dim; i++)
1110 FuncDeclaration *fd = ad->dtors[i];
1111 stc = mergeFuncAttrs(stc, fd);
1112 if (stc & STCdisable)
1114 e = NULL;
1115 break;
1117 Expression *ex = new ThisExp(loc);
1118 ex = new DotVarExp(loc, ex, fd, false);
1119 ex = new CallExp(loc, ex);
1120 e = Expression::combine(ex, e);
1122 DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__aggrDtor);
1123 dd->generated = true;
1124 dd->storage_class |= STCinference;
1125 dd->fbody = new ExpStatement(loc, e);
1126 ad->members->push(dd);
1127 dd->semantic(sc);
1128 xdtor = dd;
1129 break;
1131 // Add an __xdtor alias to make the inclusive dtor accessible
1132 if (xdtor)
1134 AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xdtor, xdtor);
1135 alias->semantic(sc);
1136 ad->members->push(alias);
1137 alias->addMember(sc, ad); // add to symbol table
1139 return xdtor;
1142 /******************************************
1143 * Create inclusive invariant for struct/class by aggregating
1144 * all the invariants in invs[].
1145 * void __invariant() const [pure nothrow @trusted]
1147 * invs[0](), invs[1](), ...;
1150 FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc)
1152 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
1153 Loc declLoc = ad->loc;
1154 Loc loc = Loc(); // internal code should have no loc to prevent coverage
1156 switch (ad->invs.dim)
1158 case 0:
1159 return NULL;
1161 case 1:
1162 // Don't return invs[0] so it has uniquely generated name.
1163 /* fall through */
1165 default:
1166 Expression *e = NULL;
1167 StorageClass stcx = 0;
1168 for (size_t i = 0; i < ad->invs.dim; i++)
1170 stc = mergeFuncAttrs(stc, ad->invs[i]);
1171 if (stc & STCdisable)
1173 // What should do?
1175 StorageClass stcy = (ad->invs[i]->storage_class & STCsynchronized) |
1176 (ad->invs[i]->type->mod & MODshared ? STCshared : 0);
1177 if (i == 0)
1178 stcx = stcy;
1179 else if (stcx ^ stcy)
1181 #if 1 // currently rejects
1182 ad->error(ad->invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported");
1183 e = NULL;
1184 break;
1185 #endif
1187 e = Expression::combine(e, new CallExp(loc, new VarExp(loc, ad->invs[i], false)));
1189 InvariantDeclaration *inv;
1190 inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id::classInvariant);
1191 inv->fbody = new ExpStatement(loc, e);
1192 ad->members->push(inv);
1193 inv->semantic(sc);
1194 return inv;