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