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