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 */
mergeFuncAttrs(StorageClass s1,FuncDeclaration * f)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 */
hasIdentityOpAssign(AggregateDeclaration * ad,Scope * sc)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 */
needOpAssign(StructDeclaration * sd)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 */
buildOpAssign(StructDeclaration * sd,Scope * sc)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 */
needOpEquals(StructDeclaration * sd)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 */
hasIdentityOpEquals(AggregateDeclaration * ad,Scope * sc)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 */
buildOpEquals(StructDeclaration * sd,Scope * sc)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 */
buildXopEquals(StructDeclaration * sd,Scope * sc)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 */
buildXopCmp(StructDeclaration * sd,Scope * sc)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 */
needToHash(StructDeclaration * sd)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 */
buildXtoHash(StructDeclaration * sd,Scope * sc)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 */
buildPostBlit(StructDeclaration * sd,Scope * sc)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 = tv->numberOfElems(loc);
845 if (n == 0)
846 continue;
847
848 ex = new ThisExp(loc);
849 ex = new DotVarExp(loc, ex, v);
850
851 // This is a hack so we can call postblits on const/immutable objects.
852 ex = new DotIdExp(loc, ex, Id::ptr);
853 ex = new CastExp(loc, ex, sdv->type->pointerTo());
854 if (stc & STCsafe)
855 stc = (stc & ~STCsafe) | STCtrusted;
856
857 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
858 new IntegerExp(loc, n, Type::tsize_t));
859 // Prevent redundant bounds check
860 ((SliceExp *)ex)->upperIsInBounds = true;
861 ((SliceExp *)ex)->lowerIsLessThanUpper = true;
862
863 ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayPostblit), ex);
864 }
865 a->push(new ExpStatement(loc, ex)); // combine in forward order
866
867 /* Bugzilla 10972: When the following field postblit calls fail,
868 * this field should be destructed for Exception Safety.
869 */
870 if (!sdv->dtor)
871 continue;
872 sdv->dtor->functionSemantic();
873
874 tv = v->type->toBasetype();
875 if (v->type->toBasetype()->ty == Tstruct)
876 {
877 // this.v.__xdtor()
878
879 ex = new ThisExp(loc);
880 ex = new DotVarExp(loc, ex, v);
881
882 // This is a hack so we can call destructors on const/immutable objects.
883 ex = new AddrExp(loc, ex);
884 ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo());
885 ex = new PtrExp(loc, ex);
886 if (stc & STCsafe)
887 stc = (stc & ~STCsafe) | STCtrusted;
888
889 ex = new DotVarExp(loc, ex, sdv->dtor, false);
890 ex = new CallExp(loc, ex);
891 }
892 else
893 {
894 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
895
896 uinteger_t n = tv->numberOfElems(loc);
897 //if (n == 0)
898 // continue;
899
900 ex = new ThisExp(loc);
901 ex = new DotVarExp(loc, ex, v);
902
903 // This is a hack so we can call destructors on const/immutable objects.
904 ex = new DotIdExp(loc, ex, Id::ptr);
905 ex = new CastExp(loc, ex, sdv->type->pointerTo());
906 if (stc & STCsafe)
907 stc = (stc & ~STCsafe) | STCtrusted;
908
909 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
910 new IntegerExp(loc, n, Type::tsize_t));
911 // Prevent redundant bounds check
912 ((SliceExp *)ex)->upperIsInBounds = true;
913 ((SliceExp *)ex)->lowerIsLessThanUpper = true;
914
915 ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayDtor), ex);
916 }
917 a->push(new OnScopeStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex)));
918 }
919
920 // Build our own "postblit" which executes a, but only if needed.
921 if (a->dim || (stc & STCdisable))
922 {
923 //printf("Building __fieldPostBlit()\n");
924 PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__fieldPostblit);
925 dd->generated = true;
926 dd->storage_class |= STCinference;
927 dd->fbody = (stc & STCdisable) ? NULL : new CompoundStatement(loc, a);
928 sd->postblits.shift(dd);
929 sd->members->push(dd);
930 dd->semantic(sc);
931 }
932
933 FuncDeclaration *xpostblit = NULL;
934 switch (sd->postblits.dim)
935 {
936 case 0:
937 break;
938
939 case 1:
940 xpostblit = sd->postblits[0];
941 break;
942
943 default:
944 Expression *e = NULL;
945 stc = STCsafe | STCnothrow | STCpure | STCnogc;
946 for (size_t i = 0; i < sd->postblits.dim; i++)
947 {
948 FuncDeclaration *fd = sd->postblits[i];
949 stc = mergeFuncAttrs(stc, fd);
950 if (stc & STCdisable)
951 {
952 e = NULL;
953 break;
954 }
955 Expression *ex = new ThisExp(loc);
956 ex = new DotVarExp(loc, ex, fd, false);
957 ex = new CallExp(loc, ex);
958 e = Expression::combine(e, ex);
959 }
960 PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__aggrPostblit);
961 dd->storage_class |= STCinference;
962 dd->fbody = new ExpStatement(loc, e);
963 sd->members->push(dd);
964 dd->semantic(sc);
965 xpostblit = dd;
966 break;
967 }
968 // Add an __xpostblit alias to make the inclusive postblit accessible
969 if (xpostblit)
970 {
971 AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xpostblit, xpostblit);
972 alias->semantic(sc);
973 sd->members->push(alias);
974 alias->addMember(sc, sd); // add to symbol table
975 }
976 return xpostblit;
977 }
978
979 /*****************************************
980 * Create inclusive destructor for struct/class by aggregating
981 * all the destructors in dtors[] with the destructors for
982 * all the members.
983 * Note the close similarity with StructDeclaration::buildPostBlit(),
984 * and the ordering changes (runs backward instead of forwards).
985 */
buildDtor(AggregateDeclaration * ad,Scope * sc)986 FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc)
987 {
988 //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars());
989 if (ad->isUnionDeclaration())
990 return NULL;
991
992 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
993 Loc declLoc = ad->dtors.dim ? ad->dtors[0]->loc : ad->loc;
994 Loc loc = Loc(); // internal code should have no loc to prevent coverage
995
996 Expression *e = NULL;
997 for (size_t i = 0; i < ad->fields.dim; i++)
998 {
999 VarDeclaration *v = ad->fields[i];
1000 if (v->storage_class & STCref)
1001 continue;
1002 if (v->overlapped)
1003 continue;
1004 Type *tv = v->type->baseElemOf();
1005 if (tv->ty != Tstruct)
1006 continue;
1007 StructDeclaration *sdv = ((TypeStruct *)tv)->sym;
1008 if (!sdv->dtor)
1009 continue;
1010 sdv->dtor->functionSemantic();
1011
1012 stc = mergeFuncAttrs(stc, sdv->dtor);
1013 if (stc & STCdisable)
1014 {
1015 e = NULL;
1016 break;
1017 }
1018
1019 Expression *ex = NULL;
1020 tv = v->type->toBasetype();
1021 if (tv->ty == Tstruct)
1022 {
1023 // this.v.__xdtor()
1024
1025 ex = new ThisExp(loc);
1026 ex = new DotVarExp(loc, ex, v);
1027
1028 // This is a hack so we can call destructors on const/immutable objects.
1029 ex = new AddrExp(loc, ex);
1030 ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo());
1031 ex = new PtrExp(loc, ex);
1032 if (stc & STCsafe)
1033 stc = (stc & ~STCsafe) | STCtrusted;
1034
1035 ex = new DotVarExp(loc, ex, sdv->dtor, false);
1036 ex = new CallExp(loc, ex);
1037 }
1038 else
1039 {
1040 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1041
1042 uinteger_t n = tv->numberOfElems(loc);
1043 if (n == 0)
1044 continue;
1045
1046 ex = new ThisExp(loc);
1047 ex = new DotVarExp(loc, ex, v);
1048
1049 // This is a hack so we can call destructors on const/immutable objects.
1050 ex = new DotIdExp(loc, ex, Id::ptr);
1051 ex = new CastExp(loc, ex, sdv->type->pointerTo());
1052 if (stc & STCsafe)
1053 stc = (stc & ~STCsafe) | STCtrusted;
1054
1055 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
1056 new IntegerExp(loc, n, Type::tsize_t));
1057 // Prevent redundant bounds check
1058 ((SliceExp *)ex)->upperIsInBounds = true;
1059 ((SliceExp *)ex)->lowerIsLessThanUpper = true;
1060
1061 ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayDtor), ex);
1062 }
1063 e = Expression::combine(ex, e); // combine in reverse order
1064 }
1065
1066 /* Build our own "destructor" which executes e
1067 */
1068 if (e || (stc & STCdisable))
1069 {
1070 //printf("Building __fieldDtor()\n");
1071 DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__fieldDtor);
1072 dd->generated = true;
1073 dd->storage_class |= STCinference;
1074 dd->fbody = new ExpStatement(loc, e);
1075 ad->dtors.shift(dd);
1076 ad->members->push(dd);
1077 dd->semantic(sc);
1078 }
1079
1080 FuncDeclaration *xdtor = NULL;
1081 switch (ad->dtors.dim)
1082 {
1083 case 0:
1084 break;
1085
1086 case 1:
1087 xdtor = ad->dtors[0];
1088 break;
1089
1090 default:
1091 e = NULL;
1092 stc = STCsafe | STCnothrow | STCpure | STCnogc;
1093 for (size_t i = 0; i < ad->dtors.dim; i++)
1094 {
1095 FuncDeclaration *fd = ad->dtors[i];
1096 stc = mergeFuncAttrs(stc, fd);
1097 if (stc & STCdisable)
1098 {
1099 e = NULL;
1100 break;
1101 }
1102 Expression *ex = new ThisExp(loc);
1103 ex = new DotVarExp(loc, ex, fd, false);
1104 ex = new CallExp(loc, ex);
1105 e = Expression::combine(ex, e);
1106 }
1107 DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__aggrDtor);
1108 dd->generated = true;
1109 dd->storage_class |= STCinference;
1110 dd->fbody = new ExpStatement(loc, e);
1111 ad->members->push(dd);
1112 dd->semantic(sc);
1113 xdtor = dd;
1114 break;
1115 }
1116 // Add an __xdtor alias to make the inclusive dtor accessible
1117 if (xdtor)
1118 {
1119 AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xdtor, xdtor);
1120 alias->semantic(sc);
1121 ad->members->push(alias);
1122 alias->addMember(sc, ad); // add to symbol table
1123 }
1124 return xdtor;
1125 }
1126
1127 /******************************************
1128 * Create inclusive invariant for struct/class by aggregating
1129 * all the invariants in invs[].
1130 * void __invariant() const [pure nothrow @trusted]
1131 * {
1132 * invs[0](), invs[1](), ...;
1133 * }
1134 */
buildInv(AggregateDeclaration * ad,Scope * sc)1135 FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc)
1136 {
1137 StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
1138 Loc declLoc = ad->loc;
1139 Loc loc = Loc(); // internal code should have no loc to prevent coverage
1140
1141 switch (ad->invs.dim)
1142 {
1143 case 0:
1144 return NULL;
1145
1146 case 1:
1147 // Don't return invs[0] so it has uniquely generated name.
1148 /* fall through */
1149
1150 default:
1151 Expression *e = NULL;
1152 StorageClass stcx = 0;
1153 for (size_t i = 0; i < ad->invs.dim; i++)
1154 {
1155 stc = mergeFuncAttrs(stc, ad->invs[i]);
1156 if (stc & STCdisable)
1157 {
1158 // What should do?
1159 }
1160 StorageClass stcy = (ad->invs[i]->storage_class & STCsynchronized) |
1161 (ad->invs[i]->type->mod & MODshared ? STCshared : 0);
1162 if (i == 0)
1163 stcx = stcy;
1164 else if (stcx ^ stcy)
1165 {
1166 #if 1 // currently rejects
1167 ad->error(ad->invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported");
1168 e = NULL;
1169 break;
1170 #endif
1171 }
1172 e = Expression::combine(e, new CallExp(loc, new VarExp(loc, ad->invs[i], false)));
1173 }
1174 InvariantDeclaration *inv;
1175 inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id::classInvariant);
1176 inv->fbody = new ExpStatement(loc, e);
1177 ad->members->push(inv);
1178 inv->semantic(sc);
1179 return inv;
1180 }
1181 }
1182