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