1 /**
2  * Builds struct member functions if needed and not defined by the user.
3  * Includes `opEquals`, `opAssign`, post blit, copy constructor and destructor.
4  *
5  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
6  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/clone.d, _clone.d)
9  * Documentation:  https://dlang.org/phobos/dmd_clone.html
10  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/clone.d
11  */
12 
13 module dmd.clone;
14 
15 import core.stdc.stdio;
16 import dmd.aggregate;
17 import dmd.arraytypes;
18 import dmd.astenums;
19 import dmd.dclass;
20 import dmd.declaration;
21 import dmd.dscope;
22 import dmd.dstruct;
23 import dmd.dsymbol;
24 import dmd.dsymbolsem;
25 import dmd.dtemplate;
26 import dmd.errors;
27 import dmd.expression;
28 import dmd.expressionsem;
29 import dmd.func;
30 import dmd.globals;
31 import dmd.id;
32 import dmd.identifier;
33 import dmd.init;
34 import dmd.location;
35 import dmd.mtype;
36 import dmd.opover;
37 import dmd.semantic2;
38 import dmd.semantic3;
39 import dmd.statement;
40 import dmd.target;
41 import dmd.typesem;
42 import dmd.tokens;
43 
44 /*******************************************
45  * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
46  * from f into s1.
47  * Params:
48  *      s1 = storage class to merge into
49  *      f = function
50  * Returns:
51  *      merged storage class
52  */
53 StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure
54 {
55     if (!f)
56         return s1;
57     StorageClass s2 = (f.storage_class & STC.disable);
58 
59     TypeFunction tf = cast(TypeFunction)f.type;
60     if (tf.trust == TRUST.safe)
61         s2 |= STC.safe;
62     else if (tf.trust == TRUST.system)
63         s2 |= STC.system;
64     else if (tf.trust == TRUST.trusted)
65         s2 |= STC.trusted;
66 
67     if (tf.purity != PURE.impure)
68         s2 |= STC.pure_;
69     if (tf.isnothrow)
70         s2 |= STC.nothrow_;
71     if (tf.isnogc)
72         s2 |= STC.nogc;
73 
74     const sa = s1 & s2;
75     const so = s1 | s2;
76 
77     StorageClass stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable);
78 
79     if (so & STC.system)
80         stc |= STC.system;
81     else if (sa & STC.trusted)
82         stc |= STC.trusted;
83     else if ((so & (STC.trusted | STC.safe)) == (STC.trusted | STC.safe))
84         stc |= STC.trusted;
85     else if (sa & STC.safe)
86         stc |= STC.safe;
87 
88     return stc;
89 }
90 
91 /*******************************************
92  * Check given aggregate actually has an identity opAssign or not.
93  * Params:
94  *      ad = struct or class
95  *      sc = current scope
96  * Returns:
97  *      if found, returns FuncDeclaration of opAssign, otherwise null
98  */
99 FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
100 {
101     Dsymbol assign = search_function(ad, Id.assign);
102     if (assign)
103     {
104         /* check identity opAssign exists
105          */
106         scope er = new NullExp(ad.loc, ad.type);    // dummy rvalue
107         scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
108         el.type = ad.type;
109         auto a = new Expressions(1);
110         const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
111         sc = sc.push();
112         sc.tinst = null;
113         sc.minst = null;
114 
115         (*a)[0] = er;
116         auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet);
117         if (!f)
118         {
119             (*a)[0] = el;
120             f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet);
121         }
122 
123         sc = sc.pop();
124         global.endGagging(errors);
125         if (f)
126         {
127             if (f.errors)
128                 return null;
129             auto fparams = f.getParameterList();
130             if (fparams.length)
131             {
132                 auto fparam0 = fparams[0];
133                 if (fparam0.type.toDsymbol(null) != ad)
134                     f = null;
135             }
136         }
137         // BUGS: This detection mechanism cannot find some opAssign-s like follows:
138         // struct S { void opAssign(ref immutable S) const; }
139         return f;
140     }
141     return null;
142 }
143 
144 /*******************************************
145  * We need an opAssign for the struct if
146  * it has a destructor or a postblit.
147  * We need to generate one if a user-specified one does not exist.
148  */
149 private bool needOpAssign(StructDeclaration sd)
150 {
151     //printf("StructDeclaration::needOpAssign() %s\n", sd.toChars());
152 
153     static bool isNeeded()
154     {
155         //printf("\tneed\n");
156         return true;
157     }
158 
159     if (sd.isUnionDeclaration())
160         return !isNeeded();
161 
162     if (sd.hasIdentityAssign || // because has identity==elaborate opAssign
163         sd.dtor ||
164         sd.postblit)
165         return isNeeded();
166 
167     /* If any of the fields need an opAssign, then we
168      * need it too.
169      */
170     foreach (v; sd.fields)
171     {
172         if (v.storage_class & STC.ref_)
173             continue;
174         if (v.overlapped)               // if field of a union
175             continue;                   // user must handle it themselves
176         Type tv = v.type.baseElemOf();
177         if (tv.ty == Tstruct)
178         {
179             TypeStruct ts = cast(TypeStruct)tv;
180             if (ts.sym.isUnionDeclaration())
181                 continue;
182             if (needOpAssign(ts.sym))
183                 return isNeeded();
184         }
185     }
186     return !isNeeded();
187 }
188 
189 /******************************************
190  * Build opAssign for a `struct`.
191  *
192  * The generated `opAssign` function has the following signature:
193  *---
194  *ref S opAssign(S s)    // S is the name of the `struct`
195  *---
196  *
197  * The opAssign function will be built for a struct `S` if the
198  * following constraints are met:
199  *
200  * 1. `S` does not have an identity `opAssign` defined.
201  *
202  * 2. `S` has at least one of the following members: a postblit (user-defined or
203  * generated for fields that have a defined postblit), a destructor
204  * (user-defined or generated for fields that have a defined destructor)
205  * or at least one field that has a defined `opAssign`.
206  *
207  * 3. `S` does not have any non-mutable fields.
208  *
209  * If `S` has a disabled destructor or at least one field that has a disabled
210  * `opAssign`, `S.opAssign` is going to be generated, but marked with `@disable`
211  *
212  * If `S` defines a destructor, the generated code for `opAssign` is:
213  *
214  *---
215  *S __swap = void;
216  *__swap = this;   // bit copy
217  *this = s;        // bit copy
218  *__swap.dtor();
219  *---
220  *
221  * Otherwise, if `S` defines a postblit, the generated code for `opAssign` is:
222  *
223  *---
224  *this = s;
225  *---
226  *
227  * Note that the parameter to the generated `opAssign` is passed by value, which means
228  * that the postblit is going to be called (if it is defined) in both  of the above
229  * situations before entering the body of `opAssign`. The assignments in the above generated
230  * function bodies are blit expressions, so they can be regarded as `memcpy`s
231  * (`opAssign` is not called as this will result in an infinite recursion; the postblit
232  * is not called because it has already been called when the parameter was passed by value).
233  *
234  * If `S` does not have a postblit or a destructor, but contains at least one field that defines
235  * an `opAssign` function (which is not disabled), then the body will make member-wise
236  * assignments:
237  *
238  *---
239  *this.field1 = s.field1;
240  *this.field2 = s.field2;
241  *...;
242  *---
243  *
244  * In this situation, the assignemnts are actual assign expressions (`opAssign` is used
245  * if defined).
246  *
247  * References:
248  *      https://dlang.org/spec/struct.html#assign-overload
249  * Params:
250  *      sd = struct to generate opAssign for
251  *      sc = context
252  * Returns:
253  *      generated `opAssign` function
254  */
255 FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
256 {
257     if (FuncDeclaration f = hasIdentityOpAssign(sd, sc))
258     {
259         sd.hasIdentityAssign = true;
260         return f;
261     }
262     // Even if non-identity opAssign is defined, built-in identity opAssign
263     // will be defined.
264     if (!needOpAssign(sd))
265         return null;
266 
267     //printf("StructDeclaration::buildOpAssign() %s\n", sd.toChars());
268     StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
269     Loc declLoc = sd.loc;
270     Loc loc; // internal code should have no loc to prevent coverage
271 
272     // One of our sub-field might have `@disable opAssign` so we need to
273     // check for it.
274     // In this event, it will be reflected by having `stc` (opAssign's
275     // storage class) include `STC.disabled`.
276     foreach (v; sd.fields)
277     {
278         if (v.storage_class & STC.ref_)
279             continue;
280         if (v.overlapped)
281             continue;
282         Type tv = v.type.baseElemOf();
283         if (tv.ty != Tstruct)
284             continue;
285         StructDeclaration sdv = (cast(TypeStruct)tv).sym;
286         stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc));
287     }
288 
289     if (sd.dtor || sd.postblit)
290     {
291         // if the type is not assignable, we cannot generate opAssign
292         if (!sd.type.isAssignable()) // https://issues.dlang.org/show_bug.cgi?id=13044
293             return null;
294         stc = mergeFuncAttrs(stc, sd.dtor);
295         if (stc & STC.safe)
296             stc = (stc & ~STC.safe) | STC.trusted;
297     }
298 
299     auto fparams = new Parameters();
300     fparams.push(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null));
301     auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_);
302     auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf);
303     fop.storage_class |= STC.inference;
304     fop.isGenerated = true;
305     Expression e;
306     if (stc & STC.disable)
307     {
308         e = null;
309     }
310     /* Do swap this and rhs.
311      *    __swap = this; this = s; __swap.dtor();
312      */
313     else if (sd.dtor)
314     {
315         //printf("\tswap copy\n");
316         TypeFunction tdtor = cast(TypeFunction)sd.dtor.type;
317         assert(tdtor.ty == Tfunction);
318 
319         auto idswap = Identifier.generateId("__swap");
320         auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc));
321         swap.storage_class |= STC.nodtor | STC.temp | STC.ctfe;
322         if (tdtor.isScopeQual)
323             swap.storage_class |= STC.scope_;
324         auto e1 = new DeclarationExp(loc, swap);
325 
326         auto e2 = new BlitExp(loc, new VarExp(loc, swap), new ThisExp(loc));
327         auto e3 = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p));
328 
329         /* Instead of running the destructor on s, run it
330          * on swap. This avoids needing to copy swap back in to s.
331          */
332         auto e4 = new CallExp(loc, new DotVarExp(loc, new VarExp(loc, swap), sd.dtor, false));
333 
334         e = Expression.combine(e1, e2, e3, e4);
335     }
336     /* postblit was called when the value was passed to opAssign, we just need to blit the result */
337     else if (sd.postblit)
338     {
339         e = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p));
340         sd.hasBlitAssign = true;
341     }
342     else
343     {
344         /* Do memberwise copy.
345          *
346          * If sd is a nested struct, its vthis field assignment is:
347          * 1. If it's nested in a class, it's a rebind of class reference.
348          * 2. If it's nested in a function or struct, it's an update of void*.
349          * In both cases, it will change the parent context.
350          */
351         //printf("\tmemberwise copy\n");
352         e = null;
353         foreach (v; sd.fields)
354         {
355             // this.v = s.v;
356             auto ec = new AssignExp(loc,
357                 new DotVarExp(loc, new ThisExp(loc), v),
358                 new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
359             e = Expression.combine(e, ec);
360         }
361     }
362     if (e)
363     {
364         Statement s1 = new ExpStatement(loc, e);
365         /* Add:
366          *   return this;
367          */
368         auto er = new ThisExp(loc);
369         Statement s2 = new ReturnStatement(loc, er);
370         fop.fbody = new CompoundStatement(loc, s1, s2);
371         tf.isreturn = true;
372     }
373     sd.members.push(fop);
374     fop.addMember(sc, sd);
375     sd.hasIdentityAssign = true; // temporary mark identity assignable
376     const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
377     Scope* sc2 = sc.push();
378     sc2.stc = 0;
379     sc2.linkage = LINK.d;
380     fop.dsymbolSemantic(sc2);
381     fop.semantic2(sc2);
382     // https://issues.dlang.org/show_bug.cgi?id=15044
383     //semantic3(fop, sc2); // isn't run here for lazy forward reference resolution.
384 
385     sc2.pop();
386     if (global.endGagging(errors)) // if errors happened
387     {
388         // Disable generated opAssign, because some members forbid identity assignment.
389         fop.storage_class |= STC.disable;
390         fop.fbody = null; // remove fbody which contains the error
391     }
392 
393     //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd.toChars(), (fop.storage_class & STC.disable) != 0);
394     //printf("fop.type: %s\n", fop.type.toPrettyChars());
395     return fop;
396 }
397 
398 /*******************************************
399  * We need an opEquals for the struct if
400  * any fields has an opEquals.
401  * Generate one if a user-specified one does not exist.
402  */
403 bool needOpEquals(StructDeclaration sd)
404 {
405     //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars());
406     if (sd.isUnionDeclaration())
407     {
408         /* If a union has only one field, treat it like a struct
409          */
410         if (sd.fields.length != 1)
411             goto Ldontneed;
412     }
413     if (sd.hasIdentityEquals)
414         goto Lneed;
415     /* If any of the fields has an opEquals, then we
416      * need it too.
417      */
418     foreach (VarDeclaration v; sd.fields)
419     {
420         if (v.storage_class & STC.ref_)
421             continue;
422         if (v.overlapped)
423             continue;
424         Type tv = v.type.toBasetype();
425         auto tvbase = tv.baseElemOf();
426         if (tvbase.ty == Tstruct)
427         {
428             TypeStruct ts = cast(TypeStruct)tvbase;
429             if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1)
430                 continue;
431             if (needOpEquals(ts.sym))
432                 goto Lneed;
433         }
434         if (tvbase.isfloating())
435         {
436             // This is necessray for:
437             //  1. comparison of +0.0 and -0.0 should be true.
438             //  2. comparison of NANs should be false always.
439             goto Lneed;
440         }
441         if (tvbase.ty == Tarray)
442             goto Lneed;
443         if (tvbase.ty == Taarray)
444             goto Lneed;
445         if (tvbase.ty == Tclass)
446             goto Lneed;
447     }
448 Ldontneed:
449     //printf("\tdontneed\n");
450     return false;
451 Lneed:
452     //printf("\tneed\n");
453     return true;
454 }
455 
456 /*******************************************
457  * Check given aggregate actually has an identity opEquals or not.
458  */
459 private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc)
460 {
461     FuncDeclaration f;
462     if (Dsymbol eq = search_function(ad, Id.eq))
463     {
464         /* check identity opEquals exists
465          */
466         scope er = new NullExp(ad.loc, null); // dummy rvalue
467         scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
468         auto a = new Expressions(1);
469 
470         bool hasIt(Type tthis)
471         {
472             const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it
473             sc = sc.push();
474             sc.tinst = null;
475             sc.minst = null;
476 
477             FuncDeclaration rfc(Expression e)
478             {
479                 (*a)[0] = e;
480                 (*a)[0].type = tthis;
481                 return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet);
482             }
483 
484             f = rfc(er);
485             if (!f)
486                 f = rfc(el);
487 
488             sc = sc.pop();
489             global.endGagging(errors);
490 
491             return f !is null;
492         }
493 
494         if (hasIt(ad.type)               ||
495             hasIt(ad.type.constOf())     ||
496             hasIt(ad.type.immutableOf()) ||
497             hasIt(ad.type.sharedOf())    ||
498             hasIt(ad.type.sharedConstOf()))
499         {
500             if (f.errors)
501                 return null;
502         }
503     }
504     return f;
505 }
506 
507 /******************************************
508  * Build opEquals for struct.
509  *      const bool opEquals(const S s) { ... }
510  *
511  * By fixing https://issues.dlang.org/show_bug.cgi?id=3789
512  * opEquals is changed to be never implicitly generated.
513  * Now, struct objects comparison s1 == s2 is translated to:
514  *      s1.tupleof == s2.tupleof
515  * to calculate structural equality. See EqualExp.op_overload.
516  */
517 FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc)
518 {
519     if (hasIdentityOpEquals(sd, sc))
520     {
521         sd.hasIdentityEquals = true;
522     }
523     return null;
524 }
525 
526 /******************************************
527  * Build __xopEquals for TypeInfo_Struct
528  *      bool __xopEquals(ref const S p) const
529  *      {
530  *          return this == p;
531  *      }
532  *
533  * This is called by TypeInfo.equals(p1, p2). If the struct does not support
534  * const objects comparison, it will throw "not implemented" Error in runtime.
535  */
536 FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
537 {
538     if (!needOpEquals(sd))
539         return null; // bitwise comparison would work
540 
541     //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars());
542     if (Dsymbol eq = search_function(sd, Id.eq))
543     {
544         if (FuncDeclaration fd = eq.isFuncDeclaration())
545         {
546             TypeFunction tfeqptr;
547             {
548                 Scope scx;
549                 scx.eSink = sc.eSink;
550                 /* const bool opEquals(ref const S s);
551                  */
552                 auto parameters = new Parameters();
553                 parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null));
554                 tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d);
555                 tfeqptr.mod = MODFlags.const_;
556                 tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx);
557             }
558             fd = fd.overloadExactMatch(tfeqptr);
559             if (fd)
560                 return fd;
561         }
562     }
563     if (!sd.xerreq)
564     {
565         // object._xopEquals
566         Identifier id = Identifier.idPool("_xopEquals");
567         Expression e = new IdentifierExp(sd.loc, Id.empty);
568         e = new DotIdExp(sd.loc, e, Id.object);
569         e = new DotIdExp(sd.loc, e, id);
570         e = e.expressionSemantic(sc);
571         if (!e.isErrorExp())
572         {
573             Dsymbol s = getDsymbol(e);
574             assert(s);
575             sd.xerreq = s.isFuncDeclaration();
576         }
577     }
578     Loc declLoc; // loc is unnecessary so __xopEquals is never called directly
579     Loc loc; // loc is unnecessary so errors are gagged
580     auto parameters = new Parameters();
581     parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
582     auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_);
583     tf = tf.addSTC(STC.const_).toTypeFunction();
584     Identifier id = Id.xopEquals;
585     auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf);
586     fop.isGenerated = true;
587     fop.parent = sd;
588     Expression e1 = new IdentifierExp(loc, Id.This);
589     Expression e2 = new IdentifierExp(loc, Id.p);
590     Expression e = new EqualExp(EXP.equal, loc, e1, e2);
591     fop.fbody = new ReturnStatement(loc, e);
592     uint errors = global.startGagging(); // Do not report errors
593     Scope* sc2 = sc.push();
594     sc2.stc = 0;
595     sc2.linkage = LINK.d;
596     fop.dsymbolSemantic(sc2);
597     fop.semantic2(sc2);
598     sc2.pop();
599     if (global.endGagging(errors)) // if errors happened
600         fop = sd.xerreq;
601     return fop;
602 }
603 
604 /******************************************
605  * Build __xopCmp for TypeInfo_Struct
606  *      int __xopCmp(ref const S p) const
607  *      {
608  *          return this.opCmp(p);
609  *      }
610  *
611  * This is called by TypeInfo.compare(p1, p2). If the struct does not support
612  * const objects comparison, it will throw "not implemented" Error in runtime.
613  */
614 FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
615 {
616     //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
617     if (Dsymbol cmp = search_function(sd, Id.cmp))
618     {
619         if (FuncDeclaration fd = cmp.isFuncDeclaration())
620         {
621             TypeFunction tfcmpptr;
622             {
623                 Scope scx;
624                 scx.eSink = sc.eSink;
625                 /* const int opCmp(ref const S s);
626                  */
627                 auto parameters = new Parameters();
628                 parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null));
629                 tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
630                 tfcmpptr.mod = MODFlags.const_;
631                 tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx);
632             }
633             fd = fd.overloadExactMatch(tfcmpptr);
634             if (fd)
635                 return fd;
636         }
637     }
638     else
639     {
640         version (none) // FIXME: doesn't work for recursive alias this
641         {
642             /* Check opCmp member exists.
643              * Consider 'alias this', but except opDispatch.
644              */
645             Expression e = new DsymbolExp(sd.loc, sd);
646             e = new DotIdExp(sd.loc, e, Id.cmp);
647             Scope* sc2 = sc.push();
648             e = e.trySemantic(sc2);
649             sc2.pop();
650             if (e)
651             {
652                 Dsymbol s = null;
653                 switch (e.op)
654                 {
655                 case EXP.overloadSet:
656                     s = e.isOverExp().vars;
657                     break;
658                 case EXP.scope_:
659                     s = e.isScopeExp().sds;
660                     break;
661                 case EXP.variable:
662                     s = e.isVarExp().var;
663                     break;
664                 default:
665                     break;
666                 }
667                 if (!s || s.ident != Id.cmp)
668                     e = null; // there's no valid member 'opCmp'
669             }
670             if (!e)
671                 return null; // bitwise comparison would work
672             /* Essentially, a struct which does not define opCmp is not comparable.
673              * At this time, typeid(S).compare might be correct that throwing "not implement" Error.
674              * But implementing it would break existing code, such as:
675              *
676              * struct S { int value; }  // no opCmp
677              * int[S] aa;   // Currently AA key uses bitwise comparison
678              *              // (It's default behavior of TypeInfo_Strust.compare).
679              *
680              * Not sure we should fix this inconsistency, so just keep current behavior.
681              */
682         }
683         else
684         {
685             return null;
686         }
687     }
688     if (!sd.xerrcmp)
689     {
690         // object._xopCmp
691         Identifier id = Identifier.idPool("_xopCmp");
692         Expression e = new IdentifierExp(sd.loc, Id.empty);
693         e = new DotIdExp(sd.loc, e, Id.object);
694         e = new DotIdExp(sd.loc, e, id);
695         e = e.expressionSemantic(sc);
696         if (!e.isErrorExp())
697         {
698             Dsymbol s = getDsymbol(e);
699             assert(s);
700             sd.xerrcmp = s.isFuncDeclaration();
701         }
702     }
703     Loc declLoc; // loc is unnecessary so __xopCmp is never called directly
704     Loc loc; // loc is unnecessary so errors are gagged
705     auto parameters = new Parameters();
706     parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
707     auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_);
708     tf = tf.addSTC(STC.const_).toTypeFunction();
709     Identifier id = Id.xopCmp;
710     auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf);
711     fop.isGenerated = true;
712     fop.parent = sd;
713     Expression e1 = new IdentifierExp(loc, Id.This);
714     Expression e2 = new IdentifierExp(loc, Id.p);
715     Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2);
716     fop.fbody = new ReturnStatement(loc, e);
717     uint errors = global.startGagging(); // Do not report errors
718     Scope* sc2 = sc.push();
719     sc2.stc = 0;
720     sc2.linkage = LINK.d;
721     fop.dsymbolSemantic(sc2);
722     fop.semantic2(sc2);
723     sc2.pop();
724     if (global.endGagging(errors)) // if errors happened
725         fop = sd.xerrcmp;
726     return fop;
727 }
728 
729 /*******************************************
730  * We need a toHash for the struct if
731  * any fields has a toHash.
732  * Generate one if a user-specified one does not exist.
733  */
734 private bool needToHash(StructDeclaration sd)
735 {
736     //printf("StructDeclaration::needToHash() %s\n", sd.toChars());
737     if (sd.isUnionDeclaration())
738         goto Ldontneed;
739     if (sd.xhash)
740         goto Lneed;
741 
742     /* If any of the fields has an toHash, then we
743      * need it too.
744      */
745     foreach (VarDeclaration v; sd.fields)
746     {
747         if (v.storage_class & STC.ref_)
748             continue;
749         if (v.overlapped)
750             continue;
751         Type tv = v.type.toBasetype();
752         auto tvbase = tv.baseElemOf();
753         if (tvbase.ty == Tstruct)
754         {
755             TypeStruct ts = cast(TypeStruct)tvbase;
756             if (ts.sym.isUnionDeclaration())
757                 continue;
758             if (needToHash(ts.sym))
759                 goto Lneed;
760         }
761         if (tvbase.isfloating())
762         {
763             /* This is necessary because comparison of +0.0 and -0.0 should be true,
764              * i.e. not a bit compare.
765              */
766             goto Lneed;
767         }
768         if (tvbase.ty == Tarray)
769             goto Lneed;
770         if (tvbase.ty == Taarray)
771             goto Lneed;
772         if (tvbase.ty == Tclass)
773             goto Lneed;
774     }
775 Ldontneed:
776     //printf("\tdontneed\n");
777     return false;
778 Lneed:
779     //printf("\tneed\n");
780     return true;
781 }
782 
783 /******************************************
784  * Build __xtoHash for non-bitwise hashing
785  *      static hash_t xtoHash(ref const S p) nothrow @trusted;
786  */
787 FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
788 {
789     if (Dsymbol s = search_function(sd, Id.tohash))
790     {
791         __gshared TypeFunction tftohash;
792         if (!tftohash)
793         {
794             tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d);
795             tftohash.mod = MODFlags.const_;
796             tftohash = cast(TypeFunction)tftohash.merge();
797         }
798         if (FuncDeclaration fd = s.isFuncDeclaration())
799         {
800             fd = fd.overloadExactMatch(tftohash);
801             if (fd)
802                 return fd;
803         }
804     }
805     if (!needToHash(sd))
806         return null;
807 
808     /* The trouble is that the following code relies on .tupleof, but .tupleof
809      * is not allowed for C files. If we allow it for C files, then that turns on
810      * the other D properties, too, such as .dup which will then conflict with allowed
811      * field names.
812      * One way to fix it is to replace the following foreach and .tupleof with C
813      * statements and expressions.
814      * But, it's debatable whether C structs should even need toHash().
815      * Note that it would only be necessary if it has floating point fields.
816      * For now, we'll just not generate a toHash() for C files.
817      */
818     if (sc.flags & SCOPE.Cfile)
819         return null;
820 
821     //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
822     Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
823     Loc loc; // internal code should have no loc to prevent coverage
824     auto parameters = new Parameters();
825     parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
826     auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted);
827     Identifier id = Id.xtoHash;
828     auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
829     fop.isGenerated = true;
830 
831     /* Do memberwise hashing.
832      *
833      * If sd is a nested struct, and if it's nested in a class, the calculated
834      * hash value will also contain the result of parent class's toHash().
835      */
836     const(char)[] code =
837         ".object.size_t h = 0;" ~
838         "foreach (i, T; typeof(p.tupleof))" ~
839         // workaround https://issues.dlang.org/show_bug.cgi?id=17968
840         "    static if(is(T* : const(.object.Object)*)) " ~
841         "        h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
842         "    else " ~
843         "        h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
844         "return h;";
845     fop.fbody = new MixinStatement(loc, new StringExp(loc, code));
846     Scope* sc2 = sc.push();
847     sc2.stc = 0;
848     sc2.linkage = LINK.d;
849     fop.dsymbolSemantic(sc2);
850     fop.semantic2(sc2);
851     sc2.pop();
852 
853     //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars());
854     return fop;
855 }
856 
857 /*****************************************
858  * Create aggregate destructor for struct/class by aggregating
859  * all the destructors in userDtors[] with the destructors for
860  * all the members.
861  * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
862  * Params:
863  *      ad = struct or class to build destructor for
864  *      sc = context
865  * Note:
866  * Close similarity with StructDeclaration::buildPostBlit(),
867  * and the ordering changes (runs backward instead of forwards).
868  */
869 void buildDtors(AggregateDeclaration ad, Scope* sc)
870 {
871     //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
872     if (ad.isUnionDeclaration())
873         return;                    // unions don't have destructors
874 
875     StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
876     Loc declLoc = ad.userDtors.length ? ad.userDtors[0].loc : ad.loc;
877     Loc loc; // internal code should have no loc to prevent coverage
878     FuncDeclaration xdtor_fwd = null;
879 
880     // Build the field destructor (`ad.fieldDtor`), if needed.
881     // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building.
882     const bool dtorIsCppPrototype = ad.userDtors.length && ad.userDtors[0]._linkage == LINK.cpp && !ad.userDtors[0].fbody;
883     if (!dtorIsCppPrototype)
884     {
885         Expression e = null;
886         for (size_t i = 0; i < ad.fields.length; i++)
887         {
888             auto v = ad.fields[i];
889             if (v.storage_class & STC.ref_)
890                 continue;
891             if (v.overlapped)
892                 continue;
893             auto tv = v.type.baseElemOf();
894             if (tv.ty != Tstruct)
895                 continue;
896             auto sdv = (cast(TypeStruct)tv).sym;
897             if (!sdv.dtor)
898                 continue;
899 
900             // fix: https://issues.dlang.org/show_bug.cgi?id=17257
901             // braces for shrink wrapping scope of a
902             {
903                 xdtor_fwd = sdv.dtor; // this dtor is temporary it could be anything
904                 auto a = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor_fwd);
905                 a.addMember(sc, ad); // temporarily add to symbol table
906             }
907 
908             sdv.dtor.functionSemantic();
909 
910             stc = mergeFuncAttrs(stc, sdv.dtor);
911             if (stc & STC.disable)
912             {
913                 e = null;
914                 break;
915             }
916 
917             Expression ex;
918             tv = v.type.toBasetype();
919             if (tv.ty == Tstruct)
920             {
921                 // this.v.__xdtor()
922 
923                 ex = new ThisExp(loc);
924                 ex = new DotVarExp(loc, ex, v);
925 
926                 // This is a hack so we can call destructors on const/immutable objects.
927                 // Do it as a type 'paint', `cast()`
928                 ex = new CastExp(loc, ex, MODFlags.none);
929                 if (stc & STC.safe)
930                     stc = (stc & ~STC.safe) | STC.trusted;
931 
932                 ex = new DotVarExp(loc, ex, sdv.dtor, false);
933                 ex = new CallExp(loc, ex);
934             }
935             else
936             {
937                 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
938 
939                 const n = tv.numberOfElems(loc);
940                 if (n == 0)
941                     continue;
942 
943                 ex = new ThisExp(loc);
944                 ex = new DotVarExp(loc, ex, v);
945 
946                 // This is a hack so we can call destructors on const/immutable objects.
947                 ex = new DotIdExp(loc, ex, Id.ptr);
948                 ex = new CastExp(loc, ex, sdv.type.pointerTo());
949                 if (stc & STC.safe)
950                     stc = (stc & ~STC.safe) | STC.trusted;
951 
952                 SliceExp se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
953                                            new IntegerExp(loc, n, Type.tsize_t));
954                 // Prevent redundant bounds check
955                 se.upperIsInBounds = true;
956                 se.lowerIsLessThanUpper = true;
957 
958                 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se);
959             }
960             e = Expression.combine(ex, e); // combine in reverse order
961         }
962 
963         if (e || (stc & STC.disable))
964         {
965             //printf("Building __fieldDtor(), %s\n", e.toChars());
966             auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor);
967             dd.isGenerated = true;
968             dd.storage_class |= STC.inference;
969             dd.fbody = new ExpStatement(loc, e);
970             ad.members.push(dd);
971             dd.dsymbolSemantic(sc);
972             ad.fieldDtor = dd;
973         }
974     }
975 
976     // Generate list of dtors to call in that order
977     DtorDeclarations dtors;
978     foreach_reverse (userDtor; ad.userDtors[])
979         dtors.push(userDtor);
980     if (ad.fieldDtor)
981         dtors.push(ad.fieldDtor);
982     if (!dtorIsCppPrototype)
983     {
984         // extern(C++) destructors call into super to destruct the full hierarchy
985         ClassDeclaration cldec = ad.isClassDeclaration();
986         if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor)
987             dtors.push(cldec.baseClass.aggrDtor);
988     }
989 
990     // Set/build `ad.aggrDtor`
991     switch (dtors.length)
992     {
993     case 0:
994         break;
995 
996     case 1:
997         // Use the single existing dtor directly as aggregate dtor.
998         // Note that this might be `cldec.baseClass.aggrDtor`.
999         ad.aggrDtor = dtors[0];
1000         break;
1001 
1002     default:
1003         // Build the aggregate destructor, calling all dtors in order.
1004         assert(!dtorIsCppPrototype);
1005         Expression e = null;
1006         e = null;
1007         stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1008         foreach (FuncDeclaration fd; dtors)
1009         {
1010             stc = mergeFuncAttrs(stc, fd);
1011             if (stc & STC.disable)
1012             {
1013                 e = null;
1014                 break;
1015             }
1016             Expression ex = new ThisExp(loc);
1017             ex = new DotVarExp(loc, ex, fd, false);
1018             CallExp ce = new CallExp(loc, ex);
1019             ce.directcall = true;
1020             e = Expression.combine(e, ce);
1021         }
1022         auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor);
1023         dd.isGenerated = true;
1024         dd.storage_class |= STC.inference;
1025         dd.fbody = new ExpStatement(loc, e);
1026         ad.members.push(dd);
1027         dd.dsymbolSemantic(sc);
1028         ad.aggrDtor = dd;
1029         break;
1030     }
1031 
1032     // Set/build `ad.dtor`.
1033     // On Windows, the dtor in the vtable is a shim with different signature.
1034     ad.dtor = (ad.aggrDtor && ad.aggrDtor._linkage == LINK.cpp && !target.cpp.twoDtorInVtable)
1035         ? buildWindowsCppDtor(ad, ad.aggrDtor, sc)
1036         : ad.aggrDtor;
1037 
1038     // Add an __xdtor alias to make `ad.dtor` accessible
1039     if (ad.dtor)
1040     {
1041         auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, ad.dtor);
1042         _alias.dsymbolSemantic(sc);
1043         ad.members.push(_alias);
1044         if (xdtor_fwd)
1045             ad.symtab.update(_alias); // update forward dtor to correct one
1046         else
1047             _alias.addMember(sc, ad); // add to symbol table
1048     }
1049 
1050     // Set/build `ad.tidtor`
1051     ad.tidtor = buildExternDDtor(ad, sc);
1052 }
1053 
1054 /**
1055  * build a shim function around the compound dtor that accepts an argument
1056  *  that is used to implement the deleting C++ destructor
1057  *
1058  * Params:
1059  *  ad = the aggregate that contains the destructor to wrap
1060  *  dtor = the destructor to wrap
1061  *  sc = the scope in which to analyze the new function
1062  *
1063  * Returns:
1064  *  the shim destructor, semantically analyzed and added to the class as a member
1065  */
1066 private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclaration dtor, Scope* sc)
1067 {
1068     auto cldec = ad.isClassDeclaration();
1069     if (!cldec || cldec.cppDtorVtblIndex == -1) // scalar deleting dtor not built for non-virtual dtors
1070         return dtor;    // perhaps also do this if STC.scope_ is set
1071 
1072     // generate deleting C++ destructor corresponding to:
1073     // void* C::~C(int del)
1074     // {
1075     //   this->~C();
1076     //   // TODO: if (del) delete (char*)this;
1077     //   return (void*) this;
1078     // }
1079     Parameter delparam = new Parameter(Loc.initial, STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null);
1080     Parameters* params = new Parameters;
1081     params.push(delparam);
1082     const stc = dtor.storage_class & ~STC.scope_; // because we add the `return this;` later
1083     auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, stc);
1084     auto func = new DtorDeclaration(dtor.loc, dtor.loc, stc, Id.cppdtor);
1085     func.type = ftype;
1086 
1087     // Always generate the function with body, because it is not exported from DLLs.
1088     const loc = dtor.loc;
1089     auto stmts = new Statements;
1090     auto call = new CallExp(loc, dtor, null);
1091     call.directcall = true;
1092     stmts.push(new ExpStatement(loc, call));
1093     stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
1094     func.fbody = new CompoundStatement(loc, stmts);
1095     func.isGenerated = true;
1096 
1097     auto sc2 = sc.push();
1098     sc2.stc &= ~STC.static_; // not a static destructor
1099     sc2.linkage = LINK.cpp;
1100 
1101     ad.members.push(func);
1102     func.addMember(sc2, ad);
1103     func.dsymbolSemantic(sc2);
1104 
1105     sc2.pop();
1106     return func;
1107 }
1108 
1109 /**
1110  * build a shim function around the aggregate dtor that translates
1111  *  a C++ destructor to a destructor with extern(D) calling convention
1112  *
1113  * Params:
1114  *  ad = the aggregate that contains the destructor to wrap
1115  *  sc = the scope in which to analyze the new function
1116  *
1117  * Returns:
1118  *  the shim destructor, semantically analyzed and added to the class as a member
1119  */
1120 private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc)
1121 {
1122     auto dtor = ad.aggrDtor;
1123     if (!dtor)
1124         return null;
1125 
1126     // Don't try to call `@disable`d dtors
1127     if (dtor.storage_class & STC.disable)
1128         return null;
1129 
1130     // Generate shim only when ABI incompatible on target platform
1131     if (dtor._linkage != LINK.cpp || !target.cpp.wrapDtorInExternD)
1132         return dtor;
1133 
1134     // generate member function that adjusts calling convention
1135     // (EAX used for 'this' instead of ECX on Windows/stack on others):
1136     // extern(D) void __ticppdtor()
1137     // {
1138     //     Class.__dtor();
1139     // }
1140     auto ftype = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dtor.storage_class);
1141     auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.ticppdtor);
1142     func.type = ftype;
1143 
1144     auto call = new CallExp(dtor.loc, dtor, null);
1145     call.directcall = true;                   // non-virtual call Class.__dtor();
1146     func.fbody = new ExpStatement(dtor.loc, call);
1147     func.isGenerated = true;
1148     func.storage_class |= STC.inference;
1149 
1150     auto sc2 = sc.push();
1151     sc2.stc &= ~STC.static_; // not a static destructor
1152     sc2.linkage = LINK.d;
1153 
1154     ad.members.push(func);
1155     func.addMember(sc2, ad);
1156     func.dsymbolSemantic(sc2);
1157     func.functionSemantic(); // to infer attributes
1158 
1159     sc2.pop();
1160     return func;
1161 }
1162 
1163 /******************************************
1164  * Create inclusive invariant for struct/class by aggregating
1165  * all the invariants in invs[].
1166  * ---
1167  * void __invariant() const [pure nothrow @trusted]
1168  * {
1169  *     invs[0](), invs[1](), ...;
1170  * }
1171  * ---
1172  */
1173 FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc)
1174 {
1175     switch (ad.invs.length)
1176     {
1177     case 0:
1178         return null;
1179 
1180     case 1:
1181         // Don't return invs[0] so it has uniquely generated name.
1182         goto default;
1183 
1184     default:
1185         Expression e = null;
1186         StorageClass stcx = 0;
1187         StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1188         foreach (i, inv; ad.invs)
1189         {
1190             stc = mergeFuncAttrs(stc, inv);
1191             if (stc & STC.disable)
1192             {
1193                 // What should do?
1194             }
1195             const stcy = (inv.storage_class & STC.synchronized_) |
1196                          (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0);
1197             if (i == 0)
1198                 stcx = stcy;
1199             else if (stcx ^ stcy)
1200             {
1201                 version (all)
1202                 {
1203                     // currently rejects
1204                     .error(inv.loc, "%s `%s` mixing invariants with different `shared`/`synchronized` qualifiers is not supported", ad.kind(), ad.toPrettyChars());
1205                     e = null;
1206                     break;
1207                 }
1208             }
1209             e = Expression.combine(e, new CallExp(Loc.initial, new VarExp(Loc.initial, inv, false)));
1210         }
1211         auto inv = new InvariantDeclaration(ad.loc, Loc.initial, stc | stcx,
1212                 Id.classInvariant, new ExpStatement(Loc.initial, e));
1213         ad.members.push(inv);
1214         inv.dsymbolSemantic(sc);
1215         return inv;
1216     }
1217 }
1218 
1219 /*****************************************
1220  * Create inclusive postblit for struct by aggregating
1221  * all the postblits in postblits[] with the postblits for
1222  * all the members.
1223  * Note the close similarity with AggregateDeclaration::buildDtor(),
1224  * and the ordering changes (runs forward instead of backwards).
1225  */
1226 FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
1227 {
1228     //printf("buildPostBlit() %s\n", sd.toChars());
1229     if (sd.isUnionDeclaration())
1230         return null;
1231 
1232     const hasUserDefinedPosblit = sd.postblits.length && !sd.postblits[0].isDisabled ? true : false;
1233 
1234     // by default, the storage class of the created postblit
1235     StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1236     Loc declLoc = sd.postblits.length ? sd.postblits[0].loc : sd.loc;
1237     Loc loc; // internal code should have no loc to prevent coverage
1238 
1239     // if any of the postblits are disabled, then the generated postblit
1240     // will be disabled
1241     foreach (postblit; sd.postblits)
1242         stc |= postblit.storage_class & STC.disable;
1243 
1244     VarDeclaration[] fieldsToDestroy;
1245     auto postblitCalls = new Statements();
1246     // iterate through all the struct fields that are not disabled
1247     for (size_t i = 0; i < sd.fields.length && !(stc & STC.disable); i++)
1248     {
1249         auto structField = sd.fields[i];
1250         if (structField.storage_class & STC.ref_)
1251             continue;
1252         if (structField.overlapped)
1253             continue;
1254         // if it's a struct declaration or an array of structs
1255         Type tv = structField.type.baseElemOf();
1256         if (tv.ty != Tstruct)
1257             continue;
1258         auto sdv = (cast(TypeStruct)tv).sym;
1259         // which has a postblit declaration
1260         if (!sdv.postblit)
1261             continue;
1262         assert(!sdv.isUnionDeclaration());
1263 
1264         // if this field's postblit is not `nothrow`, add a `scope(failure)`
1265         // block to destroy any prior successfully postblitted fields should
1266         // this field's postblit fail.
1267         // Don't generate it for betterC code since it cannot throw exceptions.
1268         if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && global.params.useExceptions)
1269         {
1270              // create a list of destructors that need to be called
1271             Expression[] dtorCalls;
1272             foreach(sf; fieldsToDestroy)
1273             {
1274                 Expression ex;
1275                 tv = sf.type.toBasetype();
1276                 if (tv.ty == Tstruct)
1277                 {
1278                     // this.v.__xdtor()
1279 
1280                     ex = new ThisExp(loc);
1281                     ex = new DotVarExp(loc, ex, sf);
1282 
1283                     // This is a hack so we can call destructors on const/immutable objects.
1284                     ex = new AddrExp(loc, ex);
1285                     ex = new CastExp(loc, ex, sf.type.mutableOf().pointerTo());
1286                     ex = new PtrExp(loc, ex);
1287                     if (stc & STC.safe)
1288                         stc = (stc & ~STC.safe) | STC.trusted;
1289 
1290                     auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym;
1291 
1292                     ex = new DotVarExp(loc, ex, sfv.dtor, false);
1293                     ex = new CallExp(loc, ex);
1294 
1295                     dtorCalls ~= ex;
1296                 }
1297                 else
1298                 {
1299                     // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1300 
1301                     const length = tv.numberOfElems(loc);
1302 
1303                     ex = new ThisExp(loc);
1304                     ex = new DotVarExp(loc, ex, sf);
1305 
1306                     // This is a hack so we can call destructors on const/immutable objects.
1307                     ex = new DotIdExp(loc, ex, Id.ptr);
1308                     ex = new CastExp(loc, ex, sdv.type.pointerTo());
1309                     if (stc & STC.safe)
1310                         stc = (stc & ~STC.safe) | STC.trusted;
1311 
1312                     auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
1313                                                     new IntegerExp(loc, length, Type.tsize_t));
1314                     // Prevent redundant bounds check
1315                     se.upperIsInBounds = true;
1316                     se.lowerIsLessThanUpper = true;
1317 
1318                     ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se);
1319 
1320                     dtorCalls ~= ex;
1321                 }
1322             }
1323             fieldsToDestroy = [];
1324 
1325             // aggregate the destructor calls
1326             auto dtors = new Statements();
1327             foreach_reverse(dc; dtorCalls)
1328             {
1329                 dtors.push(new ExpStatement(loc, dc));
1330             }
1331 
1332             // put destructor calls in a `scope(failure)` block
1333             postblitCalls.push(new ScopeGuardStatement(loc, TOK.onScopeFailure, new CompoundStatement(loc, dtors)));
1334         }
1335 
1336         // perform semantic on the member postblit in order to
1337         // be able to aggregate it later on with the rest of the
1338         // postblits
1339         sdv.postblit.functionSemantic();
1340 
1341         stc = mergeFuncAttrs(stc, sdv.postblit);
1342         stc = mergeFuncAttrs(stc, sdv.dtor);
1343 
1344         // if any of the struct member fields has disabled
1345         // its postblit, then `sd` is not copyable, so no
1346         // postblit is generated
1347         if (stc & STC.disable)
1348         {
1349             postblitCalls.setDim(0);
1350             break;
1351         }
1352 
1353         Expression ex;
1354         tv = structField.type.toBasetype();
1355         if (tv.ty == Tstruct)
1356         {
1357             // this.v.__xpostblit()
1358 
1359             ex = new ThisExp(loc);
1360             ex = new DotVarExp(loc, ex, structField);
1361 
1362             // This is a hack so we can call postblits on const/immutable objects.
1363             ex = new AddrExp(loc, ex);
1364             ex = new CastExp(loc, ex, structField.type.mutableOf().pointerTo());
1365             ex = new PtrExp(loc, ex);
1366             if (stc & STC.safe)
1367                 stc = (stc & ~STC.safe) | STC.trusted;
1368 
1369             ex = new DotVarExp(loc, ex, sdv.postblit, false);
1370             ex = new CallExp(loc, ex);
1371         }
1372         else
1373         {
1374             // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
1375 
1376             const length = tv.numberOfElems(loc);
1377             if (length == 0)
1378                 continue;
1379 
1380             ex = new ThisExp(loc);
1381             ex = new DotVarExp(loc, ex, structField);
1382 
1383             // This is a hack so we can call postblits on const/immutable objects.
1384             ex = new DotIdExp(loc, ex, Id.ptr);
1385             ex = new CastExp(loc, ex, sdv.type.pointerTo());
1386             if (stc & STC.safe)
1387                 stc = (stc & ~STC.safe) | STC.trusted;
1388 
1389             auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
1390                                             new IntegerExp(loc, length, Type.tsize_t));
1391             // Prevent redundant bounds check
1392             se.upperIsInBounds = true;
1393             se.lowerIsLessThanUpper = true;
1394             ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayPostblit), se);
1395         }
1396         postblitCalls.push(new ExpStatement(loc, ex)); // combine in forward order
1397 
1398         /* https://issues.dlang.org/show_bug.cgi?id=10972
1399          * When subsequent field postblit calls fail,
1400          * this field should be destructed for Exception Safety.
1401          */
1402         if (sdv.dtor)
1403         {
1404             sdv.dtor.functionSemantic();
1405 
1406             // keep a list of fields that need to be destroyed in case
1407             // of a future postblit failure
1408             fieldsToDestroy ~= structField;
1409         }
1410     }
1411 
1412     void checkShared()
1413     {
1414         if (sd.type.isShared())
1415             stc |= STC.shared_;
1416     }
1417 
1418     // Build our own "postblit" which executes a, but only if needed.
1419     if (postblitCalls.length || (stc & STC.disable))
1420     {
1421         //printf("Building __fieldPostBlit()\n");
1422         checkShared();
1423         auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__fieldPostblit);
1424         dd.isGenerated = true;
1425         dd.storage_class |= STC.inference | STC.scope_;
1426         dd.fbody = (stc & STC.disable) ? null : new CompoundStatement(loc, postblitCalls);
1427         sd.postblits.shift(dd);
1428         sd.members.push(dd);
1429         dd.dsymbolSemantic(sc);
1430     }
1431 
1432     // create __xpostblit, which is the generated postblit
1433     FuncDeclaration xpostblit = null;
1434     switch (sd.postblits.length)
1435     {
1436     case 0:
1437         break;
1438 
1439     case 1:
1440         xpostblit = sd.postblits[0];
1441         break;
1442 
1443     default:
1444         Expression e = null;
1445         stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1446         foreach (fd; sd.postblits)
1447         {
1448             stc = mergeFuncAttrs(stc, fd);
1449             if (stc & STC.disable)
1450             {
1451                 e = null;
1452                 break;
1453             }
1454             Expression ex = new ThisExp(loc);
1455             ex = new DotVarExp(loc, ex, fd, false);
1456             ex = new CallExp(loc, ex);
1457             e = Expression.combine(e, ex);
1458         }
1459 
1460         checkShared();
1461         auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__aggrPostblit);
1462         dd.isGenerated = true;
1463         dd.storage_class |= STC.inference;
1464         dd.fbody = new ExpStatement(loc, e);
1465         sd.members.push(dd);
1466         dd.dsymbolSemantic(sc);
1467         xpostblit = dd;
1468         break;
1469     }
1470 
1471     // Add an __xpostblit alias to make the inclusive postblit accessible
1472     if (xpostblit)
1473     {
1474         auto _alias = new AliasDeclaration(Loc.initial, Id.__xpostblit, xpostblit);
1475         _alias.dsymbolSemantic(sc);
1476         sd.members.push(_alias);
1477         _alias.addMember(sc, sd); // add to symbol table
1478     }
1479 
1480     if (sd.hasCopyCtor)
1481     {
1482         // we have user defined postblit, so we prioritize it
1483         if (hasUserDefinedPosblit)
1484         {
1485             sd.hasCopyCtor = false;
1486             return xpostblit;
1487         }
1488         // we have fields with postblits, so print deprecations
1489         if (xpostblit && !xpostblit.isDisabled())
1490         {
1491             deprecation(sd.loc, "`struct %s` implicitly-generated postblit hides copy constructor.", sd.toChars);
1492             deprecationSupplemental(sd.loc, "The field postblit will have priority over the copy constructor.");
1493             deprecationSupplemental(sd.loc, "To change this, the postblit should be disabled for `struct %s`", sd.toChars());
1494             sd.hasCopyCtor = false;
1495         }
1496         else
1497             xpostblit = null;
1498     }
1499 
1500     return xpostblit;
1501 }
1502 
1503 /**
1504  * Generates a copy constructor declaration with the specified storage
1505  * class for the parameter and the function.
1506  *
1507  * Params:
1508  *  sd = the `struct` that contains the copy constructor
1509  *  paramStc = the storage class of the copy constructor parameter
1510  *  funcStc = the storage class for the copy constructor declaration
1511  *
1512  * Returns:
1513  *  The copy constructor declaration for struct `sd`.
1514  */
1515 private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc)
1516 {
1517     auto fparams = new Parameters();
1518     auto structType = sd.type;
1519     fparams.push(new Parameter(Loc.initial, paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null));
1520     ParameterList pList = ParameterList(fparams);
1521     auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_);
1522     auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true);
1523     ccd.storage_class |= funcStc;
1524     ccd.storage_class |= STC.inference;
1525     ccd.isGenerated = true;
1526     return ccd;
1527 }
1528 
1529 /**
1530  * Generates a trivial copy constructor body that simply does memberwise
1531  * initialization:
1532  *
1533  *    this.field1 = rhs.field1;
1534  *    this.field2 = rhs.field2;
1535  *    ...
1536  *
1537  * Params:
1538  *  sd = the `struct` declaration that contains the copy constructor
1539  *
1540  * Returns:
1541  *  A `CompoundStatement` containing the body of the copy constructor.
1542  */
1543 private Statement generateCopyCtorBody(StructDeclaration sd)
1544 {
1545     Loc loc;
1546     Expression e;
1547     foreach (v; sd.fields)
1548     {
1549         auto ec = new AssignExp(loc,
1550             new DotVarExp(loc, new ThisExp(loc), v),
1551             new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
1552         e = Expression.combine(e, ec);
1553         //printf("e.toChars = %s\n", e.toChars());
1554     }
1555     Statement s1 = new ExpStatement(loc, e);
1556     return new CompoundStatement(loc, s1);
1557 }
1558 
1559 /**
1560  * Determine if a copy constructor is needed for struct sd,
1561  * if the following conditions are met:
1562  *
1563  * 1. sd does not define a copy constructor
1564  * 2. at least one field of sd defines a copy constructor
1565  *
1566  * Params:
1567  *  sd = the `struct` for which the copy constructor is generated
1568  *  hasCpCtor = set to true if a copy constructor is already present
1569  *
1570  * Returns:
1571  *  `true` if one needs to be generated
1572  *  `false` otherwise
1573  */
1574 private bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
1575 {
1576     if (global.errors)
1577         return false;
1578 
1579     auto ctor = sd.search(sd.loc, Id.ctor);
1580     if (ctor)
1581     {
1582         if (ctor.isOverloadSet())
1583             return false;
1584         if (auto td = ctor.isTemplateDeclaration())
1585             ctor = td.funcroot;
1586     }
1587 
1588     CtorDeclaration cpCtor;
1589     CtorDeclaration rvalueCtor;
1590 
1591     if (!ctor)
1592         goto LcheckFields;
1593 
1594     overloadApply(ctor, (Dsymbol s)
1595     {
1596         if (s.isTemplateDeclaration())
1597             return 0;
1598         auto ctorDecl = s.isCtorDeclaration();
1599         assert(ctorDecl);
1600         if (ctorDecl.isCpCtor)
1601         {
1602             if (!cpCtor)
1603                 cpCtor = ctorDecl;
1604             return 0;
1605         }
1606 
1607         auto tf = ctorDecl.type.toTypeFunction();
1608         const dim = tf.parameterList.length;
1609         if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
1610         {
1611             auto param = tf.parameterList[0];
1612             if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
1613             {
1614                 rvalueCtor = ctorDecl;
1615             }
1616         }
1617         return 0;
1618     });
1619 
1620     if (cpCtor)
1621     {
1622         if (rvalueCtor)
1623         {
1624             .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars());
1625             errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1626             errorSupplemental(cpCtor.loc, "copy constructor defined here");
1627         }
1628         hasCpCtor = true;
1629         return false;
1630     }
1631 
1632 LcheckFields:
1633     VarDeclaration fieldWithCpCtor;
1634     // see if any struct members define a copy constructor
1635     foreach (v; sd.fields)
1636     {
1637         if (v.storage_class & STC.ref_)
1638             continue;
1639         if (v.overlapped)
1640             continue;
1641 
1642         auto ts = v.type.baseElemOf().isTypeStruct();
1643         if (!ts)
1644             continue;
1645         if (ts.sym.hasCopyCtor)
1646         {
1647             fieldWithCpCtor = v;
1648             break;
1649         }
1650     }
1651 
1652     if (fieldWithCpCtor && rvalueCtor)
1653     {
1654         .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars());
1655         errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1656         errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here");
1657         return false;
1658     }
1659     else if (!fieldWithCpCtor)
1660         return false;
1661     return true;
1662 }
1663 
1664 /**
1665  * Generates a copy constructor if needCopyCtor() returns true.
1666  * The generated copy constructor will be of the form:
1667  *   this(ref return scope inout(S) rhs) inout
1668  *   {
1669  *      this.field1 = rhs.field1;
1670  *      this.field2 = rhs.field2;
1671  *      ...
1672  *   }
1673  *
1674  * Params:
1675  *  sd = the `struct` for which the copy constructor is generated
1676  *  sc = the scope where the copy constructor is generated
1677  *
1678  * Returns:
1679  *  `true` if `struct` sd defines a copy constructor (explicitly or generated),
1680  *  `false` otherwise.
1681  */
1682 bool buildCopyCtor(StructDeclaration sd, Scope* sc)
1683 {
1684     bool hasCpCtor;
1685     if (!needCopyCtor(sd, hasCpCtor))
1686         return hasCpCtor;
1687 
1688     //printf("generating copy constructor for %s\n", sd.toChars());
1689     const MOD paramMod = MODFlags.wild;
1690     const MOD funcMod = MODFlags.wild;
1691     auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod));
1692     auto copyCtorBody = generateCopyCtorBody(sd);
1693     ccd.fbody = copyCtorBody;
1694     sd.members.push(ccd);
1695     ccd.addMember(sc, sd);
1696     const errors = global.startGagging();
1697     Scope* sc2 = sc.push();
1698     sc2.stc = 0;
1699     sc2.linkage = LINK.d;
1700     ccd.dsymbolSemantic(sc2);
1701     ccd.semantic2(sc2);
1702     ccd.semantic3(sc2);
1703     //printf("ccd semantic: %s\n", ccd.type.toChars());
1704     sc2.pop();
1705     if (global.endGagging(errors) || sd.isUnionDeclaration())
1706     {
1707         ccd.storage_class |= STC.disable;
1708         ccd.fbody = null;
1709     }
1710     return true;
1711 }