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
;
33 import dmd
.identifier
;
45 /*******************************************
46 * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
49 * s1 = storage class to merge into
52 * merged storage class
54 StorageClass
mergeFuncAttrs(StorageClass s1
, const FuncDeclaration f
) pure
58 StorageClass s2
= (f
.storage_class
& STC
.disable
);
60 TypeFunction tf
= cast(TypeFunction
)f
.type
;
61 if (tf
.trust
== TRUST
.safe
)
63 else if (tf
.trust
== TRUST
.system
)
65 else if (tf
.trust
== TRUST
.trusted
)
68 if (tf
.purity
!= PURE
.impure
)
78 StorageClass
stc = (sa
& (STC
.pure_ | STC
.nothrow_ | STC
.nogc
)) |
(so
& STC
.disable
);
82 else if (sa
& STC
.trusted
)
84 else if ((so
& (STC
.trusted | STC
.safe
)) == (STC
.trusted | STC
.safe
))
86 else if (sa
& STC
.safe
)
92 /*******************************************
93 * Check given aggregate actually has an identity opAssign or not.
95 * ad = struct or class
98 * if found, returns FuncDeclaration of opAssign, otherwise null
100 FuncDeclaration
hasIdentityOpAssign(AggregateDeclaration ad
, Scope
* sc
)
102 Dsymbol assign
= search_function(ad
, Id
.assign
);
105 /* check identity opAssign exists
107 scope er
= new NullExp(ad
.loc
, ad
.type
); // dummy rvalue
108 scope el
= new IdentifierExp(ad
.loc
, Id
.p
); // dummy lvalue
110 auto a
= new Expressions(1);
111 const errors
= global
.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
117 auto f
= resolveFuncCall(ad
.loc
, sc
, assign
, null, ad
.type
, ArgumentList(a
), FuncResolveFlag
.quiet
);
121 f
= resolveFuncCall(ad
.loc
, sc
, assign
, null, ad
.type
, ArgumentList(a
), FuncResolveFlag
.quiet
);
125 global
.endGagging(errors
);
130 auto fparams
= f
.getParameterList();
133 auto fparam0
= fparams
[0];
134 if (fparam0
.type
.toDsymbol(null) != ad
)
138 // BUGS: This detection mechanism cannot find some opAssign-s like follows:
139 // struct S { void opAssign(ref immutable S) const; }
145 /*******************************************
146 * We need an opAssign for the struct if
147 * it has a destructor or a postblit.
148 * We need to generate one if a user-specified one does not exist.
150 private bool needOpAssign(StructDeclaration sd
)
152 //printf("StructDeclaration::needOpAssign() %s\n", sd.toChars());
154 static bool isNeeded()
156 //printf("\tneed\n");
160 if (sd
.isUnionDeclaration())
163 if (sd
.hasIdentityAssign ||
// because has identity==elaborate opAssign
168 /* If any of the fields need an opAssign, then we
171 foreach (v
; sd
.fields
)
173 if (v
.storage_class
& STC
.ref_
)
175 if (v
.overlapped
) // if field of a union
176 continue; // user must handle it themselves
177 Type tv
= v
.type
.baseElemOf();
178 if (tv
.ty
== Tstruct
)
180 TypeStruct ts
= cast(TypeStruct
)tv
;
181 if (ts
.sym
.isUnionDeclaration())
183 if (needOpAssign(ts
.sym
))
190 /******************************************
191 * Build opAssign for a `struct`.
193 * The generated `opAssign` function has the following signature:
195 *ref S opAssign(S s) // S is the name of the `struct`
198 * The opAssign function will be built for a struct `S` if the
199 * following constraints are met:
201 * 1. `S` does not have an identity `opAssign` defined.
203 * 2. `S` has at least one of the following members: a postblit (user-defined or
204 * generated for fields that have a defined postblit), a destructor
205 * (user-defined or generated for fields that have a defined destructor)
206 * or at least one field that has a defined `opAssign`.
208 * 3. `S` does not have any non-mutable fields.
210 * If `S` has a disabled destructor or at least one field that has a disabled
211 * `opAssign`, `S.opAssign` is going to be generated, but marked with `@disable`
213 * If `S` defines a destructor, the generated code for `opAssign` is:
217 *__swap = this; // bit copy
218 *this = s; // bit copy
222 * Otherwise, if `S` defines a postblit, the generated code for `opAssign` is:
228 * Note that the parameter to the generated `opAssign` is passed by value, which means
229 * that the postblit is going to be called (if it is defined) in both of the above
230 * situations before entering the body of `opAssign`. The assignments in the above generated
231 * function bodies are blit expressions, so they can be regarded as `memcpy`s
232 * (`opAssign` is not called as this will result in an infinite recursion; the postblit
233 * is not called because it has already been called when the parameter was passed by value).
235 * If `S` does not have a postblit or a destructor, but contains at least one field that defines
236 * an `opAssign` function (which is not disabled), then the body will make member-wise
240 *this.field1 = s.field1;
241 *this.field2 = s.field2;
245 * In this situation, the assignemnts are actual assign expressions (`opAssign` is used
249 * https://dlang.org/spec/struct.html#assign-overload
251 * sd = struct to generate opAssign for
254 * generated `opAssign` function
256 FuncDeclaration
buildOpAssign(StructDeclaration sd
, Scope
* sc
)
258 if (FuncDeclaration f
= hasIdentityOpAssign(sd
, sc
))
260 sd
.hasIdentityAssign
= true;
263 // Even if non-identity opAssign is defined, built-in identity opAssign
265 if (!needOpAssign(sd
))
268 //printf("StructDeclaration::buildOpAssign() %s\n", sd.toChars());
269 StorageClass
stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
270 Loc declLoc
= sd
.loc
;
271 Loc loc
; // internal code should have no loc to prevent coverage
273 // One of our sub-field might have `@disable opAssign` so we need to
275 // In this event, it will be reflected by having `stc` (opAssign's
276 // storage class) include `STC.disabled`.
277 foreach (v
; sd
.fields
)
279 if (v
.storage_class
& STC
.ref_
)
283 Type tv
= v
.type
.baseElemOf();
284 if (tv
.ty
!= Tstruct
)
286 StructDeclaration sdv
= (cast(TypeStruct
)tv
).sym
;
287 stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv
, sc
));
290 if (sd
.dtor || sd
.postblit
)
292 // if the type is not assignable, we cannot generate opAssign
293 if (!sd
.type
.isAssignable()) // https://issues.dlang.org/show_bug.cgi?id=13044
295 stc = mergeFuncAttrs(stc, sd
.dtor
);
297 stc = (stc & ~STC
.safe
) | STC
.trusted
;
300 auto fparams
= new Parameters();
301 fparams
.push(new Parameter(loc
, STC
.nodtor
, sd
.type
, Id
.p
, null, null));
302 auto tf
= new TypeFunction(ParameterList(fparams
), sd
.handleType(), LINK
.d
, stc | STC
.ref_
);
303 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, Id
.assign
, stc, tf
);
304 fop
.storage_class |
= STC
.inference
;
305 fop
.isGenerated
= true;
307 if (stc & STC
.disable
)
311 /* Do swap this and rhs.
312 * __swap = this; this = s; __swap.dtor();
316 //printf("\tswap copy\n");
317 TypeFunction tdtor
= cast(TypeFunction
)sd
.dtor
.type
;
318 assert(tdtor
.ty
== Tfunction
);
320 auto idswap
= Identifier
.generateId("__swap");
321 auto swap
= new VarDeclaration(loc
, sd
.type
, idswap
, new VoidInitializer(loc
));
322 swap
.storage_class |
= STC
.nodtor | STC
.temp | STC
.ctfe
;
323 if (tdtor
.isScopeQual
)
324 swap
.storage_class |
= STC
.scope_
;
325 auto e1
= new DeclarationExp(loc
, swap
);
327 auto e2
= new BlitExp(loc
, new VarExp(loc
, swap
), new ThisExp(loc
));
328 auto e3
= new BlitExp(loc
, new ThisExp(loc
), new IdentifierExp(loc
, Id
.p
));
330 /* Instead of running the destructor on s, run it
331 * on swap. This avoids needing to copy swap back in to s.
333 auto e4
= new CallExp(loc
, new DotVarExp(loc
, new VarExp(loc
, swap
), sd
.dtor
, false));
335 e
= Expression
.combine(e1
, e2
, e3
, e4
);
337 /* postblit was called when the value was passed to opAssign, we just need to blit the result */
338 else if (sd
.postblit
)
340 e
= new BlitExp(loc
, new ThisExp(loc
), new IdentifierExp(loc
, Id
.p
));
341 sd
.hasBlitAssign
= true;
345 /* Do memberwise copy.
347 * If sd is a nested struct, its vthis field assignment is:
348 * 1. If it's nested in a class, it's a rebind of class reference.
349 * 2. If it's nested in a function or struct, it's an update of void*.
350 * In both cases, it will change the parent context.
352 //printf("\tmemberwise copy\n");
354 foreach (v
; sd
.fields
)
357 auto ec
= new AssignExp(loc
,
358 new DotVarExp(loc
, new ThisExp(loc
), v
),
359 new DotVarExp(loc
, new IdentifierExp(loc
, Id
.p
), v
));
360 e
= Expression
.combine(e
, ec
);
365 Statement s1
= new ExpStatement(loc
, e
);
369 auto er
= new ThisExp(loc
);
370 Statement s2
= new ReturnStatement(loc
, er
);
371 fop
.fbody
= new CompoundStatement(loc
, s1
, s2
);
374 sd
.members
.push(fop
);
375 fop
.addMember(sc
, sd
);
376 sd
.hasIdentityAssign
= true; // temporary mark identity assignable
377 const errors
= global
.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
378 Scope
* sc2
= sc
.push();
380 sc2
.linkage
= LINK
.d
;
381 fop
.dsymbolSemantic(sc2
);
383 // https://issues.dlang.org/show_bug.cgi?id=15044
384 //semantic3(fop, sc2); // isn't run here for lazy forward reference resolution.
387 if (global
.endGagging(errors
)) // if errors happened
389 // Disable generated opAssign, because some members forbid identity assignment.
390 fop
.storage_class |
= STC
.disable
;
391 fop
.fbody
= null; // remove fbody which contains the error
394 //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd.toChars(), (fop.storage_class & STC.disable) != 0);
395 //printf("fop.type: %s\n", fop.type.toPrettyChars());
399 /*******************************************
400 * We need an opEquals for the struct if
401 * any fields has an opEquals.
402 * Generate one if a user-specified one does not exist.
404 bool needOpEquals(StructDeclaration sd
)
406 //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars());
407 if (sd
.isUnionDeclaration())
409 /* If a union has only one field, treat it like a struct
411 if (sd
.fields
.length
!= 1)
414 if (sd
.hasIdentityEquals
)
416 /* If any of the fields has an opEquals, then we
419 foreach (VarDeclaration v
; sd
.fields
)
421 if (v
.storage_class
& STC
.ref_
)
425 Type tv
= v
.type
.toBasetype();
426 auto tvbase
= tv
.baseElemOf();
427 if (tvbase
.ty
== Tstruct
)
429 TypeStruct ts
= cast(TypeStruct
)tvbase
;
430 if (ts
.sym
.isUnionDeclaration() && ts
.sym
.fields
.length
!= 1)
432 if (needOpEquals(ts
.sym
))
435 if (tvbase
.isfloating())
437 // This is necessray for:
438 // 1. comparison of +0.0 and -0.0 should be true.
439 // 2. comparison of NANs should be false always.
442 if (tvbase
.ty
== Tarray
)
444 if (tvbase
.ty
== Taarray
)
446 if (tvbase
.ty
== Tclass
)
450 //printf("\tdontneed\n");
453 //printf("\tneed\n");
457 /*******************************************
458 * Check given aggregate actually has an identity opEquals or not.
460 private FuncDeclaration
hasIdentityOpEquals(AggregateDeclaration ad
, Scope
* sc
)
463 if (Dsymbol eq
= search_function(ad
, Id
.eq
))
465 /* check identity opEquals exists
467 scope er
= new NullExp(ad
.loc
, null); // dummy rvalue
468 scope el
= new IdentifierExp(ad
.loc
, Id
.p
); // dummy lvalue
469 auto a
= new Expressions(1);
471 bool hasIt(Type tthis
)
473 const errors
= global
.startGagging(); // Do not report errors, even if the template opAssign fbody makes it
478 FuncDeclaration
rfc(Expression e
)
481 (*a
)[0].type
= tthis
;
482 return resolveFuncCall(ad
.loc
, sc
, eq
, null, tthis
, ArgumentList(a
), FuncResolveFlag
.quiet
);
490 global
.endGagging(errors
);
495 if (hasIt(ad
.type
) ||
496 hasIt(ad
.type
.constOf()) ||
497 hasIt(ad
.type
.immutableOf()) ||
498 hasIt(ad
.type
.sharedOf()) ||
499 hasIt(ad
.type
.sharedConstOf()))
508 /******************************************
509 * Build opEquals for struct.
510 * const bool opEquals(const S s) { ... }
512 * By fixing https://issues.dlang.org/show_bug.cgi?id=3789
513 * opEquals is changed to be never implicitly generated.
514 * Now, struct objects comparison s1 == s2 is translated to:
515 * s1.tupleof == s2.tupleof
516 * to calculate structural equality. See EqualExp.op_overload.
518 FuncDeclaration
buildOpEquals(StructDeclaration sd
, Scope
* sc
)
520 if (hasIdentityOpEquals(sd
, sc
))
522 sd
.hasIdentityEquals
= true;
527 /******************************************
528 * Build __xopEquals for TypeInfo_Struct
529 * bool __xopEquals(ref const S p) const
534 * This is called by TypeInfo.equals(p1, p2). If the struct does not support
535 * const objects comparison, it will throw "not implemented" Error in runtime.
537 FuncDeclaration
buildXopEquals(StructDeclaration sd
, Scope
* sc
)
539 if (!needOpEquals(sd
))
540 return null; // bitwise comparison would work
542 //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars());
543 if (Dsymbol eq
= search_function(sd
, Id
.eq
))
545 if (FuncDeclaration fd
= eq
.isFuncDeclaration())
547 TypeFunction tfeqptr
;
550 scx
.eSink
= sc
.eSink
;
551 /* const bool opEquals(ref const S s);
553 auto parameters
= new Parameters();
554 parameters
.push(new Parameter(Loc
.initial
, STC
.ref_ | STC
.const_
, sd
.type
, null, null, null));
555 tfeqptr
= new TypeFunction(ParameterList(parameters
), Type
.tbool
, LINK
.d
);
556 tfeqptr
.mod
= MODFlags
.const_
;
557 tfeqptr
= cast(TypeFunction
)tfeqptr
.typeSemantic(Loc
.initial
, &scx
);
559 fd
= fd
.overloadExactMatch(tfeqptr
);
567 Identifier id
= Identifier
.idPool("_xopEquals");
568 Expression e
= new IdentifierExp(sd
.loc
, Id
.empty
);
569 e
= new DotIdExp(sd
.loc
, e
, Id
.object
);
570 e
= new DotIdExp(sd
.loc
, e
, id
);
571 e
= e
.expressionSemantic(sc
);
574 Dsymbol s
= getDsymbol(e
);
576 sd
.xerreq
= s
.isFuncDeclaration();
579 Loc declLoc
; // loc is unnecessary so __xopEquals is never called directly
580 Loc loc
; // loc is unnecessary so errors are gagged
581 auto parameters
= new Parameters();
582 parameters
.push(new Parameter(loc
, STC
.ref_ | STC
.const_
, sd
.type
, Id
.p
, null, null));
583 auto tf
= new TypeFunction(ParameterList(parameters
), Type
.tbool
, LINK
.d
, STC
.const_
);
584 tf
= tf
.addSTC(STC
.const_
).toTypeFunction();
585 Identifier id
= Id
.xopEquals
;
586 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, id
, 0, tf
);
587 fop
.isGenerated
= true;
589 Expression e1
= new IdentifierExp(loc
, Id
.This
);
590 Expression e2
= new IdentifierExp(loc
, Id
.p
);
591 Expression e
= new EqualExp(EXP
.equal
, loc
, e1
, e2
);
592 fop
.fbody
= new ReturnStatement(loc
, e
);
593 uint errors
= global
.startGagging(); // Do not report errors
594 Scope
* sc2
= sc
.push();
596 sc2
.linkage
= LINK
.d
;
597 fop
.dsymbolSemantic(sc2
);
600 if (global
.endGagging(errors
)) // if errors happened
605 /******************************************
606 * Build __xopCmp for TypeInfo_Struct
607 * int __xopCmp(ref const S p) const
609 * return this.opCmp(p);
612 * This is called by TypeInfo.compare(p1, p2). If the struct does not support
613 * const objects comparison, it will throw "not implemented" Error in runtime.
615 FuncDeclaration
buildXopCmp(StructDeclaration sd
, Scope
* sc
)
617 //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
618 if (Dsymbol
cmp = search_function(sd
, Id
.cmp))
620 if (FuncDeclaration fd
= cmp.isFuncDeclaration())
622 TypeFunction tfcmpptr
;
625 scx
.eSink
= sc
.eSink
;
626 /* const int opCmp(ref const S s);
628 auto parameters
= new Parameters();
629 parameters
.push(new Parameter(Loc
.initial
, STC
.ref_ | STC
.const_
, sd
.type
, null, null, null));
630 tfcmpptr
= new TypeFunction(ParameterList(parameters
), Type
.tint32
, LINK
.d
);
631 tfcmpptr
.mod
= MODFlags
.const_
;
632 tfcmpptr
= cast(TypeFunction
)tfcmpptr
.typeSemantic(Loc
.initial
, &scx
);
634 fd
= fd
.overloadExactMatch(tfcmpptr
);
641 version (none
) // FIXME: doesn't work for recursive alias this
643 /* Check opCmp member exists.
644 * Consider 'alias this', but except opDispatch.
646 Expression e
= new DsymbolExp(sd
.loc
, sd
);
647 e
= new DotIdExp(sd
.loc
, e
, Id
.cmp);
648 Scope
* sc2
= sc
.push();
649 e
= e
.trySemantic(sc2
);
656 case EXP
.overloadSet
:
657 s
= e
.isOverExp().vars
;
660 s
= e
.isScopeExp().sds
;
663 s
= e
.isVarExp().var
;
668 if (!s || s
.ident
!= Id
.cmp)
669 e
= null; // there's no valid member 'opCmp'
672 return null; // bitwise comparison would work
673 /* Essentially, a struct which does not define opCmp is not comparable.
674 * At this time, typeid(S).compare might be correct that throwing "not implement" Error.
675 * But implementing it would break existing code, such as:
677 * struct S { int value; } // no opCmp
678 * int[S] aa; // Currently AA key uses bitwise comparison
679 * // (It's default behavior of TypeInfo_Strust.compare).
681 * Not sure we should fix this inconsistency, so just keep current behavior.
692 Identifier id
= Identifier
.idPool("_xopCmp");
693 Expression e
= new IdentifierExp(sd
.loc
, Id
.empty
);
694 e
= new DotIdExp(sd
.loc
, e
, Id
.object
);
695 e
= new DotIdExp(sd
.loc
, e
, id
);
696 e
= e
.expressionSemantic(sc
);
699 Dsymbol s
= getDsymbol(e
);
701 sd
.xerrcmp
= s
.isFuncDeclaration();
704 Loc declLoc
; // loc is unnecessary so __xopCmp is never called directly
705 Loc loc
; // loc is unnecessary so errors are gagged
706 auto parameters
= new Parameters();
707 parameters
.push(new Parameter(loc
, STC
.ref_ | STC
.const_
, sd
.type
, Id
.p
, null, null));
708 auto tf
= new TypeFunction(ParameterList(parameters
), Type
.tint32
, LINK
.d
, STC
.const_
);
709 tf
= tf
.addSTC(STC
.const_
).toTypeFunction();
710 Identifier id
= Id
.xopCmp
;
711 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, id
, 0, tf
);
712 fop
.isGenerated
= true;
714 Expression e1
= new IdentifierExp(loc
, Id
.This
);
715 Expression e2
= new IdentifierExp(loc
, Id
.p
);
716 Expression e
= new CallExp(loc
, new DotIdExp(loc
, e1
, Id
.cmp), e2
);
717 fop
.fbody
= new ReturnStatement(loc
, e
);
718 uint errors
= global
.startGagging(); // Do not report errors
719 Scope
* sc2
= sc
.push();
721 sc2
.linkage
= LINK
.d
;
722 fop
.dsymbolSemantic(sc2
);
725 if (global
.endGagging(errors
)) // if errors happened
730 /*******************************************
731 * We need a toHash for the struct if
732 * any fields has a toHash.
733 * Generate one if a user-specified one does not exist.
735 private bool needToHash(StructDeclaration sd
)
737 //printf("StructDeclaration::needToHash() %s\n", sd.toChars());
738 if (sd
.isUnionDeclaration())
743 /* If any of the fields has an toHash, then we
746 foreach (VarDeclaration v
; sd
.fields
)
748 if (v
.storage_class
& STC
.ref_
)
752 Type tv
= v
.type
.toBasetype();
753 auto tvbase
= tv
.baseElemOf();
754 if (tvbase
.ty
== Tstruct
)
756 TypeStruct ts
= cast(TypeStruct
)tvbase
;
757 if (ts
.sym
.isUnionDeclaration())
759 if (needToHash(ts
.sym
))
762 if (tvbase
.isfloating())
764 /* This is necessary because comparison of +0.0 and -0.0 should be true,
765 * i.e. not a bit compare.
769 if (tvbase
.ty
== Tarray
)
771 if (tvbase
.ty
== Taarray
)
773 if (tvbase
.ty
== Tclass
)
777 //printf("\tdontneed\n");
780 //printf("\tneed\n");
784 /******************************************
785 * Build __xtoHash for non-bitwise hashing
786 * static hash_t xtoHash(ref const S p) nothrow @trusted;
788 FuncDeclaration
buildXtoHash(StructDeclaration sd
, Scope
* sc
)
790 if (Dsymbol s
= search_function(sd
, Id
.tohash
))
792 __gshared TypeFunction tftohash
;
795 tftohash
= new TypeFunction(ParameterList(), Type
.thash_t
, LINK
.d
);
796 tftohash
.mod
= MODFlags
.const_
;
797 tftohash
= cast(TypeFunction
)tftohash
.merge();
799 if (FuncDeclaration fd
= s
.isFuncDeclaration())
801 fd
= fd
.overloadExactMatch(tftohash
);
809 /* The trouble is that the following code relies on .tupleof, but .tupleof
810 * is not allowed for C files. If we allow it for C files, then that turns on
811 * the other D properties, too, such as .dup which will then conflict with allowed
813 * One way to fix it is to replace the following foreach and .tupleof with C
814 * statements and expressions.
815 * But, it's debatable whether C structs should even need toHash().
816 * Note that it would only be necessary if it has floating point fields.
817 * For now, we'll just not generate a toHash() for C files.
819 if (sc
.flags
& SCOPE
.Cfile
)
822 //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
823 Loc declLoc
; // loc is unnecessary so __xtoHash is never called directly
824 Loc loc
; // internal code should have no loc to prevent coverage
825 auto parameters
= new Parameters();
826 parameters
.push(new Parameter(loc
, STC
.ref_ | STC
.const_
, sd
.type
, Id
.p
, null, null));
827 auto tf
= new TypeFunction(ParameterList(parameters
), Type
.thash_t
, LINK
.d
, STC
.nothrow_ | STC
.trusted
);
828 Identifier id
= Id
.xtoHash
;
829 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, id
, STC
.static_
, tf
);
830 fop
.isGenerated
= true;
832 /* Do memberwise hashing.
834 * If sd is a nested struct, and if it's nested in a class, the calculated
835 * hash value will also contain the result of parent class's toHash().
838 ".object.size_t h = 0;" ~
839 "foreach (i, T; typeof(p.tupleof))" ~
840 // workaround https://issues.dlang.org/show_bug.cgi?id=17968
841 " static if(is(T* : const(.object.Object)*)) " ~
842 " h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
844 " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
846 fop
.fbody
= new MixinStatement(loc
, new StringExp(loc
, code
));
847 Scope
* sc2
= sc
.push();
849 sc2
.linkage
= LINK
.d
;
850 fop
.dsymbolSemantic(sc2
);
854 //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars());
858 /*****************************************
859 * Create aggregate destructor for struct/class by aggregating
860 * all the destructors in userDtors[] with the destructors for
862 * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
864 * ad = struct or class to build destructor for
867 * Close similarity with StructDeclaration::buildPostBlit(),
868 * and the ordering changes (runs backward instead of forwards).
870 void buildDtors(AggregateDeclaration ad
, Scope
* sc
)
872 //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
873 if (ad
.isUnionDeclaration())
874 return; // unions don't have destructors
876 StorageClass
stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
877 Loc declLoc
= ad
.userDtors
.length ? ad
.userDtors
[0].loc
: ad
.loc
;
878 Loc loc
; // internal code should have no loc to prevent coverage
879 FuncDeclaration xdtor_fwd
= null;
881 // Build the field destructor (`ad.fieldDtor`), if needed.
882 // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building.
883 const bool dtorIsCppPrototype
= ad
.userDtors
.length
&& ad
.userDtors
[0]._linkage
== LINK
.cpp
&& !ad
.userDtors
[0].fbody
;
884 if (!dtorIsCppPrototype
)
887 for (size_t i
= 0; i
< ad
.fields
.length
; i
++)
889 auto v
= ad
.fields
[i
];
890 if (v
.storage_class
& STC
.ref_
)
894 auto tv
= v
.type
.baseElemOf();
895 if (tv
.ty
!= Tstruct
)
897 auto sdv
= (cast(TypeStruct
)tv
).sym
;
901 // fix: https://issues.dlang.org/show_bug.cgi?id=17257
902 // braces for shrink wrapping scope of a
904 xdtor_fwd
= sdv
.dtor
; // this dtor is temporary it could be anything
905 auto a
= new AliasDeclaration(Loc
.initial
, Id
.__xdtor
, xdtor_fwd
);
906 a
.addMember(sc
, ad
); // temporarily add to symbol table
909 functionSemantic(sdv
.dtor
);
911 stc = mergeFuncAttrs(stc, sdv
.dtor
);
912 if (stc & STC
.disable
)
919 tv
= v
.type
.toBasetype();
920 if (tv
.ty
== Tstruct
)
924 ex
= new ThisExp(loc
);
925 ex
= new DotVarExp(loc
, ex
, v
);
927 // This is a hack so we can call destructors on const/immutable objects.
928 // Do it as a type 'paint', `cast()`
929 ex
= new CastExp(loc
, ex
, MODFlags
.none
);
931 stc = (stc & ~STC
.safe
) | STC
.trusted
;
933 ex
= new DotVarExp(loc
, ex
, sdv
.dtor
, false);
934 ex
= new CallExp(loc
, ex
);
938 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
940 const n
= tv
.numberOfElems(loc
);
944 ex
= new ThisExp(loc
);
945 ex
= new DotVarExp(loc
, ex
, v
);
947 // This is a hack so we can call destructors on const/immutable objects.
948 ex
= new DotIdExp(loc
, ex
, Id
.ptr
);
949 ex
= new CastExp(loc
, ex
, sdv
.type
.pointerTo());
951 stc = (stc & ~STC
.safe
) | STC
.trusted
;
953 SliceExp se
= new SliceExp(loc
, ex
, new IntegerExp(loc
, 0, Type
.tsize_t
),
954 new IntegerExp(loc
, n
, Type
.tsize_t
));
955 // Prevent redundant bounds check
956 se
.upperIsInBounds
= true;
957 se
.lowerIsLessThanUpper
= true;
959 ex
= new CallExp(loc
, new IdentifierExp(loc
, Id
.__ArrayDtor
), se
);
961 e
= Expression
.combine(ex
, e
); // combine in reverse order
964 if (e ||
(stc & STC
.disable
))
966 //printf("Building __fieldDtor(), %s\n", e.toChars());
967 auto dd = new DtorDeclaration(declLoc
, Loc
.initial
, stc, Id
.__fieldDtor
);
968 dd.isGenerated
= true;
969 dd.storage_class |
= STC
.inference
;
970 dd.fbody
= new ExpStatement(loc
, e
);
972 dd.dsymbolSemantic(sc
);
977 // Generate list of dtors to call in that order
978 DtorDeclarations dtors
;
979 foreach_reverse (userDtor
; ad
.userDtors
[])
980 dtors
.push(userDtor
);
982 dtors
.push(ad
.fieldDtor
);
983 if (!dtorIsCppPrototype
)
985 // extern(C++) destructors call into super to destruct the full hierarchy
986 ClassDeclaration cldec
= ad
.isClassDeclaration();
987 if (cldec
&& cldec
.classKind
== ClassKind
.cpp
&& cldec
.baseClass
&& cldec
.baseClass
.aggrDtor
)
988 dtors
.push(cldec
.baseClass
.aggrDtor
);
991 // Set/build `ad.aggrDtor`
992 switch (dtors
.length
)
998 // Use the single existing dtor directly as aggregate dtor.
999 // Note that this might be `cldec.baseClass.aggrDtor`.
1000 ad
.aggrDtor
= dtors
[0];
1004 // Build the aggregate destructor, calling all dtors in order.
1005 assert(!dtorIsCppPrototype
);
1006 Expression e
= null;
1008 stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
1009 foreach (FuncDeclaration fd
; dtors
)
1011 stc = mergeFuncAttrs(stc, fd
);
1012 if (stc & STC
.disable
)
1017 Expression ex
= new ThisExp(loc
);
1018 ex
= new DotVarExp(loc
, ex
, fd
, false);
1019 CallExp ce
= new CallExp(loc
, ex
);
1020 ce
.directcall
= true;
1021 e
= Expression
.combine(e
, ce
);
1023 auto dd = new DtorDeclaration(declLoc
, Loc
.initial
, stc, Id
.__aggrDtor
);
1024 dd.isGenerated
= true;
1025 dd.storage_class |
= STC
.inference
;
1026 dd.fbody
= new ExpStatement(loc
, e
);
1027 ad
.members
.push(dd);
1028 dd.dsymbolSemantic(sc
);
1033 // Set/build `ad.dtor`.
1034 // On Windows, the dtor in the vtable is a shim with different signature.
1035 ad
.dtor
= (ad
.aggrDtor
&& ad
.aggrDtor
._linkage
== LINK
.cpp
&& !target
.cpp
.twoDtorInVtable
)
1036 ?
buildWindowsCppDtor(ad
, ad
.aggrDtor
, sc
)
1039 // Add an __xdtor alias to make `ad.dtor` accessible
1042 auto _alias
= new AliasDeclaration(Loc
.initial
, Id
.__xdtor
, ad
.dtor
);
1043 _alias
.dsymbolSemantic(sc
);
1044 ad
.members
.push(_alias
);
1046 ad
.symtab
.update(_alias
); // update forward dtor to correct one
1048 _alias
.addMember(sc
, ad
); // add to symbol table
1051 // Set/build `ad.tidtor`
1052 ad
.tidtor
= buildExternDDtor(ad
, sc
);
1056 * build a shim function around the compound dtor that accepts an argument
1057 * that is used to implement the deleting C++ destructor
1060 * ad = the aggregate that contains the destructor to wrap
1061 * dtor = the destructor to wrap
1062 * sc = the scope in which to analyze the new function
1065 * the shim destructor, semantically analyzed and added to the class as a member
1067 private DtorDeclaration
buildWindowsCppDtor(AggregateDeclaration ad
, DtorDeclaration dtor
, Scope
* sc
)
1069 auto cldec
= ad
.isClassDeclaration();
1070 if (!cldec || cldec
.cppDtorVtblIndex
== -1) // scalar deleting dtor not built for non-virtual dtors
1071 return dtor
; // perhaps also do this if STC.scope_ is set
1073 // generate deleting C++ destructor corresponding to:
1074 // void* C::~C(int del)
1077 // // TODO: if (del) delete (char*)this;
1078 // return (void*) this;
1080 Parameter delparam
= new Parameter(Loc
.initial
, STC
.undefined_
, Type
.tuns32
, Identifier
.idPool("del"), new IntegerExp(dtor
.loc
, 0, Type
.tuns32
), null);
1081 Parameters
* params
= new Parameters
;
1082 params
.push(delparam
);
1083 const stc = dtor
.storage_class
& ~STC
.scope_
; // because we add the `return this;` later
1084 auto ftype
= new TypeFunction(ParameterList(params
), Type
.tvoidptr
, LINK
.cpp
, stc);
1085 auto func
= new DtorDeclaration(dtor
.loc
, dtor
.loc
, stc, Id
.cppdtor
);
1088 // Always generate the function with body, because it is not exported from DLLs.
1089 const loc
= dtor
.loc
;
1090 auto stmts
= new Statements
;
1091 auto call = new CallExp(loc
, dtor
, null);
1092 call.directcall
= true;
1093 stmts
.push(new ExpStatement(loc
, call));
1094 stmts
.push(new ReturnStatement(loc
, new CastExp(loc
, new ThisExp(loc
), Type
.tvoidptr
)));
1095 func
.fbody
= new CompoundStatement(loc
, stmts
);
1096 func
.isGenerated
= true;
1098 auto sc2
= sc
.push();
1099 sc2
.stc &= ~STC
.static_
; // not a static destructor
1100 sc2
.linkage
= LINK
.cpp
;
1102 ad
.members
.push(func
);
1103 func
.addMember(sc2
, ad
);
1104 func
.dsymbolSemantic(sc2
);
1111 * build a shim function around the aggregate dtor that translates
1112 * a C++ destructor to a destructor with extern(D) calling convention
1115 * ad = the aggregate that contains the destructor to wrap
1116 * sc = the scope in which to analyze the new function
1119 * the shim destructor, semantically analyzed and added to the class as a member
1121 private DtorDeclaration
buildExternDDtor(AggregateDeclaration ad
, Scope
* sc
)
1123 auto dtor
= ad
.aggrDtor
;
1127 // Don't try to call `@disable`d dtors
1128 if (dtor
.storage_class
& STC
.disable
)
1131 // Generate shim only when ABI incompatible on target platform
1132 if (dtor
._linkage
!= LINK
.cpp ||
!target
.cpp
.wrapDtorInExternD
)
1135 // generate member function that adjusts calling convention
1136 // (EAX used for 'this' instead of ECX on Windows/stack on others):
1137 // extern(D) void __ticppdtor()
1141 auto ftype
= new TypeFunction(ParameterList(), Type
.tvoid
, LINK
.d
, dtor
.storage_class
);
1142 auto func
= new DtorDeclaration(dtor
.loc
, dtor
.loc
, dtor
.storage_class
, Id
.ticppdtor
);
1145 auto call = new CallExp(dtor
.loc
, dtor
, null);
1146 call.directcall
= true; // non-virtual call Class.__dtor();
1147 func
.fbody
= new ExpStatement(dtor
.loc
, call);
1148 func
.isGenerated
= true;
1149 func
.storage_class |
= STC
.inference
;
1151 auto sc2
= sc
.push();
1152 sc2
.stc &= ~STC
.static_
; // not a static destructor
1153 sc2
.linkage
= LINK
.d
;
1155 ad
.members
.push(func
);
1156 func
.addMember(sc2
, ad
);
1157 func
.dsymbolSemantic(sc2
);
1158 functionSemantic(func
); // to infer attributes
1164 /******************************************
1165 * Create inclusive invariant for struct/class by aggregating
1166 * all the invariants in invs[].
1168 * void __invariant() const [pure nothrow @trusted]
1170 * invs[0](), invs[1](), ...;
1174 FuncDeclaration
buildInv(AggregateDeclaration ad
, Scope
* sc
)
1176 switch (ad
.invs
.length
)
1182 // Don't return invs[0] so it has uniquely generated name.
1186 Expression e
= null;
1187 StorageClass stcx
= 0;
1188 StorageClass
stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
1189 foreach (i
, inv
; ad
.invs
)
1191 stc = mergeFuncAttrs(stc, inv
);
1192 if (stc & STC
.disable
)
1196 const stcy
= (inv
.storage_class
& STC
.synchronized_
) |
1197 (inv
.type
.mod
& MODFlags
.shared_ ? STC
.shared_
: 0);
1200 else if (stcx ^ stcy
)
1204 // currently rejects
1205 .error(inv
.loc
, "%s `%s` mixing invariants with different `shared`/`synchronized` qualifiers is not supported", ad
.kind(), ad
.toPrettyChars());
1210 e
= Expression
.combine(e
, new CallExp(Loc
.initial
, new VarExp(Loc
.initial
, inv
, false)));
1212 auto inv
= new InvariantDeclaration(ad
.loc
, Loc
.initial
, stc | stcx
,
1213 Id
.classInvariant
, new ExpStatement(Loc
.initial
, e
));
1214 ad
.members
.push(inv
);
1215 inv
.dsymbolSemantic(sc
);
1220 /*****************************************
1221 * Create inclusive postblit for struct by aggregating
1222 * all the postblits in postblits[] with the postblits for
1224 * Note the close similarity with AggregateDeclaration::buildDtor(),
1225 * and the ordering changes (runs forward instead of backwards).
1227 FuncDeclaration
buildPostBlit(StructDeclaration sd
, Scope
* sc
)
1229 //printf("buildPostBlit() %s\n", sd.toChars());
1230 if (sd
.isUnionDeclaration())
1233 const hasUserDefinedPosblit
= sd
.postblits
.length
&& !sd
.postblits
[0].isDisabled ?
true : false;
1235 // by default, the storage class of the created postblit
1236 StorageClass
stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
1237 Loc declLoc
= sd
.postblits
.length ? sd
.postblits
[0].loc
: sd
.loc
;
1238 Loc loc
; // internal code should have no loc to prevent coverage
1240 // if any of the postblits are disabled, then the generated postblit
1242 foreach (postblit
; sd
.postblits
)
1243 stc |
= postblit
.storage_class
& STC
.disable
;
1245 VarDeclaration
[] fieldsToDestroy
;
1246 auto postblitCalls
= new Statements();
1247 // iterate through all the struct fields that are not disabled
1248 for (size_t i
= 0; i
< sd
.fields
.length
&& !(stc & STC
.disable
); i
++)
1250 auto structField
= sd
.fields
[i
];
1251 if (structField
.storage_class
& STC
.ref_
)
1253 if (structField
.overlapped
)
1255 // if it's a struct declaration or an array of structs
1256 Type tv
= structField
.type
.baseElemOf();
1257 if (tv
.ty
!= Tstruct
)
1259 auto sdv
= (cast(TypeStruct
)tv
).sym
;
1260 // which has a postblit declaration
1263 assert(!sdv
.isUnionDeclaration());
1265 // if this field's postblit is not `nothrow`, add a `scope(failure)`
1266 // block to destroy any prior successfully postblitted fields should
1267 // this field's postblit fail.
1268 // Don't generate it for betterC code since it cannot throw exceptions.
1269 if (fieldsToDestroy
.length
> 0 && !(cast(TypeFunction
)sdv
.postblit
.type
).isnothrow
&& global
.params
.useExceptions
)
1271 // create a list of destructors that need to be called
1272 Expression
[] dtorCalls
;
1273 foreach(sf
; fieldsToDestroy
)
1276 tv
= sf
.type
.toBasetype();
1277 if (tv
.ty
== Tstruct
)
1281 ex
= new ThisExp(loc
);
1282 ex
= new DotVarExp(loc
, ex
, sf
);
1284 // This is a hack so we can call destructors on const/immutable objects.
1285 ex
= new AddrExp(loc
, ex
);
1286 ex
= new CastExp(loc
, ex
, sf
.type
.mutableOf().pointerTo());
1287 ex
= new PtrExp(loc
, ex
);
1289 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1291 auto sfv
= (cast(TypeStruct
)sf
.type
.baseElemOf()).sym
;
1293 ex
= new DotVarExp(loc
, ex
, sfv
.dtor
, false);
1294 ex
= new CallExp(loc
, ex
);
1300 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1302 const length
= tv
.numberOfElems(loc
);
1304 ex
= new ThisExp(loc
);
1305 ex
= new DotVarExp(loc
, ex
, sf
);
1307 // This is a hack so we can call destructors on const/immutable objects.
1308 ex
= new DotIdExp(loc
, ex
, Id
.ptr
);
1309 ex
= new CastExp(loc
, ex
, sdv
.type
.pointerTo());
1311 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1313 auto se
= new SliceExp(loc
, ex
, new IntegerExp(loc
, 0, Type
.tsize_t
),
1314 new IntegerExp(loc
, length
, Type
.tsize_t
));
1315 // Prevent redundant bounds check
1316 se
.upperIsInBounds
= true;
1317 se
.lowerIsLessThanUpper
= true;
1319 ex
= new CallExp(loc
, new IdentifierExp(loc
, Id
.__ArrayDtor
), se
);
1324 fieldsToDestroy
= [];
1326 // aggregate the destructor calls
1327 auto dtors
= new Statements();
1328 foreach_reverse(dc
; dtorCalls
)
1330 dtors
.push(new ExpStatement(loc
, dc
));
1333 // put destructor calls in a `scope(failure)` block
1334 postblitCalls
.push(new ScopeGuardStatement(loc
, TOK
.onScopeFailure
, new CompoundStatement(loc
, dtors
)));
1337 // perform semantic on the member postblit in order to
1338 // be able to aggregate it later on with the rest of the
1340 functionSemantic(sdv
.postblit
);
1342 stc = mergeFuncAttrs(stc, sdv
.postblit
);
1343 stc = mergeFuncAttrs(stc, sdv
.dtor
);
1345 // if any of the struct member fields has disabled
1346 // its postblit, then `sd` is not copyable, so no
1347 // postblit is generated
1348 if (stc & STC
.disable
)
1350 postblitCalls
.setDim(0);
1355 tv
= structField
.type
.toBasetype();
1356 if (tv
.ty
== Tstruct
)
1358 // this.v.__xpostblit()
1360 ex
= new ThisExp(loc
);
1361 ex
= new DotVarExp(loc
, ex
, structField
);
1363 // This is a hack so we can call postblits on const/immutable objects.
1364 ex
= new AddrExp(loc
, ex
);
1365 ex
= new CastExp(loc
, ex
, structField
.type
.mutableOf().pointerTo());
1366 ex
= new PtrExp(loc
, ex
);
1368 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1370 ex
= new DotVarExp(loc
, ex
, sdv
.postblit
, false);
1371 ex
= new CallExp(loc
, ex
);
1375 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
1377 const length
= tv
.numberOfElems(loc
);
1381 ex
= new ThisExp(loc
);
1382 ex
= new DotVarExp(loc
, ex
, structField
);
1384 // This is a hack so we can call postblits on const/immutable objects.
1385 ex
= new DotIdExp(loc
, ex
, Id
.ptr
);
1386 ex
= new CastExp(loc
, ex
, sdv
.type
.pointerTo());
1388 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1390 auto se
= new SliceExp(loc
, ex
, new IntegerExp(loc
, 0, Type
.tsize_t
),
1391 new IntegerExp(loc
, length
, Type
.tsize_t
));
1392 // Prevent redundant bounds check
1393 se
.upperIsInBounds
= true;
1394 se
.lowerIsLessThanUpper
= true;
1395 ex
= new CallExp(loc
, new IdentifierExp(loc
, Id
.__ArrayPostblit
), se
);
1397 postblitCalls
.push(new ExpStatement(loc
, ex
)); // combine in forward order
1399 /* https://issues.dlang.org/show_bug.cgi?id=10972
1400 * When subsequent field postblit calls fail,
1401 * this field should be destructed for Exception Safety.
1405 functionSemantic(sdv
.dtor
);
1407 // keep a list of fields that need to be destroyed in case
1408 // of a future postblit failure
1409 fieldsToDestroy
~= structField
;
1415 if (sd
.type
.isShared())
1419 // Build our own "postblit" which executes a, but only if needed.
1420 if (postblitCalls
.length ||
(stc & STC
.disable
))
1422 //printf("Building __fieldPostBlit()\n");
1424 auto dd = new PostBlitDeclaration(declLoc
, Loc
.initial
, stc, Id
.__fieldPostblit
);
1425 dd.isGenerated
= true;
1426 dd.storage_class |
= STC
.inference | STC
.scope_
;
1427 dd.fbody
= (stc & STC
.disable
) ?
null : new CompoundStatement(loc
, postblitCalls
);
1428 sd
.postblits
.shift(dd);
1429 sd
.members
.push(dd);
1430 dd.dsymbolSemantic(sc
);
1433 // create __xpostblit, which is the generated postblit
1434 FuncDeclaration xpostblit
= null;
1435 switch (sd
.postblits
.length
)
1441 xpostblit
= sd
.postblits
[0];
1445 Expression e
= null;
1446 stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
1447 foreach (fd
; sd
.postblits
)
1449 stc = mergeFuncAttrs(stc, fd
);
1450 if (stc & STC
.disable
)
1455 Expression ex
= new ThisExp(loc
);
1456 ex
= new DotVarExp(loc
, ex
, fd
, false);
1457 ex
= new CallExp(loc
, ex
);
1458 e
= Expression
.combine(e
, ex
);
1462 auto dd = new PostBlitDeclaration(declLoc
, Loc
.initial
, stc, Id
.__aggrPostblit
);
1463 dd.isGenerated
= true;
1464 dd.storage_class |
= STC
.inference
;
1465 dd.fbody
= new ExpStatement(loc
, e
);
1466 sd
.members
.push(dd);
1467 dd.dsymbolSemantic(sc
);
1472 // Add an __xpostblit alias to make the inclusive postblit accessible
1475 auto _alias
= new AliasDeclaration(Loc
.initial
, Id
.__xpostblit
, xpostblit
);
1476 _alias
.dsymbolSemantic(sc
);
1477 sd
.members
.push(_alias
);
1478 _alias
.addMember(sc
, sd
); // add to symbol table
1483 // we have user defined postblit, so we prioritize it
1484 if (hasUserDefinedPosblit
)
1486 sd
.hasCopyCtor
= false;
1489 // we have fields with postblits, so print deprecations
1490 if (xpostblit
&& !xpostblit
.isDisabled())
1492 deprecation(sd
.loc
, "`struct %s` implicitly-generated postblit hides copy constructor.", sd
.toChars
);
1493 deprecationSupplemental(sd
.loc
, "The field postblit will have priority over the copy constructor.");
1494 deprecationSupplemental(sd
.loc
, "To change this, the postblit should be disabled for `struct %s`", sd
.toChars());
1495 sd
.hasCopyCtor
= false;
1505 * Generates a copy constructor declaration with the specified storage
1506 * class for the parameter and the function.
1509 * sd = the `struct` that contains the copy constructor
1510 * paramStc = the storage class of the copy constructor parameter
1511 * funcStc = the storage class for the copy constructor declaration
1514 * The copy constructor declaration for struct `sd`.
1516 private CtorDeclaration
generateCopyCtorDeclaration(StructDeclaration sd
, const StorageClass paramStc
, const StorageClass funcStc
)
1518 auto fparams
= new Parameters();
1519 auto structType
= sd
.type
;
1520 fparams
.push(new Parameter(Loc
.initial
, paramStc | STC
.ref_ | STC
.return_ | STC
.scope_
, structType
, Id
.p
, null, null));
1521 ParameterList pList
= ParameterList(fparams
);
1522 auto tf
= new TypeFunction(pList
, structType
, LINK
.d
, STC
.ref_
);
1523 auto ccd
= new CtorDeclaration(sd
.loc
, Loc
.initial
, STC
.ref_
, tf
, true);
1524 ccd
.storage_class |
= funcStc
;
1525 ccd
.storage_class |
= STC
.inference
;
1526 ccd
.isGenerated
= true;
1531 * Generates a trivial copy constructor body that simply does memberwise
1534 * this.field1 = rhs.field1;
1535 * this.field2 = rhs.field2;
1539 * sd = the `struct` declaration that contains the copy constructor
1542 * A `CompoundStatement` containing the body of the copy constructor.
1544 private Statement
generateCopyCtorBody(StructDeclaration sd
)
1548 foreach (v
; sd
.fields
)
1550 auto ec
= new AssignExp(loc
,
1551 new DotVarExp(loc
, new ThisExp(loc
), v
),
1552 new DotVarExp(loc
, new IdentifierExp(loc
, Id
.p
), v
));
1553 e
= Expression
.combine(e
, ec
);
1554 //printf("e.toChars = %s\n", e.toChars());
1556 Statement s1
= new ExpStatement(loc
, e
);
1557 return new CompoundStatement(loc
, s1
);
1561 * Determine if a copy constructor is needed for struct sd,
1562 * if the following conditions are met:
1564 * 1. sd does not define a copy constructor
1565 * 2. at least one field of sd defines a copy constructor
1568 * sd = the `struct` for which the copy constructor is generated
1569 * hasCpCtor = set to true if a copy constructor is already present
1572 * `true` if one needs to be generated
1575 bool needCopyCtor(StructDeclaration sd
, out bool hasCpCtor
)
1580 auto ctor
= sd
.search(sd
.loc
, Id
.ctor
);
1583 if (ctor
.isOverloadSet())
1585 if (auto td
= ctor
.isTemplateDeclaration())
1589 CtorDeclaration cpCtor
;
1590 CtorDeclaration rvalueCtor
;
1595 overloadApply(ctor
, (Dsymbol s
)
1597 if (s
.isTemplateDeclaration())
1599 auto ctorDecl
= s
.isCtorDeclaration();
1601 if (ctorDecl
.isCpCtor
)
1608 auto tf
= ctorDecl
.type
.toTypeFunction();
1609 const dim
= tf
.parameterList
.length
;
1610 if (dim
== 1 ||
(dim
> 1 && tf
.parameterList
[1].defaultArg
))
1612 auto param
= tf
.parameterList
[0];
1613 if (param
.type
.mutableOf().unSharedOf() == sd
.type
.mutableOf().unSharedOf())
1615 rvalueCtor
= ctorDecl
;
1625 .error(sd
.loc
, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd
.toChars());
1626 errorSupplemental(rvalueCtor
.loc
,"rvalue constructor defined here");
1627 errorSupplemental(cpCtor
.loc
, "copy constructor defined here");
1634 VarDeclaration fieldWithCpCtor
;
1635 // see if any struct members define a copy constructor
1636 foreach (v
; sd
.fields
)
1638 if (v
.storage_class
& STC
.ref_
)
1643 auto ts
= v
.type
.baseElemOf().isTypeStruct();
1646 if (ts
.sym
.hasCopyCtor
)
1648 fieldWithCpCtor
= v
;
1653 if (fieldWithCpCtor
&& rvalueCtor
)
1655 .error(sd
.loc
, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd
.toChars());
1656 errorSupplemental(rvalueCtor
.loc
,"rvalue constructor defined here");
1657 errorSupplemental(fieldWithCpCtor
.loc
, "field with copy constructor defined here");
1660 else if (!fieldWithCpCtor
)
1666 * Generates a copy constructor if needCopyCtor() returns true.
1667 * The generated copy constructor will be of the form:
1668 * this(ref return scope inout(S) rhs) inout
1670 * this.field1 = rhs.field1;
1671 * this.field2 = rhs.field2;
1676 * sd = the `struct` for which the copy constructor is generated
1677 * sc = the scope where the copy constructor is generated
1680 * `true` if `struct` sd defines a copy constructor (explicitly or generated),
1681 * `false` otherwise.
1683 bool buildCopyCtor(StructDeclaration sd
, Scope
* sc
)
1686 if (!needCopyCtor(sd
, hasCpCtor
))
1689 //printf("generating copy constructor for %s\n", sd.toChars());
1690 const MOD paramMod
= MODFlags
.wild
;
1691 const MOD funcMod
= MODFlags
.wild
;
1692 auto ccd
= generateCopyCtorDeclaration(sd
, ModToStc(paramMod
), ModToStc(funcMod
));
1693 auto copyCtorBody
= generateCopyCtorBody(sd
);
1694 ccd
.fbody
= copyCtorBody
;
1695 sd
.members
.push(ccd
);
1696 ccd
.addMember(sc
, sd
);
1697 const errors
= global
.startGagging();
1698 Scope
* sc2
= sc
.push();
1700 sc2
.linkage
= LINK
.d
;
1701 ccd
.dsymbolSemantic(sc2
);
1704 //printf("ccd semantic: %s\n", ccd.type.toChars());
1706 if (global
.endGagging(errors
) || sd
.isUnionDeclaration())
1708 ccd
.storage_class |
= STC
.disable
;