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
15 import core
.stdc
.stdio
;
17 import dmd
.arraytypes
;
20 import dmd
.declaration
;
24 import dmd
.dsymbolsem
;
27 import dmd
.expression
;
28 import dmd
.expressionsem
;
32 import dmd
.identifier
;
44 /*******************************************
45 * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
48 * s1 = storage class to merge into
51 * merged storage class
53 StorageClass
mergeFuncAttrs(StorageClass s1
, const FuncDeclaration f
) pure
57 StorageClass s2
= (f
.storage_class
& STC
.disable
);
59 TypeFunction tf
= cast(TypeFunction
)f
.type
;
60 if (tf
.trust
== TRUST
.safe
)
62 else if (tf
.trust
== TRUST
.system
)
64 else if (tf
.trust
== TRUST
.trusted
)
67 if (tf
.purity
!= PURE
.impure
)
77 StorageClass
stc = (sa
& (STC
.pure_ | STC
.nothrow_ | STC
.nogc
)) |
(so
& STC
.disable
);
81 else if (sa
& STC
.trusted
)
83 else if ((so
& (STC
.trusted | STC
.safe
)) == (STC
.trusted | STC
.safe
))
85 else if (sa
& STC
.safe
)
91 /*******************************************
92 * Check given aggregate actually has an identity opAssign or not.
94 * ad = struct or class
97 * if found, returns FuncDeclaration of opAssign, otherwise null
99 FuncDeclaration
hasIdentityOpAssign(AggregateDeclaration ad
, Scope
* sc
)
101 Dsymbol assign
= search_function(ad
, Id
.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
109 auto a
= new Expressions(1);
110 const errors
= global
.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
116 auto f
= resolveFuncCall(ad
.loc
, sc
, assign
, null, ad
.type
, ArgumentList(a
), FuncResolveFlag
.quiet
);
120 f
= resolveFuncCall(ad
.loc
, sc
, assign
, null, ad
.type
, ArgumentList(a
), FuncResolveFlag
.quiet
);
124 global
.endGagging(errors
);
129 auto fparams
= f
.getParameterList();
132 auto fparam0
= fparams
[0];
133 if (fparam0
.type
.toDsymbol(null) != ad
)
137 // BUGS: This detection mechanism cannot find some opAssign-s like follows:
138 // struct S { void opAssign(ref immutable S) const; }
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");
159 if (sd
.isUnionDeclaration())
162 if (sd
.hasIdentityAssign ||
// because has identity==elaborate opAssign
167 /* If any of the fields need an opAssign, then we
170 foreach (v
; sd
.fields
)
172 if (v
.storage_class
& STC
.ref_
)
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())
182 if (needOpAssign(ts
.sym
))
189 /******************************************
190 * Build opAssign for a `struct`.
192 * The generated `opAssign` function has the following signature:
194 *ref S opAssign(S s) // S is the name of the `struct`
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:
216 *__swap = this; // bit copy
217 *this = s; // bit copy
221 * Otherwise, if `S` defines a postblit, the generated code for `opAssign` is:
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
239 *this.field1 = s.field1;
240 *this.field2 = s.field2;
244 * In this situation, the assignemnts are actual assign expressions (`opAssign` is used
248 * https://dlang.org/spec/struct.html#assign-overload
250 * sd = struct to generate opAssign for
253 * generated `opAssign` function
255 FuncDeclaration
buildOpAssign(StructDeclaration sd
, Scope
* sc
)
257 if (FuncDeclaration f
= hasIdentityOpAssign(sd
, sc
))
259 sd
.hasIdentityAssign
= true;
262 // Even if non-identity opAssign is defined, built-in identity opAssign
264 if (!needOpAssign(sd
))
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
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_
)
282 Type tv
= v
.type
.baseElemOf();
283 if (tv
.ty
!= Tstruct
)
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
294 stc = mergeFuncAttrs(stc, sd
.dtor
);
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;
306 if (stc & STC
.disable
)
310 /* Do swap this and rhs.
311 * __swap = this; this = s; __swap.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;
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");
353 foreach (v
; sd
.fields
)
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
);
364 Statement s1
= new ExpStatement(loc
, e
);
368 auto er
= new ThisExp(loc
);
369 Statement s2
= new ReturnStatement(loc
, er
);
370 fop
.fbody
= new CompoundStatement(loc
, s1
, s2
);
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();
379 sc2
.linkage
= LINK
.d
;
380 fop
.dsymbolSemantic(sc2
);
382 // https://issues.dlang.org/show_bug.cgi?id=15044
383 //semantic3(fop, sc2); // isn't run here for lazy forward reference resolution.
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());
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)
413 if (sd
.hasIdentityEquals
)
415 /* If any of the fields has an opEquals, then we
418 foreach (VarDeclaration v
; sd
.fields
)
420 if (v
.storage_class
& STC
.ref_
)
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)
431 if (needOpEquals(ts
.sym
))
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.
441 if (tvbase
.ty
== Tarray
)
443 if (tvbase
.ty
== Taarray
)
445 if (tvbase
.ty
== Tclass
)
449 //printf("\tdontneed\n");
452 //printf("\tneed\n");
456 /*******************************************
457 * Check given aggregate actually has an identity opEquals or not.
459 private FuncDeclaration
hasIdentityOpEquals(AggregateDeclaration ad
, Scope
* sc
)
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
477 FuncDeclaration
rfc(Expression e
)
480 (*a
)[0].type
= tthis
;
481 return resolveFuncCall(ad
.loc
, sc
, eq
, null, tthis
, ArgumentList(a
), FuncResolveFlag
.quiet
);
489 global
.endGagging(errors
);
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()))
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;
526 /******************************************
527 * Build __xopEquals for TypeInfo_Struct
528 * bool __xopEquals(ref const S p) const
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
;
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
);
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
);
573 Dsymbol s
= getDsymbol(e
);
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;
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();
595 sc2
.linkage
= LINK
.d
;
596 fop
.dsymbolSemantic(sc2
);
599 if (global
.endGagging(errors
)) // if errors happened
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
;
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
);
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
);
655 case EXP
.overloadSet
:
656 s
= e
.isOverExp().vars
;
659 s
= e
.isScopeExp().sds
;
662 s
= e
.isVarExp().var
;
667 if (!s || s
.ident
!= Id
.cmp)
668 e
= null; // there's no valid member 'opCmp'
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.
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
);
698 Dsymbol s
= getDsymbol(e
);
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;
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();
720 sc2
.linkage
= LINK
.d
;
721 fop
.dsymbolSemantic(sc2
);
724 if (global
.endGagging(errors
)) // if errors happened
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())
742 /* If any of the fields has an toHash, then we
745 foreach (VarDeclaration v
; sd
.fields
)
747 if (v
.storage_class
& STC
.ref_
)
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())
758 if (needToHash(ts
.sym
))
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.
768 if (tvbase
.ty
== Tarray
)
770 if (tvbase
.ty
== Taarray
)
772 if (tvbase
.ty
== Tclass
)
776 //printf("\tdontneed\n");
779 //printf("\tneed\n");
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
;
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
);
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
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
)
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().
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]);" ~
843 " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
845 fop
.fbody
= new MixinStatement(loc
, new StringExp(loc
, code
));
846 Scope
* sc2
= sc
.push();
848 sc2
.linkage
= LINK
.d
;
849 fop
.dsymbolSemantic(sc2
);
853 //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars());
857 /*****************************************
858 * Create aggregate destructor for struct/class by aggregating
859 * all the destructors in userDtors[] with the destructors for
861 * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
863 * ad = struct or class to build destructor for
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
)
886 for (size_t i
= 0; i
< ad
.fields
.length
; i
++)
888 auto v
= ad
.fields
[i
];
889 if (v
.storage_class
& STC
.ref_
)
893 auto tv
= v
.type
.baseElemOf();
894 if (tv
.ty
!= Tstruct
)
896 auto sdv
= (cast(TypeStruct
)tv
).sym
;
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
)
918 tv
= v
.type
.toBasetype();
919 if (tv
.ty
== Tstruct
)
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
);
930 stc = (stc & ~STC
.safe
) | STC
.trusted
;
932 ex
= new DotVarExp(loc
, ex
, sdv
.dtor
, false);
933 ex
= new CallExp(loc
, ex
);
937 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
939 const n
= tv
.numberOfElems(loc
);
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());
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
);
971 dd.dsymbolSemantic(sc
);
976 // Generate list of dtors to call in that order
977 DtorDeclarations dtors
;
978 foreach_reverse (userDtor
; ad
.userDtors
[])
979 dtors
.push(userDtor
);
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
)
997 // Use the single existing dtor directly as aggregate dtor.
998 // Note that this might be `cldec.baseClass.aggrDtor`.
999 ad
.aggrDtor
= dtors
[0];
1003 // Build the aggregate destructor, calling all dtors in order.
1004 assert(!dtorIsCppPrototype
);
1005 Expression 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
)
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
);
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
)
1038 // Add an __xdtor alias to make `ad.dtor` accessible
1041 auto _alias
= new AliasDeclaration(Loc
.initial
, Id
.__xdtor
, ad
.dtor
);
1042 _alias
.dsymbolSemantic(sc
);
1043 ad
.members
.push(_alias
);
1045 ad
.symtab
.update(_alias
); // update forward dtor to correct one
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
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
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)
1076 // // TODO: if (del) delete (char*)this;
1077 // return (void*) this;
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
);
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
);
1110 * build a shim function around the aggregate dtor that translates
1111 * a C++ destructor to a destructor with extern(D) calling convention
1114 * ad = the aggregate that contains the destructor to wrap
1115 * sc = the scope in which to analyze the new function
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
;
1126 // Don't try to call `@disable`d dtors
1127 if (dtor
.storage_class
& STC
.disable
)
1130 // Generate shim only when ABI incompatible on target platform
1131 if (dtor
._linkage
!= LINK
.cpp ||
!target
.cpp
.wrapDtorInExternD
)
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()
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
);
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
1163 /******************************************
1164 * Create inclusive invariant for struct/class by aggregating
1165 * all the invariants in invs[].
1167 * void __invariant() const [pure nothrow @trusted]
1169 * invs[0](), invs[1](), ...;
1173 FuncDeclaration
buildInv(AggregateDeclaration ad
, Scope
* sc
)
1175 switch (ad
.invs
.length
)
1181 // Don't return invs[0] so it has uniquely generated name.
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
)
1195 const stcy
= (inv
.storage_class
& STC
.synchronized_
) |
1196 (inv
.type
.mod
& MODFlags
.shared_ ? STC
.shared_
: 0);
1199 else if (stcx ^ stcy
)
1203 // currently rejects
1204 .error(inv
.loc
, "%s `%s` mixing invariants with different `shared`/`synchronized` qualifiers is not supported", ad
.kind(), ad
.toPrettyChars());
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
);
1219 /*****************************************
1220 * Create inclusive postblit for struct by aggregating
1221 * all the postblits in postblits[] with the postblits for
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())
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
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_
)
1252 if (structField
.overlapped
)
1254 // if it's a struct declaration or an array of structs
1255 Type tv
= structField
.type
.baseElemOf();
1256 if (tv
.ty
!= Tstruct
)
1258 auto sdv
= (cast(TypeStruct
)tv
).sym
;
1259 // which has a postblit declaration
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
)
1275 tv
= sf
.type
.toBasetype();
1276 if (tv
.ty
== Tstruct
)
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
);
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
);
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());
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
);
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
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);
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
);
1367 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1369 ex
= new DotVarExp(loc
, ex
, sdv
.postblit
, false);
1370 ex
= new CallExp(loc
, ex
);
1374 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
1376 const length
= tv
.numberOfElems(loc
);
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());
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.
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
;
1414 if (sd
.type
.isShared())
1418 // Build our own "postblit" which executes a, but only if needed.
1419 if (postblitCalls
.length ||
(stc & STC
.disable
))
1421 //printf("Building __fieldPostBlit()\n");
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
)
1440 xpostblit
= sd
.postblits
[0];
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
)
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
);
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
);
1471 // Add an __xpostblit alias to make the inclusive postblit accessible
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
1482 // we have user defined postblit, so we prioritize it
1483 if (hasUserDefinedPosblit
)
1485 sd
.hasCopyCtor
= false;
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;
1504 * Generates a copy constructor declaration with the specified storage
1505 * class for the parameter and the function.
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
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;
1530 * Generates a trivial copy constructor body that simply does memberwise
1533 * this.field1 = rhs.field1;
1534 * this.field2 = rhs.field2;
1538 * sd = the `struct` declaration that contains the copy constructor
1541 * A `CompoundStatement` containing the body of the copy constructor.
1543 private Statement
generateCopyCtorBody(StructDeclaration sd
)
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
1567 * sd = the `struct` for which the copy constructor is generated
1568 * hasCpCtor = set to true if a copy constructor is already present
1571 * `true` if one needs to be generated
1574 bool needCopyCtor(StructDeclaration sd
, out bool hasCpCtor
)
1579 auto ctor
= sd
.search(sd
.loc
, Id
.ctor
);
1582 if (ctor
.isOverloadSet())
1584 if (auto td
= ctor
.isTemplateDeclaration())
1588 CtorDeclaration cpCtor
;
1589 CtorDeclaration rvalueCtor
;
1594 overloadApply(ctor
, (Dsymbol s
)
1596 if (s
.isTemplateDeclaration())
1598 auto ctorDecl
= s
.isCtorDeclaration();
1600 if (ctorDecl
.isCpCtor
)
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
;
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");
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_
)
1642 auto ts
= v
.type
.baseElemOf().isTypeStruct();
1645 if (ts
.sym
.hasCopyCtor
)
1647 fieldWithCpCtor
= v
;
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");
1659 else if (!fieldWithCpCtor
)
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;
1675 * sd = the `struct` for which the copy constructor is generated
1676 * sc = the scope where the copy constructor is generated
1679 * `true` if `struct` sd defines a copy constructor (explicitly or generated),
1680 * `false` otherwise.
1682 bool buildCopyCtor(StructDeclaration sd
, Scope
* sc
)
1685 if (!needCopyCtor(sd
, 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();
1699 sc2
.linkage
= LINK
.d
;
1700 ccd
.dsymbolSemantic(sc2
);
1703 //printf("ccd semantic: %s\n", ccd.type.toChars());
1705 if (global
.endGagging(errors
) || sd
.isUnionDeclaration())
1707 ccd
.storage_class |
= STC
.disable
;