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