d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[official-gcc.git] / gcc / d / dmd / clone.d
blobc536d25f0a3b79319055bc70f4a95e1306598d14
1 /**
2 * Builds struct member functions if needed and not defined by the user.
3 * Includes `opEquals`, `opAssign`, post blit, copy constructor and destructor.
5 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 * Source: $(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
13 module dmd.clone;
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;
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
52 StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure
54 if (!f)
55 return s1;
56 StorageClass s2 = (f.storage_class & STC.disable);
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;
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;
73 const sa = s1 & s2;
74 const so = s1 | s2;
76 StorageClass stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable);
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;
87 return stc;
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
98 FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
100 Dsymbol assign = search_function(ad, Id.assign);
101 if (assign)
103 /* check identity opAssign exists
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;
115 a[0] = er;
116 auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, &a, FuncResolveFlag.quiet);
117 if (!f)
119 a[0] = el;
120 f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, &a, FuncResolveFlag.quiet);
123 sc = sc.pop();
124 global.endGagging(errors);
125 if (f)
127 if (f.errors)
128 return null;
129 auto fparams = f.getParameterList();
130 if (fparams.length)
132 auto fparam0 = fparams[0];
133 if (fparam0.type.toDsymbol(null) != ad)
134 f = null;
137 // BUGS: This detection mechanism cannot find some opAssign-s like follows:
138 // struct S { void opAssign(ref immutable S) const; }
139 return f;
141 return null;
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.
149 private bool needOpAssign(StructDeclaration sd)
151 //printf("StructDeclaration::needOpAssign() %s\n", sd.toChars());
153 static bool isNeeded()
155 //printf("\tneed\n");
156 return true;
159 if (sd.isUnionDeclaration())
160 return !isNeeded();
162 if (sd.hasIdentityAssign || // because has identity==elaborate opAssign
163 sd.dtor ||
164 sd.postblit)
165 return isNeeded();
167 /* If any of the fields need an opAssign, then we
168 * need it too.
170 foreach (v; sd.fields)
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)
179 TypeStruct ts = cast(TypeStruct)tv;
180 if (ts.sym.isUnionDeclaration())
181 continue;
182 if (needOpAssign(ts.sym))
183 return isNeeded();
186 return !isNeeded();
189 /******************************************
190 * Build opAssign for a `struct`.
192 * The generated `opAssign` function has the following signature:
193 *---
194 *ref S opAssign(S s) // S is the name of the `struct`
195 *---
197 * The opAssign function will be built for a struct `S` if the
198 * following constraints are met:
200 * 1. `S` does not have an identity `opAssign` defined.
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`.
207 * 3. `S` does not have any non-mutable fields.
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`
212 * If `S` defines a destructor, the generated code for `opAssign` is:
214 *---
215 *S __swap = void;
216 *__swap = this; // bit copy
217 *this = s; // bit copy
218 *__swap.dtor();
219 *---
221 * Otherwise, if `S` defines a postblit, the generated code for `opAssign` is:
223 *---
224 *this = s;
225 *---
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).
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:
238 *---
239 *this.field1 = s.field1;
240 *this.field2 = s.field2;
241 *...;
242 *---
244 * In this situation, the assignemnts are actual assign expressions (`opAssign` is used
245 * if defined).
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
255 FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
257 if (FuncDeclaration f = hasIdentityOpAssign(sd, sc))
259 sd.hasIdentityAssign = true;
260 return f;
262 // Even if non-identity opAssign is defined, built-in identity opAssign
263 // will be defined.
264 if (!needOpAssign(sd))
265 return null;
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
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)
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));
289 if (sd.dtor || sd.postblit)
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;
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.generated = true;
305 Expression e;
306 if (stc & STC.disable)
308 e = null;
310 /* Do swap this and rhs.
311 * __swap = this; this = s; __swap.dtor();
313 else if (sd.dtor)
315 //printf("\tswap copy\n");
316 TypeFunction tdtor = cast(TypeFunction)sd.dtor.type;
317 assert(tdtor.ty == Tfunction);
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);
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));
329 /* Instead of running the destructor on s, run it
330 * on swap. This avoids needing to copy swap back in to s.
332 auto e4 = new CallExp(loc, new DotVarExp(loc, new VarExp(loc, swap), sd.dtor, false));
334 e = Expression.combine(e1, e2, e3, e4);
336 /* postblit was called when the value was passed to opAssign, we just need to blit the result */
337 else if (sd.postblit)
339 e = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p));
340 sd.hasBlitAssign = true;
342 else
344 /* Do memberwise copy.
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.
351 //printf("\tmemberwise copy\n");
352 e = null;
353 foreach (v; sd.fields)
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);
362 if (e)
364 Statement s1 = new ExpStatement(loc, e);
365 /* Add:
366 * return this;
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;
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.
385 sc2.pop();
386 if (global.endGagging(errors)) // if errors happened
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
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;
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.
403 bool needOpEquals(StructDeclaration sd)
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.
413 foreach (VarDeclaration v; sd.fields)
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)
423 TypeStruct ts = cast(TypeStruct)tvbase;
424 if (ts.sym.isUnionDeclaration())
425 continue;
426 if (needOpEquals(ts.sym))
427 goto Lneed;
429 if (tvbase.isfloating())
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;
436 if (tvbase.ty == Tarray)
437 goto Lneed;
438 if (tvbase.ty == Taarray)
439 goto Lneed;
440 if (tvbase.ty == Tclass)
441 goto Lneed;
443 Ldontneed:
444 //printf("\tdontneed\n");
445 return false;
446 Lneed:
447 //printf("\tneed\n");
448 return true;
451 /*******************************************
452 * Check given aggregate actually has an identity opEquals or not.
454 private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc)
456 FuncDeclaration f;
457 if (Dsymbol eq = search_function(ad, Id.eq))
459 /* check identity opEquals exists
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);
466 bool hasIt(Type tthis)
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;
473 FuncDeclaration rfc(Expression e)
475 a[0] = e;
476 a[0].type = tthis;
477 return resolveFuncCall(ad.loc, sc, eq, null, tthis, &a, FuncResolveFlag.quiet);
480 f = rfc(er);
481 if (!f)
482 f = rfc(el);
484 sc = sc.pop();
485 global.endGagging(errors);
487 return f !is null;
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()))
496 if (f.errors)
497 return null;
500 return f;
503 /******************************************
504 * Build opEquals for struct.
505 * const bool opEquals(const S s) { ... }
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.
513 FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc)
515 if (hasIdentityOpEquals(sd, sc))
517 sd.hasIdentityEquals = true;
519 return null;
522 /******************************************
523 * Build __xopEquals for TypeInfo_Struct
524 * static bool __xopEquals(ref const S p, ref const S q)
526 * return p == q;
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.
532 FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
534 if (!needOpEquals(sd))
535 return null; // bitwise comparison would work
537 //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars());
538 if (Dsymbol eq = search_function(sd, Id.eq))
540 if (FuncDeclaration fd = eq.isFuncDeclaration())
542 TypeFunction tfeqptr;
544 Scope scx;
545 /* const bool opEquals(ref const S s);
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);
553 fd = fd.overloadExactMatch(tfeqptr);
554 if (fd)
555 return fd;
558 if (!sd.xerreq)
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 Dsymbol s = getDsymbol(e);
567 assert(s);
568 sd.xerreq = s.isFuncDeclaration();
570 Loc declLoc; // loc is unnecessary so __xopEquals is never called directly
571 Loc loc; // loc is unnecessary so errors are gagged
572 auto parameters = new Parameters();
573 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null))
574 .push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null));
575 auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d);
576 Identifier id = Id.xopEquals;
577 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
578 fop.generated = true;
579 Expression e1 = new IdentifierExp(loc, Id.p);
580 Expression e2 = new IdentifierExp(loc, Id.q);
581 Expression e = new EqualExp(EXP.equal, loc, e1, e2);
582 fop.fbody = new ReturnStatement(loc, e);
583 uint errors = global.startGagging(); // Do not report errors
584 Scope* sc2 = sc.push();
585 sc2.stc = 0;
586 sc2.linkage = LINK.d;
587 fop.dsymbolSemantic(sc2);
588 fop.semantic2(sc2);
589 sc2.pop();
590 if (global.endGagging(errors)) // if errors happened
591 fop = sd.xerreq;
592 return fop;
595 /******************************************
596 * Build __xopCmp for TypeInfo_Struct
597 * static bool __xopCmp(ref const S p, ref const S q)
599 * return p.opCmp(q);
602 * This is called by TypeInfo.compare(p1, p2). If the struct does not support
603 * const objects comparison, it will throw "not implemented" Error in runtime.
605 FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
607 //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
608 if (Dsymbol cmp = search_function(sd, Id.cmp))
610 if (FuncDeclaration fd = cmp.isFuncDeclaration())
612 TypeFunction tfcmpptr;
614 Scope scx;
615 /* const int opCmp(ref const S s);
617 auto parameters = new Parameters();
618 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null));
619 tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
620 tfcmpptr.mod = MODFlags.const_;
621 tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx);
623 fd = fd.overloadExactMatch(tfcmpptr);
624 if (fd)
625 return fd;
628 else
630 version (none) // FIXME: doesn't work for recursive alias this
632 /* Check opCmp member exists.
633 * Consider 'alias this', but except opDispatch.
635 Expression e = new DsymbolExp(sd.loc, sd);
636 e = new DotIdExp(sd.loc, e, Id.cmp);
637 Scope* sc2 = sc.push();
638 e = e.trySemantic(sc2);
639 sc2.pop();
640 if (e)
642 Dsymbol s = null;
643 switch (e.op)
645 case EXP.overloadSet:
646 s = (cast(OverExp)e).vars;
647 break;
648 case EXP.scope_:
649 s = (cast(ScopeExp)e).sds;
650 break;
651 case EXP.variable:
652 s = (cast(VarExp)e).var;
653 break;
654 default:
655 break;
657 if (!s || s.ident != Id.cmp)
658 e = null; // there's no valid member 'opCmp'
660 if (!e)
661 return null; // bitwise comparison would work
662 /* Essentially, a struct which does not define opCmp is not comparable.
663 * At this time, typeid(S).compare might be correct that throwing "not implement" Error.
664 * But implementing it would break existing code, such as:
666 * struct S { int value; } // no opCmp
667 * int[S] aa; // Currently AA key uses bitwise comparison
668 * // (It's default behavior of TypeInfo_Strust.compare).
670 * Not sure we should fix this inconsistency, so just keep current behavior.
673 else
675 return null;
678 if (!sd.xerrcmp)
680 // object._xopCmp
681 Identifier id = Identifier.idPool("_xopCmp");
682 Expression e = new IdentifierExp(sd.loc, Id.empty);
683 e = new DotIdExp(sd.loc, e, Id.object);
684 e = new DotIdExp(sd.loc, e, id);
685 e = e.expressionSemantic(sc);
686 Dsymbol s = getDsymbol(e);
687 assert(s);
688 sd.xerrcmp = s.isFuncDeclaration();
690 Loc declLoc; // loc is unnecessary so __xopCmp is never called directly
691 Loc loc; // loc is unnecessary so errors are gagged
692 auto parameters = new Parameters();
693 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null));
694 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null));
695 auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
696 Identifier id = Id.xopCmp;
697 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
698 fop.generated = true;
699 Expression e1 = new IdentifierExp(loc, Id.p);
700 Expression e2 = new IdentifierExp(loc, Id.q);
701 version (IN_GCC)
702 Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2);
703 else
704 Expression e = new CallExp(loc, new DotIdExp(loc, e2, Id.cmp), e1);
705 fop.fbody = new ReturnStatement(loc, e);
706 uint errors = global.startGagging(); // Do not report errors
707 Scope* sc2 = sc.push();
708 sc2.stc = 0;
709 sc2.linkage = LINK.d;
710 fop.dsymbolSemantic(sc2);
711 fop.semantic2(sc2);
712 sc2.pop();
713 if (global.endGagging(errors)) // if errors happened
714 fop = sd.xerrcmp;
715 return fop;
718 /*******************************************
719 * We need a toHash for the struct if
720 * any fields has a toHash.
721 * Generate one if a user-specified one does not exist.
723 private bool needToHash(StructDeclaration sd)
725 //printf("StructDeclaration::needToHash() %s\n", sd.toChars());
726 if (sd.isUnionDeclaration())
727 goto Ldontneed;
728 if (sd.xhash)
729 goto Lneed;
731 /* If any of the fields has an toHash, then we
732 * need it too.
734 foreach (VarDeclaration v; sd.fields)
736 if (v.storage_class & STC.ref_)
737 continue;
738 if (v.overlapped)
739 continue;
740 Type tv = v.type.toBasetype();
741 auto tvbase = tv.baseElemOf();
742 if (tvbase.ty == Tstruct)
744 TypeStruct ts = cast(TypeStruct)tvbase;
745 if (ts.sym.isUnionDeclaration())
746 continue;
747 if (needToHash(ts.sym))
748 goto Lneed;
750 if (tvbase.isfloating())
752 /* This is necessary because comparison of +0.0 and -0.0 should be true,
753 * i.e. not a bit compare.
755 goto Lneed;
757 if (tvbase.ty == Tarray)
758 goto Lneed;
759 if (tvbase.ty == Taarray)
760 goto Lneed;
761 if (tvbase.ty == Tclass)
762 goto Lneed;
764 Ldontneed:
765 //printf("\tdontneed\n");
766 return false;
767 Lneed:
768 //printf("\tneed\n");
769 return true;
772 /******************************************
773 * Build __xtoHash for non-bitwise hashing
774 * static hash_t xtoHash(ref const S p) nothrow @trusted;
776 FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
778 if (Dsymbol s = search_function(sd, Id.tohash))
780 __gshared TypeFunction tftohash;
781 if (!tftohash)
783 tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d);
784 tftohash.mod = MODFlags.const_;
785 tftohash = cast(TypeFunction)tftohash.merge();
787 if (FuncDeclaration fd = s.isFuncDeclaration())
789 fd = fd.overloadExactMatch(tftohash);
790 if (fd)
791 return fd;
794 if (!needToHash(sd))
795 return null;
797 /* The trouble is that the following code relies on .tupleof, but .tupleof
798 * is not allowed for C files. If we allow it for C files, then that turns on
799 * the other D properties, too, such as .dup which will then conflict with allowed
800 * field names.
801 * One way to fix it is to replace the following foreach and .tupleof with C
802 * statements and expressions.
803 * But, it's debatable whether C structs should even need toHash().
804 * Note that it would only be necessary if it has floating point fields.
805 * For now, we'll just not generate a toHash() for C files.
807 if (sc.flags & SCOPE.Cfile)
808 return null;
810 //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
811 Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
812 Loc loc; // internal code should have no loc to prevent coverage
813 auto parameters = new Parameters();
814 parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null));
815 auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted);
816 Identifier id = Id.xtoHash;
817 auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
818 fop.generated = true;
820 /* Do memberwise hashing.
822 * If sd is a nested struct, and if it's nested in a class, the calculated
823 * hash value will also contain the result of parent class's toHash().
825 const(char)[] code =
826 ".object.size_t h = 0;" ~
827 "foreach (i, T; typeof(p.tupleof))" ~
828 // workaround https://issues.dlang.org/show_bug.cgi?id=17968
829 " static if(is(T* : const(.object.Object)*)) " ~
830 " h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
831 " else " ~
832 " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
833 "return h;";
834 fop.fbody = new CompileStatement(loc, new StringExp(loc, code));
835 Scope* sc2 = sc.push();
836 sc2.stc = 0;
837 sc2.linkage = LINK.d;
838 fop.dsymbolSemantic(sc2);
839 fop.semantic2(sc2);
840 sc2.pop();
842 //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars());
843 return fop;
846 /*****************************************
847 * Create aggregate destructor for struct/class by aggregating
848 * all the destructors in userDtors[] with the destructors for
849 * all the members.
850 * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
851 * Params:
852 * ad = struct or class to build destructor for
853 * sc = context
854 * Note:
855 * Close similarity with StructDeclaration::buildPostBlit(),
856 * and the ordering changes (runs backward instead of forwards).
858 void buildDtors(AggregateDeclaration ad, Scope* sc)
860 //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
861 if (ad.isUnionDeclaration())
862 return; // unions don't have destructors
864 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
865 Loc declLoc = ad.userDtors.dim ? ad.userDtors[0].loc : ad.loc;
866 Loc loc; // internal code should have no loc to prevent coverage
867 FuncDeclaration xdtor_fwd = null;
869 // Build the field destructor (`ad.fieldDtor`), if needed.
870 // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building.
871 const bool dtorIsCppPrototype = ad.userDtors.dim && ad.userDtors[0].linkage == LINK.cpp && !ad.userDtors[0].fbody;
872 if (!dtorIsCppPrototype)
874 Expression e = null;
875 for (size_t i = 0; i < ad.fields.dim; i++)
877 auto v = ad.fields[i];
878 if (v.storage_class & STC.ref_)
879 continue;
880 if (v.overlapped)
881 continue;
882 auto tv = v.type.baseElemOf();
883 if (tv.ty != Tstruct)
884 continue;
885 auto sdv = (cast(TypeStruct)tv).sym;
886 if (!sdv.dtor)
887 continue;
889 // fix: https://issues.dlang.org/show_bug.cgi?id=17257
890 // braces for shrink wrapping scope of a
892 xdtor_fwd = sdv.dtor; // this dtor is temporary it could be anything
893 auto a = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor_fwd);
894 a.addMember(sc, ad); // temporarily add to symbol table
897 sdv.dtor.functionSemantic();
899 stc = mergeFuncAttrs(stc, sdv.dtor);
900 if (stc & STC.disable)
902 e = null;
903 break;
906 Expression ex;
907 tv = v.type.toBasetype();
908 if (tv.ty == Tstruct)
910 // this.v.__xdtor()
912 ex = new ThisExp(loc);
913 ex = new DotVarExp(loc, ex, v);
915 // This is a hack so we can call destructors on const/immutable objects.
916 // Do it as a type 'paint'.
917 ex = new CastExp(loc, ex, v.type.mutableOf());
918 if (stc & STC.safe)
919 stc = (stc & ~STC.safe) | STC.trusted;
921 ex = new DotVarExp(loc, ex, sdv.dtor, false);
922 ex = new CallExp(loc, ex);
924 else
926 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
928 const n = tv.numberOfElems(loc);
929 if (n == 0)
930 continue;
932 ex = new ThisExp(loc);
933 ex = new DotVarExp(loc, ex, v);
935 // This is a hack so we can call destructors on const/immutable objects.
936 ex = new DotIdExp(loc, ex, Id.ptr);
937 ex = new CastExp(loc, ex, sdv.type.pointerTo());
938 if (stc & STC.safe)
939 stc = (stc & ~STC.safe) | STC.trusted;
941 ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
942 new IntegerExp(loc, n, Type.tsize_t));
943 // Prevent redundant bounds check
944 (cast(SliceExp)ex).upperIsInBounds = true;
945 (cast(SliceExp)ex).lowerIsLessThanUpper = true;
947 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), ex);
949 e = Expression.combine(ex, e); // combine in reverse order
952 if (e || (stc & STC.disable))
954 //printf("Building __fieldDtor(), %s\n", e.toChars());
955 auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor);
956 dd.generated = true;
957 dd.storage_class |= STC.inference;
958 dd.fbody = new ExpStatement(loc, e);
959 ad.members.push(dd);
960 dd.dsymbolSemantic(sc);
961 ad.fieldDtor = dd;
965 // Generate list of dtors to call in that order
966 DtorDeclarations dtors;
967 foreach_reverse (userDtor; ad.userDtors[])
968 dtors.push(userDtor);
969 if (ad.fieldDtor)
970 dtors.push(ad.fieldDtor);
971 if (!dtorIsCppPrototype)
973 // extern(C++) destructors call into super to destruct the full hierarchy
974 ClassDeclaration cldec = ad.isClassDeclaration();
975 if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor)
976 dtors.push(cldec.baseClass.aggrDtor);
979 // Set/build `ad.aggrDtor`
980 switch (dtors.dim)
982 case 0:
983 break;
985 case 1:
986 // Use the single existing dtor directly as aggregate dtor.
987 // Note that this might be `cldec.baseClass.aggrDtor`.
988 ad.aggrDtor = dtors[0];
989 break;
991 default:
992 // Build the aggregate destructor, calling all dtors in order.
993 assert(!dtorIsCppPrototype);
994 Expression e = null;
995 e = null;
996 stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
997 foreach (FuncDeclaration fd; dtors)
999 stc = mergeFuncAttrs(stc, fd);
1000 if (stc & STC.disable)
1002 e = null;
1003 break;
1005 Expression ex = new ThisExp(loc);
1006 ex = new DotVarExp(loc, ex, fd, false);
1007 CallExp ce = new CallExp(loc, ex);
1008 ce.directcall = true;
1009 e = Expression.combine(e, ce);
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 ad.aggrDtor = dd;
1018 break;
1021 // Set/build `ad.dtor`.
1022 // On Windows, the dtor in the vtable is a shim with different signature.
1023 ad.dtor = (ad.aggrDtor && ad.aggrDtor.linkage == LINK.cpp && !target.cpp.twoDtorInVtable)
1024 ? buildWindowsCppDtor(ad, ad.aggrDtor, sc)
1025 : ad.aggrDtor;
1027 // Add an __xdtor alias to make `ad.dtor` accessible
1028 if (ad.dtor)
1030 auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, ad.dtor);
1031 _alias.dsymbolSemantic(sc);
1032 ad.members.push(_alias);
1033 if (xdtor_fwd)
1034 ad.symtab.update(_alias); // update forward dtor to correct one
1035 else
1036 _alias.addMember(sc, ad); // add to symbol table
1039 // Set/build `ad.tidtor`
1040 ad.tidtor = buildExternDDtor(ad, sc);
1044 * build a shim function around the compound dtor that accepts an argument
1045 * that is used to implement the deleting C++ destructor
1047 * Params:
1048 * ad = the aggregate that contains the destructor to wrap
1049 * dtor = the destructor to wrap
1050 * sc = the scope in which to analyze the new function
1052 * Returns:
1053 * the shim destructor, semantically analyzed and added to the class as a member
1055 private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclaration dtor, Scope* sc)
1057 auto cldec = ad.isClassDeclaration();
1058 if (!cldec || cldec.cppDtorVtblIndex == -1) // scalar deleting dtor not built for non-virtual dtors
1059 return dtor;
1061 // generate deleting C++ destructor corresponding to:
1062 // void* C::~C(int del)
1063 // {
1064 // this->~C();
1065 // // TODO: if (del) delete (char*)this;
1066 // return (void*) this;
1067 // }
1068 Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null);
1069 Parameters* params = new Parameters;
1070 params.push(delparam);
1071 auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, dtor.storage_class);
1072 auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.cppdtor);
1073 func.type = ftype;
1075 // Always generate the function with body, because it is not exported from DLLs.
1076 const loc = dtor.loc;
1077 auto stmts = new Statements;
1078 auto call = new CallExp(loc, dtor, null);
1079 call.directcall = true;
1080 stmts.push(new ExpStatement(loc, call));
1081 stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
1082 func.fbody = new CompoundStatement(loc, stmts);
1083 func.generated = true;
1085 auto sc2 = sc.push();
1086 sc2.stc &= ~STC.static_; // not a static destructor
1087 sc2.linkage = LINK.cpp;
1089 ad.members.push(func);
1090 func.addMember(sc2, ad);
1091 func.dsymbolSemantic(sc2);
1093 sc2.pop();
1094 return func;
1098 * build a shim function around the aggregate dtor that translates
1099 * a C++ destructor to a destructor with extern(D) calling convention
1101 * Params:
1102 * ad = the aggregate that contains the destructor to wrap
1103 * sc = the scope in which to analyze the new function
1105 * Returns:
1106 * the shim destructor, semantically analyzed and added to the class as a member
1108 private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc)
1110 auto dtor = ad.aggrDtor;
1111 if (!dtor)
1112 return null;
1114 // Generate shim only when ABI incompatible on target platform
1115 if (ad.classKind != ClassKind.cpp || !target.cpp.wrapDtorInExternD)
1116 return dtor;
1118 // generate member function that adjusts calling convention
1119 // (EAX used for 'this' instead of ECX on Windows/stack on others):
1120 // extern(D) void __ticppdtor()
1121 // {
1122 // Class.__dtor();
1123 // }
1124 auto ftype = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dtor.storage_class);
1125 auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.ticppdtor);
1126 func.type = ftype;
1128 auto call = new CallExp(dtor.loc, dtor, null);
1129 call.directcall = true; // non-virtual call Class.__dtor();
1130 func.fbody = new ExpStatement(dtor.loc, call);
1131 func.generated = true;
1132 func.storage_class |= STC.inference;
1134 auto sc2 = sc.push();
1135 sc2.stc &= ~STC.static_; // not a static destructor
1136 sc2.linkage = LINK.d;
1138 ad.members.push(func);
1139 func.addMember(sc2, ad);
1140 func.dsymbolSemantic(sc2);
1141 func.functionSemantic(); // to infer attributes
1143 sc2.pop();
1144 return func;
1147 /******************************************
1148 * Create inclusive invariant for struct/class by aggregating
1149 * all the invariants in invs[].
1150 * ---
1151 * void __invariant() const [pure nothrow @trusted]
1153 * invs[0](), invs[1](), ...;
1155 * ---
1157 FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc)
1159 switch (ad.invs.dim)
1161 case 0:
1162 return null;
1164 case 1:
1165 // Don't return invs[0] so it has uniquely generated name.
1166 goto default;
1168 default:
1169 Expression e = null;
1170 StorageClass stcx = 0;
1171 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1172 foreach (i, inv; ad.invs)
1174 stc = mergeFuncAttrs(stc, inv);
1175 if (stc & STC.disable)
1177 // What should do?
1179 const stcy = (inv.storage_class & STC.synchronized_) |
1180 (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0);
1181 if (i == 0)
1182 stcx = stcy;
1183 else if (stcx ^ stcy)
1185 version (all)
1187 // currently rejects
1188 ad.error(inv.loc, "mixing invariants with different `shared`/`synchronized` qualifiers is not supported");
1189 e = null;
1190 break;
1193 e = Expression.combine(e, new CallExp(Loc.initial, new VarExp(Loc.initial, inv, false)));
1195 auto inv = new InvariantDeclaration(ad.loc, Loc.initial, stc | stcx,
1196 Id.classInvariant, new ExpStatement(Loc.initial, e));
1197 ad.members.push(inv);
1198 inv.dsymbolSemantic(sc);
1199 return inv;
1203 /*****************************************
1204 * Create inclusive postblit for struct by aggregating
1205 * all the postblits in postblits[] with the postblits for
1206 * all the members.
1207 * Note the close similarity with AggregateDeclaration::buildDtor(),
1208 * and the ordering changes (runs forward instead of backwards).
1210 FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
1212 //printf("buildPostBlit() %s\n", sd.toChars());
1213 if (sd.isUnionDeclaration())
1214 return null;
1216 const hasUserDefinedPosblit = sd.postblits.dim && !sd.postblits[0].isDisabled ? true : false;
1218 // by default, the storage class of the created postblit
1219 StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1220 Loc declLoc = sd.postblits.dim ? sd.postblits[0].loc : sd.loc;
1221 Loc loc; // internal code should have no loc to prevent coverage
1223 // if any of the postblits are disabled, then the generated postblit
1224 // will be disabled
1225 foreach (postblit; sd.postblits)
1226 stc |= postblit.storage_class & STC.disable;
1228 VarDeclaration[] fieldsToDestroy;
1229 auto postblitCalls = new Statements();
1230 // iterate through all the struct fields that are not disabled
1231 for (size_t i = 0; i < sd.fields.dim && !(stc & STC.disable); i++)
1233 auto structField = sd.fields[i];
1234 if (structField.storage_class & STC.ref_)
1235 continue;
1236 if (structField.overlapped)
1237 continue;
1238 // if it's a struct declaration or an array of structs
1239 Type tv = structField.type.baseElemOf();
1240 if (tv.ty != Tstruct)
1241 continue;
1242 auto sdv = (cast(TypeStruct)tv).sym;
1243 // which has a postblit declaration
1244 if (!sdv.postblit)
1245 continue;
1246 assert(!sdv.isUnionDeclaration());
1248 // if this field's postblit is not `nothrow`, add a `scope(failure)`
1249 // block to destroy any prior successfully postblitted fields should
1250 // this field's postblit fail
1251 if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow)
1253 // create a list of destructors that need to be called
1254 Expression[] dtorCalls;
1255 foreach(sf; fieldsToDestroy)
1257 Expression ex;
1258 tv = sf.type.toBasetype();
1259 if (tv.ty == Tstruct)
1261 // this.v.__xdtor()
1263 ex = new ThisExp(loc);
1264 ex = new DotVarExp(loc, ex, sf);
1266 // This is a hack so we can call destructors on const/immutable objects.
1267 ex = new AddrExp(loc, ex);
1268 ex = new CastExp(loc, ex, sf.type.mutableOf().pointerTo());
1269 ex = new PtrExp(loc, ex);
1270 if (stc & STC.safe)
1271 stc = (stc & ~STC.safe) | STC.trusted;
1273 auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym;
1275 ex = new DotVarExp(loc, ex, sfv.dtor, false);
1276 ex = new CallExp(loc, ex);
1278 dtorCalls ~= ex;
1280 else
1282 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1284 const length = tv.numberOfElems(loc);
1286 ex = new ThisExp(loc);
1287 ex = new DotVarExp(loc, ex, sf);
1289 // This is a hack so we can call destructors on const/immutable objects.
1290 ex = new DotIdExp(loc, ex, Id.ptr);
1291 ex = new CastExp(loc, ex, sdv.type.pointerTo());
1292 if (stc & STC.safe)
1293 stc = (stc & ~STC.safe) | STC.trusted;
1295 auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
1296 new IntegerExp(loc, length, Type.tsize_t));
1297 // Prevent redundant bounds check
1298 se.upperIsInBounds = true;
1299 se.lowerIsLessThanUpper = true;
1301 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se);
1303 dtorCalls ~= ex;
1306 fieldsToDestroy = [];
1308 // aggregate the destructor calls
1309 auto dtors = new Statements();
1310 foreach_reverse(dc; dtorCalls)
1312 dtors.push(new ExpStatement(loc, dc));
1315 // put destructor calls in a `scope(failure)` block
1316 postblitCalls.push(new ScopeGuardStatement(loc, TOK.onScopeFailure, new CompoundStatement(loc, dtors)));
1319 // perform semantic on the member postblit in order to
1320 // be able to aggregate it later on with the rest of the
1321 // postblits
1322 sdv.postblit.functionSemantic();
1324 stc = mergeFuncAttrs(stc, sdv.postblit);
1325 stc = mergeFuncAttrs(stc, sdv.dtor);
1327 // if any of the struct member fields has disabled
1328 // its postblit, then `sd` is not copyable, so no
1329 // postblit is generated
1330 if (stc & STC.disable)
1332 postblitCalls.setDim(0);
1333 break;
1336 Expression ex;
1337 tv = structField.type.toBasetype();
1338 if (tv.ty == Tstruct)
1340 // this.v.__xpostblit()
1342 ex = new ThisExp(loc);
1343 ex = new DotVarExp(loc, ex, structField);
1345 // This is a hack so we can call postblits on const/immutable objects.
1346 ex = new AddrExp(loc, ex);
1347 ex = new CastExp(loc, ex, structField.type.mutableOf().pointerTo());
1348 ex = new PtrExp(loc, ex);
1349 if (stc & STC.safe)
1350 stc = (stc & ~STC.safe) | STC.trusted;
1352 ex = new DotVarExp(loc, ex, sdv.postblit, false);
1353 ex = new CallExp(loc, ex);
1355 else
1357 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
1359 const length = tv.numberOfElems(loc);
1360 if (length == 0)
1361 continue;
1363 ex = new ThisExp(loc);
1364 ex = new DotVarExp(loc, ex, structField);
1366 // This is a hack so we can call postblits on const/immutable objects.
1367 ex = new DotIdExp(loc, ex, Id.ptr);
1368 ex = new CastExp(loc, ex, sdv.type.pointerTo());
1369 if (stc & STC.safe)
1370 stc = (stc & ~STC.safe) | STC.trusted;
1372 auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
1373 new IntegerExp(loc, length, Type.tsize_t));
1374 // Prevent redundant bounds check
1375 se.upperIsInBounds = true;
1376 se.lowerIsLessThanUpper = true;
1377 ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayPostblit), se);
1379 postblitCalls.push(new ExpStatement(loc, ex)); // combine in forward order
1381 /* https://issues.dlang.org/show_bug.cgi?id=10972
1382 * When subsequent field postblit calls fail,
1383 * this field should be destructed for Exception Safety.
1385 if (sdv.dtor)
1387 sdv.dtor.functionSemantic();
1389 // keep a list of fields that need to be destroyed in case
1390 // of a future postblit failure
1391 fieldsToDestroy ~= structField;
1395 void checkShared()
1397 if (sd.type.isShared())
1398 stc |= STC.shared_;
1401 // Build our own "postblit" which executes a, but only if needed.
1402 if (postblitCalls.dim || (stc & STC.disable))
1404 //printf("Building __fieldPostBlit()\n");
1405 checkShared();
1406 auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__fieldPostblit);
1407 dd.generated = true;
1408 dd.storage_class |= STC.inference | STC.scope_;
1409 dd.fbody = (stc & STC.disable) ? null : new CompoundStatement(loc, postblitCalls);
1410 sd.postblits.shift(dd);
1411 sd.members.push(dd);
1412 dd.dsymbolSemantic(sc);
1415 // create __xpostblit, which is the generated postblit
1416 FuncDeclaration xpostblit = null;
1417 switch (sd.postblits.dim)
1419 case 0:
1420 break;
1422 case 1:
1423 xpostblit = sd.postblits[0];
1424 break;
1426 default:
1427 Expression e = null;
1428 stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
1429 foreach (fd; sd.postblits)
1431 stc = mergeFuncAttrs(stc, fd);
1432 if (stc & STC.disable)
1434 e = null;
1435 break;
1437 Expression ex = new ThisExp(loc);
1438 ex = new DotVarExp(loc, ex, fd, false);
1439 ex = new CallExp(loc, ex);
1440 e = Expression.combine(e, ex);
1443 checkShared();
1444 auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__aggrPostblit);
1445 dd.generated = true;
1446 dd.storage_class |= STC.inference;
1447 dd.fbody = new ExpStatement(loc, e);
1448 sd.members.push(dd);
1449 dd.dsymbolSemantic(sc);
1450 xpostblit = dd;
1451 break;
1454 // Add an __xpostblit alias to make the inclusive postblit accessible
1455 if (xpostblit)
1457 auto _alias = new AliasDeclaration(Loc.initial, Id.__xpostblit, xpostblit);
1458 _alias.dsymbolSemantic(sc);
1459 sd.members.push(_alias);
1460 _alias.addMember(sc, sd); // add to symbol table
1463 if (sd.hasCopyCtor)
1465 // we have user defined postblit, so we prioritize it
1466 if (hasUserDefinedPosblit)
1468 sd.hasCopyCtor = false;
1469 return xpostblit;
1471 // we have fields with postblits, so print deprecations
1472 if (xpostblit && !xpostblit.isDisabled())
1474 deprecation(sd.loc, "`struct %s` implicitly-generated postblit hides copy constructor.", sd.toChars);
1475 deprecationSupplemental(sd.loc, "The field postblit will have priority over the copy constructor.");
1476 deprecationSupplemental(sd.loc, "To change this, the postblit should be disabled for `struct %s`", sd.toChars());
1477 sd.hasCopyCtor = false;
1479 else
1480 xpostblit = null;
1483 return xpostblit;
1487 * Generates a copy constructor declaration with the specified storage
1488 * class for the parameter and the function.
1490 * Params:
1491 * sd = the `struct` that contains the copy constructor
1492 * paramStc = the storage class of the copy constructor parameter
1493 * funcStc = the storage class for the copy constructor declaration
1495 * Returns:
1496 * The copy constructor declaration for struct `sd`.
1498 private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc)
1500 auto fparams = new Parameters();
1501 auto structType = sd.type;
1502 fparams.push(new Parameter(paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null));
1503 ParameterList pList = ParameterList(fparams);
1504 auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_);
1505 auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true);
1506 ccd.storage_class |= funcStc;
1507 ccd.storage_class |= STC.inference;
1508 ccd.generated = true;
1509 return ccd;
1513 * Generates a trivial copy constructor body that simply does memberwise
1514 * initialization:
1516 * this.field1 = rhs.field1;
1517 * this.field2 = rhs.field2;
1518 * ...
1520 * Params:
1521 * sd = the `struct` declaration that contains the copy constructor
1523 * Returns:
1524 * A `CompoundStatement` containing the body of the copy constructor.
1526 private Statement generateCopyCtorBody(StructDeclaration sd)
1528 Loc loc;
1529 Expression e;
1530 foreach (v; sd.fields)
1532 auto ec = new AssignExp(loc,
1533 new DotVarExp(loc, new ThisExp(loc), v),
1534 new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
1535 e = Expression.combine(e, ec);
1536 //printf("e.toChars = %s\n", e.toChars());
1538 Statement s1 = new ExpStatement(loc, e);
1539 return new CompoundStatement(loc, s1);
1543 * Determine if a copy constructor is needed for struct sd,
1544 * if the following conditions are met:
1546 * 1. sd does not define a copy constructor
1547 * 2. at least one field of sd defines a copy constructor
1549 * Params:
1550 * sd = the `struct` for which the copy constructor is generated
1551 * hasCpCtor = set to true if a copy constructor is already present
1553 * Returns:
1554 * `true` if one needs to be generated
1555 * `false` otherwise
1557 private bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
1559 if (global.errors)
1560 return false;
1562 auto ctor = sd.search(sd.loc, Id.ctor);
1563 if (ctor)
1565 if (ctor.isOverloadSet())
1566 return false;
1567 if (auto td = ctor.isTemplateDeclaration())
1568 ctor = td.funcroot;
1571 CtorDeclaration cpCtor;
1572 CtorDeclaration rvalueCtor;
1574 if (!ctor)
1575 goto LcheckFields;
1577 overloadApply(ctor, (Dsymbol s)
1579 if (s.isTemplateDeclaration())
1580 return 0;
1581 auto ctorDecl = s.isCtorDeclaration();
1582 assert(ctorDecl);
1583 if (ctorDecl.isCpCtor)
1585 if (!cpCtor)
1586 cpCtor = ctorDecl;
1587 return 0;
1590 auto tf = ctorDecl.type.toTypeFunction();
1591 const dim = tf.parameterList.length;
1592 if (dim == 1)
1594 auto param = tf.parameterList[0];
1595 if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
1597 rvalueCtor = ctorDecl;
1600 return 0;
1603 if (cpCtor)
1605 if (rvalueCtor)
1607 .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars());
1608 errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1609 errorSupplemental(cpCtor.loc, "copy constructor defined here");
1611 hasCpCtor = true;
1612 return false;
1615 LcheckFields:
1616 VarDeclaration fieldWithCpCtor;
1617 // see if any struct members define a copy constructor
1618 foreach (v; sd.fields)
1620 if (v.storage_class & STC.ref_)
1621 continue;
1622 if (v.overlapped)
1623 continue;
1625 auto ts = v.type.baseElemOf().isTypeStruct();
1626 if (!ts)
1627 continue;
1628 if (ts.sym.hasCopyCtor)
1630 fieldWithCpCtor = v;
1631 break;
1635 if (fieldWithCpCtor && rvalueCtor)
1637 .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars());
1638 errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1639 errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here");
1640 return false;
1642 else if (!fieldWithCpCtor)
1643 return false;
1644 return true;
1648 * Generates a copy constructor if needCopyCtor() returns true.
1649 * The generated copy constructor will be of the form:
1650 * this(ref return scope inout(S) rhs) inout
1652 * this.field1 = rhs.field1;
1653 * this.field2 = rhs.field2;
1654 * ...
1657 * Params:
1658 * sd = the `struct` for which the copy constructor is generated
1659 * sc = the scope where the copy constructor is generated
1661 * Returns:
1662 * `true` if `struct` sd defines a copy constructor (explicitly or generated),
1663 * `false` otherwise.
1665 bool buildCopyCtor(StructDeclaration sd, Scope* sc)
1667 bool hasCpCtor;
1668 if (!needCopyCtor(sd, hasCpCtor))
1669 return hasCpCtor;
1671 //printf("generating copy constructor for %s\n", sd.toChars());
1672 const MOD paramMod = MODFlags.wild;
1673 const MOD funcMod = MODFlags.wild;
1674 auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod));
1675 auto copyCtorBody = generateCopyCtorBody(sd);
1676 ccd.fbody = copyCtorBody;
1677 sd.members.push(ccd);
1678 ccd.addMember(sc, sd);
1679 const errors = global.startGagging();
1680 Scope* sc2 = sc.push();
1681 sc2.stc = 0;
1682 sc2.linkage = LINK.d;
1683 ccd.dsymbolSemantic(sc2);
1684 ccd.semantic2(sc2);
1685 ccd.semantic3(sc2);
1686 //printf("ccd semantic: %s\n", ccd.type.toChars());
1687 sc2.pop();
1688 if (global.endGagging(errors) || sd.isUnionDeclaration())
1690 ccd.storage_class |= STC.disable;
1691 ccd.fbody = null;
1693 return true;