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