]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/clone.c
Merge dmd upstream 6d5b853d3
[thirdparty/gcc.git] / gcc / d / dmd / clone.c
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
26 Expression *semantic(Expression *e, Scope *sc);
27
28 /*******************************************
29 * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
30 */
31 StorageClass 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 */
87 FuncDeclaration *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 */
141 bool 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
176 Lneed:
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 */
201 FuncDeclaration *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 */
352 bool 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 }
397 Ldontneed:
398 //printf("\tdontneed\n");
399 return false;
400
401 Lneed:
402 //printf("\tneed\n");
403 return true;
404 }
405
406 /*******************************************
407 * Check given aggregate actually has an identity opEquals or not.
408 */
409 FuncDeclaration *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 */
471 FuncDeclaration *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 */
490 FuncDeclaration *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 */
573 FuncDeclaration *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 */
657 bool 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 }
701 Ldontneed:
702 //printf("\tdontneed\n");
703 return false;
704
705 Lneed:
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 */
714 FuncDeclaration *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 */
781 FuncDeclaration *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 = 1;
845 while (tv->ty == Tsarray)
846 {
847 n *= ((TypeSArray *)tv)->dim->toUInteger();
848 tv = tv->nextOf()->toBasetype();
849 }
850 if (n == 0)
851 continue;
852
853 ex = new ThisExp(loc);
854 ex = new DotVarExp(loc, ex, v);
855
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;
861
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;
867
868 ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayPostblit), ex);
869 }
870 a->push(new ExpStatement(loc, ex)); // combine in forward order
871
872 /* Bugzilla 10972: When the following field postblit calls fail,
873 * this field should be destructed for Exception Safety.
874 */
875 if (!sdv->dtor)
876 continue;
877 sdv->dtor->functionSemantic();
878
879 tv = v->type->toBasetype();
880 if (v->type->toBasetype()->ty == Tstruct)
881 {
882 // this.v.__xdtor()
883
884 ex = new ThisExp(loc);
885 ex = new DotVarExp(loc, ex, v);
886
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;
893
894 ex = new DotVarExp(loc, ex, sdv->dtor, false);
895 ex = new CallExp(loc, ex);
896 }
897 else
898 {
899 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
900
901 uinteger_t n = 1;
902 while (tv->ty == Tsarray)
903 {
904 n *= ((TypeSArray *)tv)->dim->toUInteger();
905 tv = tv->nextOf()->toBasetype();
906 }
907 //if (n == 0)
908 // continue;
909
910 ex = new ThisExp(loc);
911 ex = new DotVarExp(loc, ex, v);
912
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;
918
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;
924
925 ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayDtor), ex);
926 }
927 a->push(new OnScopeStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex)));
928 }
929
930 // Build our own "postblit" which executes a, but only if needed.
931 if (a->dim || (stc & STCdisable))
932 {
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);
941 }
942
943 FuncDeclaration *xpostblit = NULL;
944 switch (sd->postblits.dim)
945 {
946 case 0:
947 break;
948
949 case 1:
950 xpostblit = sd->postblits[0];
951 break;
952
953 default:
954 Expression *e = NULL;
955 stc = STCsafe | STCnothrow | STCpure | STCnogc;
956 for (size_t i = 0; i < sd->postblits.dim; i++)
957 {
958 FuncDeclaration *fd = sd->postblits[i];
959 stc = mergeFuncAttrs(stc, fd);
960 if (stc & STCdisable)
961 {
962 e = NULL;
963 break;
964 }
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);
969 }
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;
977 }
978 // Add an __xpostblit alias to make the inclusive postblit accessible
979 if (xpostblit)
980 {
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
985 }
986 return xpostblit;
987 }
988
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).
995 */
996 FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc)
997 {
998 //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars());
999 if (ad->isUnionDeclaration())
1000 return NULL;
1001
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
1005
1006 Expression *e = NULL;
1007 for (size_t i = 0; i < ad->fields.dim; i++)
1008 {
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();
1021
1022 stc = mergeFuncAttrs(stc, sdv->dtor);
1023 if (stc & STCdisable)
1024 {
1025 e = NULL;
1026 break;
1027 }
1028
1029 Expression *ex = NULL;
1030 tv = v->type->toBasetype();
1031 if (tv->ty == Tstruct)
1032 {
1033 // this.v.__xdtor()
1034
1035 ex = new ThisExp(loc);
1036 ex = new DotVarExp(loc, ex, v);
1037
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;
1044
1045 ex = new DotVarExp(loc, ex, sdv->dtor, false);
1046 ex = new CallExp(loc, ex);
1047 }
1048 else
1049 {
1050 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1051
1052 uinteger_t n = 1;
1053 while (tv->ty == Tsarray)
1054 {
1055 n *= ((TypeSArray *)tv)->dim->toUInteger();
1056 tv = tv->nextOf()->toBasetype();
1057 }
1058 if (n == 0)
1059 continue;
1060
1061 ex = new ThisExp(loc);
1062 ex = new DotVarExp(loc, ex, v);
1063
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;
1069
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;
1075
1076 ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayDtor), ex);
1077 }
1078 e = Expression::combine(ex, e); // combine in reverse order
1079 }
1080
1081 /* Build our own "destructor" which executes e
1082 */
1083 if (e || (stc & STCdisable))
1084 {
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);
1093 }
1094
1095 FuncDeclaration *xdtor = NULL;
1096 switch (ad->dtors.dim)
1097 {
1098 case 0:
1099 break;
1100
1101 case 1:
1102 xdtor = ad->dtors[0];
1103 break;
1104
1105 default:
1106 e = NULL;
1107 stc = STCsafe | STCnothrow | STCpure | STCnogc;
1108 for (size_t i = 0; i < ad->dtors.dim; i++)
1109 {
1110 FuncDeclaration *fd = ad->dtors[i];
1111 stc = mergeFuncAttrs(stc, fd);
1112 if (stc & STCdisable)
1113 {
1114 e = NULL;
1115 break;
1116 }
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);
1121 }
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;
1130 }
1131 // Add an __xdtor alias to make the inclusive dtor accessible
1132 if (xdtor)
1133 {
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
1138 }
1139 return xdtor;
1140 }
1141
1142 /******************************************
1143 * Create inclusive invariant for struct/class by aggregating
1144 * all the invariants in invs[].
1145 * void __invariant() const [pure nothrow @trusted]
1146 * {
1147 * invs[0](), invs[1](), ...;
1148 * }
1149 */
1150 FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc)
1151 {
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
1155
1156 switch (ad->invs.dim)
1157 {
1158 case 0:
1159 return NULL;
1160
1161 case 1:
1162 // Don't return invs[0] so it has uniquely generated name.
1163 /* fall through */
1164
1165 default:
1166 Expression *e = NULL;
1167 StorageClass stcx = 0;
1168 for (size_t i = 0; i < ad->invs.dim; i++)
1169 {
1170 stc = mergeFuncAttrs(stc, ad->invs[i]);
1171 if (stc & STCdisable)
1172 {
1173 // What should do?
1174 }
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)
1180 {
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
1186 }
1187 e = Expression::combine(e, new CallExp(loc, new VarExp(loc, ad->invs[i], false)));
1188 }
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;
1195 }
1196 }