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