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 = 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(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 = 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                 /* const bool opEquals(ref const S s);
550                  */
551                 auto parameters = new Parameters();
552                 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null));
553                 tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d);
554                 tfeqptr.mod = MODFlags.const_;
555                 tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx);
556             }
557             fd = fd.overloadExactMatch(tfeqptr);
558             if (fd)
559                 return fd;
560         }
561     }
562     if (!sd.xerreq)
563     {
564         // object._xopEquals
565         Identifier id = Identifier.idPool("_xopEquals");
566         Expression e = new IdentifierExp(sd.loc, Id.empty);
567         e = new DotIdExp(sd.loc, e, Id.object);
568         e = new DotIdExp(sd.loc, e, id);
569         e = e.expressionSemantic(sc);
570         if (!e.isErrorExp())
571         {
572             Dsymbol s = getDsymbol(e);
573             assert(s);
574             sd.xerreq = s.isFuncDeclaration();
575         }
576     }
577     Loc declLoc; // loc is unnecessary so __xopEquals is never called directly
578     Loc loc; // loc is unnecessary so errors are gagged
579     auto parameters = new Parameters();
580     parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null));
581     auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_);
582     tf = tf.addSTC(STC.const_).toTypeFunction();
583     Identifier id = Id.xopEquals;
584     auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf);
585     fop.isGenerated = true;
586     fop.parent = sd;
587     Expression e1 = new IdentifierExp(loc, Id.This);
588     Expression e2 = new IdentifierExp(loc, Id.p);
589     Expression e = new EqualExp(EXP.equal, loc, e1, e2);
590     fop.fbody = new ReturnStatement(loc, e);
591     uint errors = global.startGagging(); // Do not report errors
592     Scope* sc2 = sc.push();
593     sc2.stc = 0;
594     sc2.linkage = LINK.d;
595     fop.dsymbolSemantic(sc2);
596     fop.semantic2(sc2);
597     sc2.pop();
598     if (global.endGagging(errors)) // if errors happened
599         fop = sd.xerreq;
600     return fop;
601 }
602 
603 /******************************************
604  * Build __xopCmp for TypeInfo_Struct
605  *      int __xopCmp(ref const S p) const
606  *      {
607  *          return this.opCmp(p);
608  *      }
609  *
610  * This is called by TypeInfo.compare(p1, p2). If the struct does not support
611  * const objects comparison, it will throw "not implemented" Error in runtime.
612  */
613 FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
614 {
615     //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
616     if (Dsymbol cmp = search_function(sd, Id.cmp))
617     {
618         if (FuncDeclaration fd = cmp.isFuncDeclaration())
619         {
620             TypeFunction tfcmpptr;
621             {
622                 Scope scx;
623                 /* const int opCmp(ref const S s);
624                  */
625                 auto parameters = new Parameters();
626                 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null));
627                 tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
628                 tfcmpptr.mod = MODFlags.const_;
629                 tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx);
630             }
631             fd = fd.overloadExactMatch(tfcmpptr);
632             if (fd)
633                 return fd;
634         }
635     }
636     else
637     {
638         version (none) // FIXME: doesn't work for recursive alias this
639         {
640             /* Check opCmp member exists.
641              * Consider 'alias this', but except opDispatch.
642              */
643             Expression e = new DsymbolExp(sd.loc, sd);
644             e = new DotIdExp(sd.loc, e, Id.cmp);
645             Scope* sc2 = sc.push();
646             e = e.trySemantic(sc2);
647             sc2.pop();
648             if (e)
649             {
650                 Dsymbol s = null;
651                 switch (e.op)
652                 {
653                 case EXP.overloadSet:
654                     s = e.isOverExp().vars;
655                     break;
656                 case EXP.scope_:
657                     s = e.isScopeExp().sds;
658                     break;
659                 case EXP.variable:
660                     s = e.isVarExp().var;
661                     break;
662                 default:
663                     break;
664                 }
665                 if (!s || s.ident != Id.cmp)
666                     e = null; // there's no valid member 'opCmp'
667             }
668             if (!e)
669                 return null; // bitwise comparison would work
670             /* Essentially, a struct which does not define opCmp is not comparable.
671              * At this time, typeid(S).compare might be correct that throwing "not implement" Error.
672              * But implementing it would break existing code, such as:
673              *
674              * struct S { int value; }  // no opCmp
675              * int[S] aa;   // Currently AA key uses bitwise comparison
676              *              // (It's default behavior of TypeInfo_Strust.compare).
677              *
678              * Not sure we should fix this inconsistency, so just keep current behavior.
679              */
680         }
681         else
682         {
683             return null;
684         }
685     }
686     if (!sd.xerrcmp)
687     {
688         // object._xopCmp
689         Identifier id = Identifier.idPool("_xopCmp");
690         Expression e = new IdentifierExp(sd.loc, Id.empty);
691         e = new DotIdExp(sd.loc, e, Id.object);
692         e = new DotIdExp(sd.loc, e, id);
693         e = e.expressionSemantic(sc);
694         if (!e.isErrorExp())
695         {
696             Dsymbol s = getDsymbol(e);
697             assert(s);
698             sd.xerrcmp = s.isFuncDeclaration();
699         }
700     }
701     Loc declLoc; // loc is unnecessary so __xopCmp is never called directly
702     Loc loc; // loc is unnecessary so errors are gagged
703     auto parameters = new Parameters();
704     parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null));
705     auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_);
706     tf = tf.addSTC(STC.const_).toTypeFunction();
707     Identifier id = Id.xopCmp;
708     auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf);
709     fop.isGenerated = true;
710     fop.parent = sd;
711     Expression e1 = new IdentifierExp(loc, Id.This);
712     Expression e2 = new IdentifierExp(loc, Id.p);
713     Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2);
714     fop.fbody = new ReturnStatement(loc, e);
715     uint errors = global.startGagging(); // Do not report errors
716     Scope* sc2 = sc.push();
717     sc2.stc = 0;
718     sc2.linkage = LINK.d;
719     fop.dsymbolSemantic(sc2);
720     fop.semantic2(sc2);
721     sc2.pop();
722     if (global.endGagging(errors)) // if errors happened
723         fop = sd.xerrcmp;
724     return fop;
725 }
726 
727 /*******************************************
728  * We need a toHash for the struct if
729  * any fields has a toHash.
730  * Generate one if a user-specified one does not exist.
731  */
732 private bool needToHash(StructDeclaration sd)
733 {
734     //printf("StructDeclaration::needToHash() %s\n", sd.toChars());
735     if (sd.isUnionDeclaration())
736         goto Ldontneed;
737     if (sd.xhash)
738         goto Lneed;
739 
740     /* If any of the fields has an toHash, then we
741      * need it too.
742      */
743     foreach (VarDeclaration v; sd.fields)
744     {
745         if (v.storage_class & STC.ref_)
746             continue;
747         if (v.overlapped)
748             continue;
749         Type tv = v.type.toBasetype();
750         auto tvbase = tv.baseElemOf();
751         if (tvbase.ty == Tstruct)
752         {
753             TypeStruct ts = cast(TypeStruct)tvbase;
754             if (ts.sym.isUnionDeclaration())
755                 continue;
756             if (needToHash(ts.sym))
757                 goto Lneed;
758         }
759         if (tvbase.isfloating())
760         {
761             /* This is necessary because comparison of +0.0 and -0.0 should be true,
762              * i.e. not a bit compare.
763              */
764             goto Lneed;
765         }
766         if (tvbase.ty == Tarray)
767             goto Lneed;
768         if (tvbase.ty == Taarray)
769             goto Lneed;
770         if (tvbase.ty == Tclass)
771             goto Lneed;
772     }
773 Ldontneed:
774     //printf("\tdontneed\n");
775     return false;
776 Lneed:
777     //printf("\tneed\n");
778     return true;
779 }
780 
781 /******************************************
782  * Build __xtoHash for non-bitwise hashing
783  *      static hash_t xtoHash(ref const S p) nothrow @trusted;
784  */
785 FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
786 {
787     if (Dsymbol s = search_function(sd, Id.tohash))
788     {
789         __gshared TypeFunction tftohash;
790         if (!tftohash)
791         {
792             tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d);
793             tftohash.mod = MODFlags.const_;
794             tftohash = cast(TypeFunction)tftohash.merge();
795         }
796         if (FuncDeclaration fd = s.isFuncDeclaration())
797         {
798             fd = fd.overloadExactMatch(tftohash);
799             if (fd)
800                 return fd;
801         }
802     }
803     if (!needToHash(sd))
804         return null;
805 
806     /* The trouble is that the following code relies on .tupleof, but .tupleof
807      * is not allowed for C files. If we allow it for C files, then that turns on
808      * the other D properties, too, such as .dup which will then conflict with allowed
809      * field names.
810      * One way to fix it is to replace the following foreach and .tupleof with C
811      * statements and expressions.
812      * But, it's debatable whether C structs should even need toHash().
813      * Note that it would only be necessary if it has floating point fields.
814      * For now, we'll just not generate a toHash() for C files.
815      */
816     if (sc.flags & SCOPE.Cfile)
817         return null;
818 
819     //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
820     Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
821     Loc loc; // internal code should have no loc to prevent coverage
822     auto parameters = new Parameters();
823     parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null));
824     auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted);
825     Identifier id = Id.xtoHash;
826     auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
827     fop.isGenerated = true;
828 
829     /* Do memberwise hashing.
830      *
831      * If sd is a nested struct, and if it's nested in a class, the calculated
832      * hash value will also contain the result of parent class's toHash().
833      */
834     const(char)[] code =
835         ".object.size_t h = 0;" ~
836         "foreach (i, T; typeof(p.tupleof))" ~
837         // workaround https://issues.dlang.org/show_bug.cgi?id=17968
838         "    static if(is(T* : const(.object.Object)*)) " ~
839         "        h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
840         "    else " ~
841         "        h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
842         "return h;";
843     fop.fbody = new MixinStatement(loc, new StringExp(loc, code));
844     Scope* sc2 = sc.push();
845     sc2.stc = 0;
846     sc2.linkage = LINK.d;
847     fop.dsymbolSemantic(sc2);
848     fop.semantic2(sc2);
849     sc2.pop();
850 
851     //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars());
852     return fop;
853 }
854 
855 /*****************************************
856  * Create aggregate destructor for struct/class by aggregating
857  * all the destructors in userDtors[] with the destructors for
858  * all the members.
859  * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
860  * Params:
861  *      ad = struct or class to build destructor for
862  *      sc = context
863  * Note:
864  * Close similarity with StructDeclaration::buildPostBlit(),
865  * and the ordering changes (runs backward instead of forwards).
866  */
867 void buildDtors(AggregateDeclaration ad, Scope* sc)
868 {
869     //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
870     if (ad.isUnionDeclaration())
871         return;                    // unions don't have destructors
872 
873     StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
874     Loc declLoc = ad.userDtors.length ? ad.userDtors[0].loc : ad.loc;
875     Loc loc; // internal code should have no loc to prevent coverage
876     FuncDeclaration xdtor_fwd = null;
877 
878     // Build the field destructor (`ad.fieldDtor`), if needed.
879     // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building.
880     const bool dtorIsCppPrototype = ad.userDtors.length && ad.userDtors[0]._linkage == LINK.cpp && !ad.userDtors[0].fbody;
881     if (!dtorIsCppPrototype)
882     {
883         Expression e = null;
884         for (size_t i = 0; i < ad.fields.length; i++)
885         {
886             auto v = ad.fields[i];
887             if (v.storage_class & STC.ref_)
888                 continue;
889             if (v.overlapped)
890                 continue;
891             auto tv = v.type.baseElemOf();
892             if (tv.ty != Tstruct)
893                 continue;
894             auto sdv = (cast(TypeStruct)tv).sym;
895             if (!sdv.dtor)
896                 continue;
897 
898             // fix: https://issues.dlang.org/show_bug.cgi?id=17257
899             // braces for shrink wrapping scope of a
900             {
901                 xdtor_fwd = sdv.dtor; // this dtor is temporary it could be anything
902                 auto a = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor_fwd);
903                 a.addMember(sc, ad); // temporarily add to symbol table
904             }
905 
906             sdv.dtor.functionSemantic();
907 
908             stc = mergeFuncAttrs(stc, sdv.dtor);
909             if (stc & STC.disable)
910             {
911                 e = null;
912                 break;
913             }
914 
915             Expression ex;
916             tv = v.type.toBasetype();
917             if (tv.ty == Tstruct)
918             {
919                 // this.v.__xdtor()
920 
921                 ex = new ThisExp(loc);
922                 ex = new DotVarExp(loc, ex, v);
923 
924                 // This is a hack so we can call destructors on const/immutable objects.
925                 // Do it as a type 'paint', `cast()`
926                 ex = new CastExp(loc, ex, MODFlags.none);
927                 if (stc & STC.safe)
928                     stc = (stc & ~STC.safe) | STC.trusted;
929 
930                 ex = new DotVarExp(loc, ex, sdv.dtor, false);
931                 ex = new CallExp(loc, ex);
932             }
933             else
934             {
935                 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
936 
937                 const n = tv.numberOfElems(loc);
938                 if (n == 0)
939                     continue;
940 
941                 ex = new ThisExp(loc);
942                 ex = new DotVarExp(loc, ex, v);
943 
944                 // This is a hack so we can call destructors on const/immutable objects.
945                 ex = new DotIdExp(loc, ex, Id.ptr);
946                 ex = new CastExp(loc, ex, sdv.type.pointerTo());
947                 if (stc & STC.safe)
948                     stc = (stc & ~STC.safe) | STC.trusted;
949 
950                 SliceExp se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
951                                            new IntegerExp(loc, n, Type.tsize_t));
952                 // Prevent redundant bounds check
953                 se.upperIsInBounds = true;
954                 se.lowerIsLessThanUpper = true;
955 
956                 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se);
957             }
958             e = Expression.combine(ex, e); // combine in reverse order
959         }
960 
961         if (e || (stc & STC.disable))
962         {
963             //printf("Building __fieldDtor(), %s\n", e.toChars());
964             auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor);
965             dd.isGenerated = true;
966             dd.storage_class |= STC.inference;
967             dd.fbody = new ExpStatement(loc, e);
968             ad.members.push(dd);
969             dd.dsymbolSemantic(sc);
970             ad.fieldDtor = dd;
971         }
972     }
973 
974     // Generate list of dtors to call in that order
975     DtorDeclarations dtors;
976     foreach_reverse (userDtor; ad.userDtors[])
977         dtors.push(userDtor);
978     if (ad.fieldDtor)
979         dtors.push(ad.fieldDtor);
980     if (!dtorIsCppPrototype)
981     {
982         // extern(C++) destructors call into super to destruct the full hierarchy
983         ClassDeclaration cldec = ad.isClassDeclaration();
984         if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor)
985             dtors.push(cldec.baseClass.aggrDtor);
986     }
987 
988     // Set/build `ad.aggrDtor`
989     switch (dtors.length)
990     {
991     case 0:
992         break;
993 
994     case 1:
995         // Use the single existing dtor directly as aggregate dtor.
996         // Note that this might be `cldec.baseClass.aggrDtor`.
997         ad.aggrDtor = dtors[0];
998         break;
999 
1000     default:
1001         // Build the aggregate destructor, calling all dtors in order.
1002         assert(!dtorIsCppPrototype);
1003         Expression e = null;
1004         e = null;
1005         stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1006         foreach (FuncDeclaration fd; dtors)
1007         {
1008             stc = mergeFuncAttrs(stc, fd);
1009             if (stc & STC.disable)
1010             {
1011                 e = null;
1012                 break;
1013             }
1014             Expression ex = new ThisExp(loc);
1015             ex = new DotVarExp(loc, ex, fd, false);
1016             CallExp ce = new CallExp(loc, ex);
1017             ce.directcall = true;
1018             e = Expression.combine(e, ce);
1019         }
1020         auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor);
1021         dd.isGenerated = true;
1022         dd.storage_class |= STC.inference;
1023         dd.fbody = new ExpStatement(loc, e);
1024         ad.members.push(dd);
1025         dd.dsymbolSemantic(sc);
1026         ad.aggrDtor = dd;
1027         break;
1028     }
1029 
1030     // Set/build `ad.dtor`.
1031     // On Windows, the dtor in the vtable is a shim with different signature.
1032     ad.dtor = (ad.aggrDtor && ad.aggrDtor._linkage == LINK.cpp && !target.cpp.twoDtorInVtable)
1033         ? buildWindowsCppDtor(ad, ad.aggrDtor, sc)
1034         : ad.aggrDtor;
1035 
1036     // Add an __xdtor alias to make `ad.dtor` accessible
1037     if (ad.dtor)
1038     {
1039         auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, ad.dtor);
1040         _alias.dsymbolSemantic(sc);
1041         ad.members.push(_alias);
1042         if (xdtor_fwd)
1043             ad.symtab.update(_alias); // update forward dtor to correct one
1044         else
1045             _alias.addMember(sc, ad); // add to symbol table
1046     }
1047 
1048     // Set/build `ad.tidtor`
1049     ad.tidtor = buildExternDDtor(ad, sc);
1050 }
1051 
1052 /**
1053  * build a shim function around the compound dtor that accepts an argument
1054  *  that is used to implement the deleting C++ destructor
1055  *
1056  * Params:
1057  *  ad = the aggregate that contains the destructor to wrap
1058  *  dtor = the destructor to wrap
1059  *  sc = the scope in which to analyze the new function
1060  *
1061  * Returns:
1062  *  the shim destructor, semantically analyzed and added to the class as a member
1063  */
1064 private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclaration dtor, Scope* sc)
1065 {
1066     auto cldec = ad.isClassDeclaration();
1067     if (!cldec || cldec.cppDtorVtblIndex == -1) // scalar deleting dtor not built for non-virtual dtors
1068         return dtor;    // perhaps also do this if STC.scope_ is set
1069 
1070     // generate deleting C++ destructor corresponding to:
1071     // void* C::~C(int del)
1072     // {
1073     //   this->~C();
1074     //   // TODO: if (del) delete (char*)this;
1075     //   return (void*) this;
1076     // }
1077     Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null);
1078     Parameters* params = new Parameters;
1079     params.push(delparam);
1080     const stc = dtor.storage_class & ~STC.scope_; // because we add the `return this;` later
1081     auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, stc);
1082     auto func = new DtorDeclaration(dtor.loc, dtor.loc, stc, Id.cppdtor);
1083     func.type = ftype;
1084 
1085     // Always generate the function with body, because it is not exported from DLLs.
1086     const loc = dtor.loc;
1087     auto stmts = new Statements;
1088     auto call = new CallExp(loc, dtor, null);
1089     call.directcall = true;
1090     stmts.push(new ExpStatement(loc, call));
1091     stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
1092     func.fbody = new CompoundStatement(loc, stmts);
1093     func.isGenerated = true;
1094 
1095     auto sc2 = sc.push();
1096     sc2.stc &= ~STC.static_; // not a static destructor
1097     sc2.linkage = LINK.cpp;
1098 
1099     ad.members.push(func);
1100     func.addMember(sc2, ad);
1101     func.dsymbolSemantic(sc2);
1102 
1103     sc2.pop();
1104     return func;
1105 }
1106 
1107 /**
1108  * build a shim function around the aggregate dtor that translates
1109  *  a C++ destructor to a destructor with extern(D) calling convention
1110  *
1111  * Params:
1112  *  ad = the aggregate that contains the destructor to wrap
1113  *  sc = the scope in which to analyze the new function
1114  *
1115  * Returns:
1116  *  the shim destructor, semantically analyzed and added to the class as a member
1117  */
1118 private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc)
1119 {
1120     auto dtor = ad.aggrDtor;
1121     if (!dtor)
1122         return null;
1123 
1124     // Don't try to call `@disable`d dtors
1125     if (dtor.storage_class & STC.disable)
1126         return null;
1127 
1128     // Generate shim only when ABI incompatible on target platform
1129     if (ad.classKind != ClassKind.cpp || !target.cpp.wrapDtorInExternD)
1130         return dtor;
1131 
1132     // generate member function that adjusts calling convention
1133     // (EAX used for 'this' instead of ECX on Windows/stack on others):
1134     // extern(D) void __ticppdtor()
1135     // {
1136     //     Class.__dtor();
1137     // }
1138     auto ftype = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dtor.storage_class);
1139     auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.ticppdtor);
1140     func.type = ftype;
1141 
1142     auto call = new CallExp(dtor.loc, dtor, null);
1143     call.directcall = true;                   // non-virtual call Class.__dtor();
1144     func.fbody = new ExpStatement(dtor.loc, call);
1145     func.isGenerated = true;
1146     func.storage_class |= STC.inference;
1147 
1148     auto sc2 = sc.push();
1149     sc2.stc &= ~STC.static_; // not a static destructor
1150     sc2.linkage = LINK.d;
1151 
1152     ad.members.push(func);
1153     func.addMember(sc2, ad);
1154     func.dsymbolSemantic(sc2);
1155     func.functionSemantic(); // to infer attributes
1156 
1157     sc2.pop();
1158     return func;
1159 }
1160 
1161 /******************************************
1162  * Create inclusive invariant for struct/class by aggregating
1163  * all the invariants in invs[].
1164  * ---
1165  * void __invariant() const [pure nothrow @trusted]
1166  * {
1167  *     invs[0](), invs[1](), ...;
1168  * }
1169  * ---
1170  */
1171 FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc)
1172 {
1173     switch (ad.invs.length)
1174     {
1175     case 0:
1176         return null;
1177 
1178     case 1:
1179         // Don't return invs[0] so it has uniquely generated name.
1180         goto default;
1181 
1182     default:
1183         Expression e = null;
1184         StorageClass stcx = 0;
1185         StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1186         foreach (i, inv; ad.invs)
1187         {
1188             stc = mergeFuncAttrs(stc, inv);
1189             if (stc & STC.disable)
1190             {
1191                 // What should do?
1192             }
1193             const stcy = (inv.storage_class & STC.synchronized_) |
1194                          (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0);
1195             if (i == 0)
1196                 stcx = stcy;
1197             else if (stcx ^ stcy)
1198             {
1199                 version (all)
1200                 {
1201                     // currently rejects
1202                     ad.error(inv.loc, "mixing invariants with different `shared`/`synchronized` qualifiers is not supported");
1203                     e = null;
1204                     break;
1205                 }
1206             }
1207             e = Expression.combine(e, new CallExp(Loc.initial, new VarExp(Loc.initial, inv, false)));
1208         }
1209         auto inv = new InvariantDeclaration(ad.loc, Loc.initial, stc | stcx,
1210                 Id.classInvariant, new ExpStatement(Loc.initial, e));
1211         ad.members.push(inv);
1212         inv.dsymbolSemantic(sc);
1213         return inv;
1214     }
1215 }
1216 
1217 /*****************************************
1218  * Create inclusive postblit for struct by aggregating
1219  * all the postblits in postblits[] with the postblits for
1220  * all the members.
1221  * Note the close similarity with AggregateDeclaration::buildDtor(),
1222  * and the ordering changes (runs forward instead of backwards).
1223  */
1224 FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
1225 {
1226     //printf("buildPostBlit() %s\n", sd.toChars());
1227     if (sd.isUnionDeclaration())
1228         return null;
1229 
1230     const hasUserDefinedPosblit = sd.postblits.length && !sd.postblits[0].isDisabled ? true : false;
1231 
1232     // by default, the storage class of the created postblit
1233     StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1234     Loc declLoc = sd.postblits.length ? sd.postblits[0].loc : sd.loc;
1235     Loc loc; // internal code should have no loc to prevent coverage
1236 
1237     // if any of the postblits are disabled, then the generated postblit
1238     // will be disabled
1239     foreach (postblit; sd.postblits)
1240         stc |= postblit.storage_class & STC.disable;
1241 
1242     VarDeclaration[] fieldsToDestroy;
1243     auto postblitCalls = new Statements();
1244     // iterate through all the struct fields that are not disabled
1245     for (size_t i = 0; i < sd.fields.length && !(stc & STC.disable); i++)
1246     {
1247         auto structField = sd.fields[i];
1248         if (structField.storage_class & STC.ref_)
1249             continue;
1250         if (structField.overlapped)
1251             continue;
1252         // if it's a struct declaration or an array of structs
1253         Type tv = structField.type.baseElemOf();
1254         if (tv.ty != Tstruct)
1255             continue;
1256         auto sdv = (cast(TypeStruct)tv).sym;
1257         // which has a postblit declaration
1258         if (!sdv.postblit)
1259             continue;
1260         assert(!sdv.isUnionDeclaration());
1261 
1262         // if this field's postblit is not `nothrow`, add a `scope(failure)`
1263         // block to destroy any prior successfully postblitted fields should
1264         // this field's postblit fail.
1265         // Don't generate it for betterC code since it cannot throw exceptions.
1266         if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && !global.params.betterC)
1267         {
1268              // create a list of destructors that need to be called
1269             Expression[] dtorCalls;
1270             foreach(sf; fieldsToDestroy)
1271             {
1272                 Expression ex;
1273                 tv = sf.type.toBasetype();
1274                 if (tv.ty == Tstruct)
1275                 {
1276                     // this.v.__xdtor()
1277 
1278                     ex = new ThisExp(loc);
1279                     ex = new DotVarExp(loc, ex, sf);
1280 
1281                     // This is a hack so we can call destructors on const/immutable objects.
1282                     ex = new AddrExp(loc, ex);
1283                     ex = new CastExp(loc, ex, sf.type.mutableOf().pointerTo());
1284                     ex = new PtrExp(loc, ex);
1285                     if (stc & STC.safe)
1286                         stc = (stc & ~STC.safe) | STC.trusted;
1287 
1288                     auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym;
1289 
1290                     ex = new DotVarExp(loc, ex, sfv.dtor, false);
1291                     ex = new CallExp(loc, ex);
1292 
1293                     dtorCalls ~= ex;
1294                 }
1295                 else
1296                 {
1297                     // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1298 
1299                     const length = tv.numberOfElems(loc);
1300 
1301                     ex = new ThisExp(loc);
1302                     ex = new DotVarExp(loc, ex, sf);
1303 
1304                     // This is a hack so we can call destructors on const/immutable objects.
1305                     ex = new DotIdExp(loc, ex, Id.ptr);
1306                     ex = new CastExp(loc, ex, sdv.type.pointerTo());
1307                     if (stc & STC.safe)
1308                         stc = (stc & ~STC.safe) | STC.trusted;
1309 
1310                     auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
1311                                                     new IntegerExp(loc, length, Type.tsize_t));
1312                     // Prevent redundant bounds check
1313                     se.upperIsInBounds = true;
1314                     se.lowerIsLessThanUpper = true;
1315 
1316                     ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se);
1317 
1318                     dtorCalls ~= ex;
1319                 }
1320             }
1321             fieldsToDestroy = [];
1322 
1323             // aggregate the destructor calls
1324             auto dtors = new Statements();
1325             foreach_reverse(dc; dtorCalls)
1326             {
1327                 dtors.push(new ExpStatement(loc, dc));
1328             }
1329 
1330             // put destructor calls in a `scope(failure)` block
1331             postblitCalls.push(new ScopeGuardStatement(loc, TOK.onScopeFailure, new CompoundStatement(loc, dtors)));
1332         }
1333 
1334         // perform semantic on the member postblit in order to
1335         // be able to aggregate it later on with the rest of the
1336         // postblits
1337         sdv.postblit.functionSemantic();
1338 
1339         stc = mergeFuncAttrs(stc, sdv.postblit);
1340         stc = mergeFuncAttrs(stc, sdv.dtor);
1341 
1342         // if any of the struct member fields has disabled
1343         // its postblit, then `sd` is not copyable, so no
1344         // postblit is generated
1345         if (stc & STC.disable)
1346         {
1347             postblitCalls.setDim(0);
1348             break;
1349         }
1350 
1351         Expression ex;
1352         tv = structField.type.toBasetype();
1353         if (tv.ty == Tstruct)
1354         {
1355             // this.v.__xpostblit()
1356 
1357             ex = new ThisExp(loc);
1358             ex = new DotVarExp(loc, ex, structField);
1359 
1360             // This is a hack so we can call postblits on const/immutable objects.
1361             ex = new AddrExp(loc, ex);
1362             ex = new CastExp(loc, ex, structField.type.mutableOf().pointerTo());
1363             ex = new PtrExp(loc, ex);
1364             if (stc & STC.safe)
1365                 stc = (stc & ~STC.safe) | STC.trusted;
1366 
1367             ex = new DotVarExp(loc, ex, sdv.postblit, false);
1368             ex = new CallExp(loc, ex);
1369         }
1370         else
1371         {
1372             // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
1373 
1374             const length = tv.numberOfElems(loc);
1375             if (length == 0)
1376                 continue;
1377 
1378             ex = new ThisExp(loc);
1379             ex = new DotVarExp(loc, ex, structField);
1380 
1381             // This is a hack so we can call postblits on const/immutable objects.
1382             ex = new DotIdExp(loc, ex, Id.ptr);
1383             ex = new CastExp(loc, ex, sdv.type.pointerTo());
1384             if (stc & STC.safe)
1385                 stc = (stc & ~STC.safe) | STC.trusted;
1386 
1387             auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
1388                                             new IntegerExp(loc, length, Type.tsize_t));
1389             // Prevent redundant bounds check
1390             se.upperIsInBounds = true;
1391             se.lowerIsLessThanUpper = true;
1392             ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayPostblit), se);
1393         }
1394         postblitCalls.push(new ExpStatement(loc, ex)); // combine in forward order
1395 
1396         /* https://issues.dlang.org/show_bug.cgi?id=10972
1397          * When subsequent field postblit calls fail,
1398          * this field should be destructed for Exception Safety.
1399          */
1400         if (sdv.dtor)
1401         {
1402             sdv.dtor.functionSemantic();
1403 
1404             // keep a list of fields that need to be destroyed in case
1405             // of a future postblit failure
1406             fieldsToDestroy ~= structField;
1407         }
1408     }
1409 
1410     void checkShared()
1411     {
1412         if (sd.type.isShared())
1413             stc |= STC.shared_;
1414     }
1415 
1416     // Build our own "postblit" which executes a, but only if needed.
1417     if (postblitCalls.length || (stc & STC.disable))
1418     {
1419         //printf("Building __fieldPostBlit()\n");
1420         checkShared();
1421         auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__fieldPostblit);
1422         dd.isGenerated = true;
1423         dd.storage_class |= STC.inference | STC.scope_;
1424         dd.fbody = (stc & STC.disable) ? null : new CompoundStatement(loc, postblitCalls);
1425         sd.postblits.shift(dd);
1426         sd.members.push(dd);
1427         dd.dsymbolSemantic(sc);
1428     }
1429 
1430     // create __xpostblit, which is the generated postblit
1431     FuncDeclaration xpostblit = null;
1432     switch (sd.postblits.length)
1433     {
1434     case 0:
1435         break;
1436 
1437     case 1:
1438         xpostblit = sd.postblits[0];
1439         break;
1440 
1441     default:
1442         Expression e = null;
1443         stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1444         foreach (fd; sd.postblits)
1445         {
1446             stc = mergeFuncAttrs(stc, fd);
1447             if (stc & STC.disable)
1448             {
1449                 e = null;
1450                 break;
1451             }
1452             Expression ex = new ThisExp(loc);
1453             ex = new DotVarExp(loc, ex, fd, false);
1454             ex = new CallExp(loc, ex);
1455             e = Expression.combine(e, ex);
1456         }
1457 
1458         checkShared();
1459         auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__aggrPostblit);
1460         dd.isGenerated = true;
1461         dd.storage_class |= STC.inference;
1462         dd.fbody = new ExpStatement(loc, e);
1463         sd.members.push(dd);
1464         dd.dsymbolSemantic(sc);
1465         xpostblit = dd;
1466         break;
1467     }
1468 
1469     // Add an __xpostblit alias to make the inclusive postblit accessible
1470     if (xpostblit)
1471     {
1472         auto _alias = new AliasDeclaration(Loc.initial, Id.__xpostblit, xpostblit);
1473         _alias.dsymbolSemantic(sc);
1474         sd.members.push(_alias);
1475         _alias.addMember(sc, sd); // add to symbol table
1476     }
1477 
1478     if (sd.hasCopyCtor)
1479     {
1480         // we have user defined postblit, so we prioritize it
1481         if (hasUserDefinedPosblit)
1482         {
1483             sd.hasCopyCtor = false;
1484             return xpostblit;
1485         }
1486         // we have fields with postblits, so print deprecations
1487         if (xpostblit && !xpostblit.isDisabled())
1488         {
1489             deprecation(sd.loc, "`struct %s` implicitly-generated postblit hides copy constructor.", sd.toChars);
1490             deprecationSupplemental(sd.loc, "The field postblit will have priority over the copy constructor.");
1491             deprecationSupplemental(sd.loc, "To change this, the postblit should be disabled for `struct %s`", sd.toChars());
1492             sd.hasCopyCtor = false;
1493         }
1494         else
1495             xpostblit = null;
1496     }
1497 
1498     return xpostblit;
1499 }
1500 
1501 /**
1502  * Generates a copy constructor declaration with the specified storage
1503  * class for the parameter and the function.
1504  *
1505  * Params:
1506  *  sd = the `struct` that contains the copy constructor
1507  *  paramStc = the storage class of the copy constructor parameter
1508  *  funcStc = the storage class for the copy constructor declaration
1509  *
1510  * Returns:
1511  *  The copy constructor declaration for struct `sd`.
1512  */
1513 private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc)
1514 {
1515     auto fparams = new Parameters();
1516     auto structType = sd.type;
1517     fparams.push(new Parameter(paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null));
1518     ParameterList pList = ParameterList(fparams);
1519     auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_);
1520     auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true);
1521     ccd.storage_class |= funcStc;
1522     ccd.storage_class |= STC.inference;
1523     ccd.isGenerated = true;
1524     return ccd;
1525 }
1526 
1527 /**
1528  * Generates a trivial copy constructor body that simply does memberwise
1529  * initialization:
1530  *
1531  *    this.field1 = rhs.field1;
1532  *    this.field2 = rhs.field2;
1533  *    ...
1534  *
1535  * Params:
1536  *  sd = the `struct` declaration that contains the copy constructor
1537  *
1538  * Returns:
1539  *  A `CompoundStatement` containing the body of the copy constructor.
1540  */
1541 private Statement generateCopyCtorBody(StructDeclaration sd)
1542 {
1543     Loc loc;
1544     Expression e;
1545     foreach (v; sd.fields)
1546     {
1547         auto ec = new AssignExp(loc,
1548             new DotVarExp(loc, new ThisExp(loc), v),
1549             new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
1550         e = Expression.combine(e, ec);
1551         //printf("e.toChars = %s\n", e.toChars());
1552     }
1553     Statement s1 = new ExpStatement(loc, e);
1554     return new CompoundStatement(loc, s1);
1555 }
1556 
1557 /**
1558  * Determine if a copy constructor is needed for struct sd,
1559  * if the following conditions are met:
1560  *
1561  * 1. sd does not define a copy constructor
1562  * 2. at least one field of sd defines a copy constructor
1563  *
1564  * Params:
1565  *  sd = the `struct` for which the copy constructor is generated
1566  *  hasCpCtor = set to true if a copy constructor is already present
1567  *
1568  * Returns:
1569  *  `true` if one needs to be generated
1570  *  `false` otherwise
1571  */
1572 private bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
1573 {
1574     if (global.errors)
1575         return false;
1576 
1577     auto ctor = sd.search(sd.loc, Id.ctor);
1578     if (ctor)
1579     {
1580         if (ctor.isOverloadSet())
1581             return false;
1582         if (auto td = ctor.isTemplateDeclaration())
1583             ctor = td.funcroot;
1584     }
1585 
1586     CtorDeclaration cpCtor;
1587     CtorDeclaration rvalueCtor;
1588 
1589     if (!ctor)
1590         goto LcheckFields;
1591 
1592     overloadApply(ctor, (Dsymbol s)
1593     {
1594         if (s.isTemplateDeclaration())
1595             return 0;
1596         auto ctorDecl = s.isCtorDeclaration();
1597         assert(ctorDecl);
1598         if (ctorDecl.isCpCtor)
1599         {
1600             if (!cpCtor)
1601                 cpCtor = ctorDecl;
1602             return 0;
1603         }
1604 
1605         auto tf = ctorDecl.type.toTypeFunction();
1606         const dim = tf.parameterList.length;
1607         if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
1608         {
1609             auto param = tf.parameterList[0];
1610             if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
1611             {
1612                 rvalueCtor = ctorDecl;
1613             }
1614         }
1615         return 0;
1616     });
1617 
1618     if (cpCtor)
1619     {
1620         if (rvalueCtor)
1621         {
1622             .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars());
1623             errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1624             errorSupplemental(cpCtor.loc, "copy constructor defined here");
1625         }
1626         hasCpCtor = true;
1627         return false;
1628     }
1629 
1630 LcheckFields:
1631     VarDeclaration fieldWithCpCtor;
1632     // see if any struct members define a copy constructor
1633     foreach (v; sd.fields)
1634     {
1635         if (v.storage_class & STC.ref_)
1636             continue;
1637         if (v.overlapped)
1638             continue;
1639 
1640         auto ts = v.type.baseElemOf().isTypeStruct();
1641         if (!ts)
1642             continue;
1643         if (ts.sym.hasCopyCtor)
1644         {
1645             fieldWithCpCtor = v;
1646             break;
1647         }
1648     }
1649 
1650     if (fieldWithCpCtor && rvalueCtor)
1651     {
1652         .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars());
1653         errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1654         errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here");
1655         return false;
1656     }
1657     else if (!fieldWithCpCtor)
1658         return false;
1659     return true;
1660 }
1661 
1662 /**
1663  * Generates a copy constructor if needCopyCtor() returns true.
1664  * The generated copy constructor will be of the form:
1665  *   this(ref return scope inout(S) rhs) inout
1666  *   {
1667  *      this.field1 = rhs.field1;
1668  *      this.field2 = rhs.field2;
1669  *      ...
1670  *   }
1671  *
1672  * Params:
1673  *  sd = the `struct` for which the copy constructor is generated
1674  *  sc = the scope where the copy constructor is generated
1675  *
1676  * Returns:
1677  *  `true` if `struct` sd defines a copy constructor (explicitly or generated),
1678  *  `false` otherwise.
1679  */
1680 bool buildCopyCtor(StructDeclaration sd, Scope* sc)
1681 {
1682     bool hasCpCtor;
1683     if (!needCopyCtor(sd, hasCpCtor))
1684         return hasCpCtor;
1685 
1686     //printf("generating copy constructor for %s\n", sd.toChars());
1687     const MOD paramMod = MODFlags.wild;
1688     const MOD funcMod = MODFlags.wild;
1689     auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod));
1690     auto copyCtorBody = generateCopyCtorBody(sd);
1691     ccd.fbody = copyCtorBody;
1692     sd.members.push(ccd);
1693     ccd.addMember(sc, sd);
1694     const errors = global.startGagging();
1695     Scope* sc2 = sc.push();
1696     sc2.stc = 0;
1697     sc2.linkage = LINK.d;
1698     ccd.dsymbolSemantic(sc2);
1699     ccd.semantic2(sc2);
1700     ccd.semantic3(sc2);
1701     //printf("ccd semantic: %s\n", ccd.type.toChars());
1702     sc2.pop();
1703     if (global.endGagging(errors) || sd.isUnionDeclaration())
1704     {
1705         ccd.storage_class |= STC.disable;
1706         ccd.fbody = null;
1707     }
1708     return true;
1709 }