2 * Builds struct member functions if needed and not defined by the user.
3 * Includes `opEquals`, `opAssign`, post blit, copy constructor and destructor.
5 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/clone.d, _clone.d)
9 * Documentation: https://dlang.org/phobos/dmd_clone.html
10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/clone.d
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
;
43 /*******************************************
44 * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
47 * s1 = storage class to merge into
50 * merged storage class
52 StorageClass
mergeFuncAttrs(StorageClass s1
, const FuncDeclaration f
) pure
56 StorageClass s2
= (f
.storage_class
& STC
.disable
);
58 TypeFunction tf
= cast(TypeFunction
)f
.type
;
59 if (tf
.trust
== TRUST
.safe
)
61 else if (tf
.trust
== TRUST
.system
)
63 else if (tf
.trust
== TRUST
.trusted
)
66 if (tf
.purity
!= PURE
.impure
)
76 StorageClass
stc = (sa
& (STC
.pure_ | STC
.nothrow_ | STC
.nogc
)) |
(so
& STC
.disable
);
80 else if (sa
& STC
.trusted
)
82 else if ((so
& (STC
.trusted | STC
.safe
)) == (STC
.trusted | STC
.safe
))
84 else if (sa
& STC
.safe
)
90 /*******************************************
91 * Check given aggregate actually has an identity opAssign or not.
93 * ad = struct or class
96 * if found, returns FuncDeclaration of opAssign, otherwise null
98 FuncDeclaration
hasIdentityOpAssign(AggregateDeclaration ad
, Scope
* sc
)
100 Dsymbol assign
= search_function(ad
, Id
.assign
);
103 /* check identity opAssign exists
105 scope er
= new NullExp(ad
.loc
, ad
.type
); // dummy rvalue
106 scope el
= new IdentifierExp(ad
.loc
, Id
.p
); // dummy lvalue
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
, &a
, FuncResolveFlag
.quiet
);
120 f
= resolveFuncCall(ad
.loc
, sc
, assign
, null, ad
.type
, &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(STC
.nodtor
, sd
.type
, Id
.p
, null, null));
301 auto tf
= new TypeFunction(ParameterList(fparams
), sd
.handleType(), LINK
.d
, stc | STC
.ref_
);
302 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, Id
.assign
, stc, tf
);
303 fop
.storage_class |
= STC
.inference
;
304 fop
.generated
= true;
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 (sd
.hasIdentityEquals
)
410 /* If any of the fields has an opEquals, then we
413 foreach (VarDeclaration v
; sd
.fields
)
415 if (v
.storage_class
& STC
.ref_
)
419 Type tv
= v
.type
.toBasetype();
420 auto tvbase
= tv
.baseElemOf();
421 if (tvbase
.ty
== Tstruct
)
423 TypeStruct ts
= cast(TypeStruct
)tvbase
;
424 if (ts
.sym
.isUnionDeclaration())
426 if (needOpEquals(ts
.sym
))
429 if (tvbase
.isfloating())
431 // This is necessray for:
432 // 1. comparison of +0.0 and -0.0 should be true.
433 // 2. comparison of NANs should be false always.
436 if (tvbase
.ty
== Tarray
)
438 if (tvbase
.ty
== Taarray
)
440 if (tvbase
.ty
== Tclass
)
444 //printf("\tdontneed\n");
447 //printf("\tneed\n");
451 /*******************************************
452 * Check given aggregate actually has an identity opEquals or not.
454 private FuncDeclaration
hasIdentityOpEquals(AggregateDeclaration ad
, Scope
* sc
)
457 if (Dsymbol eq
= search_function(ad
, Id
.eq
))
459 /* check identity opEquals exists
461 scope er
= new NullExp(ad
.loc
, null); // dummy rvalue
462 scope el
= new IdentifierExp(ad
.loc
, Id
.p
); // dummy lvalue
466 bool hasIt(Type tthis
)
468 const errors
= global
.startGagging(); // Do not report errors, even if the template opAssign fbody makes it
473 FuncDeclaration
rfc(Expression e
)
477 return resolveFuncCall(ad
.loc
, sc
, eq
, null, tthis
, &a
, FuncResolveFlag
.quiet
);
485 global
.endGagging(errors
);
490 if (hasIt(ad
.type
) ||
491 hasIt(ad
.type
.constOf()) ||
492 hasIt(ad
.type
.immutableOf()) ||
493 hasIt(ad
.type
.sharedOf()) ||
494 hasIt(ad
.type
.sharedConstOf()))
503 /******************************************
504 * Build opEquals for struct.
505 * const bool opEquals(const S s) { ... }
507 * By fixing https://issues.dlang.org/show_bug.cgi?id=3789
508 * opEquals is changed to be never implicitly generated.
509 * Now, struct objects comparison s1 == s2 is translated to:
510 * s1.tupleof == s2.tupleof
511 * to calculate structural equality. See EqualExp.op_overload.
513 FuncDeclaration
buildOpEquals(StructDeclaration sd
, Scope
* sc
)
515 if (hasIdentityOpEquals(sd
, sc
))
517 sd
.hasIdentityEquals
= true;
522 /******************************************
523 * Build __xopEquals for TypeInfo_Struct
524 * static bool __xopEquals(ref const S p, ref const S q)
529 * This is called by TypeInfo.equals(p1, p2). If the struct does not support
530 * const objects comparison, it will throw "not implemented" Error in runtime.
532 FuncDeclaration
buildXopEquals(StructDeclaration sd
, Scope
* sc
)
534 if (!needOpEquals(sd
))
535 return null; // bitwise comparison would work
537 //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars());
538 if (Dsymbol eq
= search_function(sd
, Id
.eq
))
540 if (FuncDeclaration fd
= eq
.isFuncDeclaration())
542 TypeFunction tfeqptr
;
545 /* const bool opEquals(ref const S s);
547 auto parameters
= new Parameters();
548 parameters
.push(new Parameter(STC
.ref_ | STC
.const_
, sd
.type
, null, null, null));
549 tfeqptr
= new TypeFunction(ParameterList(parameters
), Type
.tbool
, LINK
.d
);
550 tfeqptr
.mod
= MODFlags
.const_
;
551 tfeqptr
= cast(TypeFunction
)tfeqptr
.typeSemantic(Loc
.initial
, &scx
);
553 fd
= fd
.overloadExactMatch(tfeqptr
);
561 Identifier id
= Identifier
.idPool("_xopEquals");
562 Expression e
= new IdentifierExp(sd
.loc
, Id
.empty
);
563 e
= new DotIdExp(sd
.loc
, e
, Id
.object
);
564 e
= new DotIdExp(sd
.loc
, e
, id
);
565 e
= e
.expressionSemantic(sc
);
566 Dsymbol s
= getDsymbol(e
);
568 sd
.xerreq
= s
.isFuncDeclaration();
570 Loc declLoc
; // loc is unnecessary so __xopEquals is never called directly
571 Loc loc
; // loc is unnecessary so errors are gagged
572 auto parameters
= new Parameters();
573 parameters
.push(new Parameter(STC
.ref_ | STC
.const_
, sd
.type
, Id
.p
, null, null))
574 .push(new Parameter(STC
.ref_ | STC
.const_
, sd
.type
, Id
.q
, null, null));
575 auto tf
= new TypeFunction(ParameterList(parameters
), Type
.tbool
, LINK
.d
);
576 Identifier id
= Id
.xopEquals
;
577 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, id
, STC
.static_
, tf
);
578 fop
.generated
= true;
579 Expression e1
= new IdentifierExp(loc
, Id
.p
);
580 Expression e2
= new IdentifierExp(loc
, Id
.q
);
581 Expression e
= new EqualExp(EXP
.equal
, loc
, e1
, e2
);
582 fop
.fbody
= new ReturnStatement(loc
, e
);
583 uint errors
= global
.startGagging(); // Do not report errors
584 Scope
* sc2
= sc
.push();
586 sc2
.linkage
= LINK
.d
;
587 fop
.dsymbolSemantic(sc2
);
590 if (global
.endGagging(errors
)) // if errors happened
595 /******************************************
596 * Build __xopCmp for TypeInfo_Struct
597 * static bool __xopCmp(ref const S p, ref const S q)
602 * This is called by TypeInfo.compare(p1, p2). If the struct does not support
603 * const objects comparison, it will throw "not implemented" Error in runtime.
605 FuncDeclaration
buildXopCmp(StructDeclaration sd
, Scope
* sc
)
607 //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
608 if (Dsymbol
cmp = search_function(sd
, Id
.cmp))
610 if (FuncDeclaration fd
= cmp.isFuncDeclaration())
612 TypeFunction tfcmpptr
;
615 /* const int opCmp(ref const S s);
617 auto parameters
= new Parameters();
618 parameters
.push(new Parameter(STC
.ref_ | STC
.const_
, sd
.type
, null, null, null));
619 tfcmpptr
= new TypeFunction(ParameterList(parameters
), Type
.tint32
, LINK
.d
);
620 tfcmpptr
.mod
= MODFlags
.const_
;
621 tfcmpptr
= cast(TypeFunction
)tfcmpptr
.typeSemantic(Loc
.initial
, &scx
);
623 fd
= fd
.overloadExactMatch(tfcmpptr
);
630 version (none
) // FIXME: doesn't work for recursive alias this
632 /* Check opCmp member exists.
633 * Consider 'alias this', but except opDispatch.
635 Expression e
= new DsymbolExp(sd
.loc
, sd
);
636 e
= new DotIdExp(sd
.loc
, e
, Id
.cmp);
637 Scope
* sc2
= sc
.push();
638 e
= e
.trySemantic(sc2
);
645 case EXP
.overloadSet
:
646 s
= (cast(OverExp
)e
).vars
;
649 s
= (cast(ScopeExp
)e
).sds
;
652 s
= (cast(VarExp
)e
).var
;
657 if (!s || s
.ident
!= Id
.cmp)
658 e
= null; // there's no valid member 'opCmp'
661 return null; // bitwise comparison would work
662 /* Essentially, a struct which does not define opCmp is not comparable.
663 * At this time, typeid(S).compare might be correct that throwing "not implement" Error.
664 * But implementing it would break existing code, such as:
666 * struct S { int value; } // no opCmp
667 * int[S] aa; // Currently AA key uses bitwise comparison
668 * // (It's default behavior of TypeInfo_Strust.compare).
670 * Not sure we should fix this inconsistency, so just keep current behavior.
681 Identifier id
= Identifier
.idPool("_xopCmp");
682 Expression e
= new IdentifierExp(sd
.loc
, Id
.empty
);
683 e
= new DotIdExp(sd
.loc
, e
, Id
.object
);
684 e
= new DotIdExp(sd
.loc
, e
, id
);
685 e
= e
.expressionSemantic(sc
);
686 Dsymbol s
= getDsymbol(e
);
688 sd
.xerrcmp
= s
.isFuncDeclaration();
690 Loc declLoc
; // loc is unnecessary so __xopCmp is never called directly
691 Loc loc
; // loc is unnecessary so errors are gagged
692 auto parameters
= new Parameters();
693 parameters
.push(new Parameter(STC
.ref_ | STC
.const_
, sd
.type
, Id
.p
, null, null));
694 parameters
.push(new Parameter(STC
.ref_ | STC
.const_
, sd
.type
, Id
.q
, null, null));
695 auto tf
= new TypeFunction(ParameterList(parameters
), Type
.tint32
, LINK
.d
);
696 Identifier id
= Id
.xopCmp
;
697 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, id
, STC
.static_
, tf
);
698 fop
.generated
= true;
699 Expression e1
= new IdentifierExp(loc
, Id
.p
);
700 Expression e2
= new IdentifierExp(loc
, Id
.q
);
702 Expression e
= new CallExp(loc
, new DotIdExp(loc
, e1
, Id
.cmp), e2
);
704 Expression e
= new CallExp(loc
, new DotIdExp(loc
, e2
, Id
.cmp), e1
);
705 fop
.fbody
= new ReturnStatement(loc
, e
);
706 uint errors
= global
.startGagging(); // Do not report errors
707 Scope
* sc2
= sc
.push();
709 sc2
.linkage
= LINK
.d
;
710 fop
.dsymbolSemantic(sc2
);
713 if (global
.endGagging(errors
)) // if errors happened
718 /*******************************************
719 * We need a toHash for the struct if
720 * any fields has a toHash.
721 * Generate one if a user-specified one does not exist.
723 private bool needToHash(StructDeclaration sd
)
725 //printf("StructDeclaration::needToHash() %s\n", sd.toChars());
726 if (sd
.isUnionDeclaration())
731 /* If any of the fields has an toHash, then we
734 foreach (VarDeclaration v
; sd
.fields
)
736 if (v
.storage_class
& STC
.ref_
)
740 Type tv
= v
.type
.toBasetype();
741 auto tvbase
= tv
.baseElemOf();
742 if (tvbase
.ty
== Tstruct
)
744 TypeStruct ts
= cast(TypeStruct
)tvbase
;
745 if (ts
.sym
.isUnionDeclaration())
747 if (needToHash(ts
.sym
))
750 if (tvbase
.isfloating())
752 /* This is necessary because comparison of +0.0 and -0.0 should be true,
753 * i.e. not a bit compare.
757 if (tvbase
.ty
== Tarray
)
759 if (tvbase
.ty
== Taarray
)
761 if (tvbase
.ty
== Tclass
)
765 //printf("\tdontneed\n");
768 //printf("\tneed\n");
772 /******************************************
773 * Build __xtoHash for non-bitwise hashing
774 * static hash_t xtoHash(ref const S p) nothrow @trusted;
776 FuncDeclaration
buildXtoHash(StructDeclaration sd
, Scope
* sc
)
778 if (Dsymbol s
= search_function(sd
, Id
.tohash
))
780 __gshared TypeFunction tftohash
;
783 tftohash
= new TypeFunction(ParameterList(), Type
.thash_t
, LINK
.d
);
784 tftohash
.mod
= MODFlags
.const_
;
785 tftohash
= cast(TypeFunction
)tftohash
.merge();
787 if (FuncDeclaration fd
= s
.isFuncDeclaration())
789 fd
= fd
.overloadExactMatch(tftohash
);
797 /* The trouble is that the following code relies on .tupleof, but .tupleof
798 * is not allowed for C files. If we allow it for C files, then that turns on
799 * the other D properties, too, such as .dup which will then conflict with allowed
801 * One way to fix it is to replace the following foreach and .tupleof with C
802 * statements and expressions.
803 * But, it's debatable whether C structs should even need toHash().
804 * Note that it would only be necessary if it has floating point fields.
805 * For now, we'll just not generate a toHash() for C files.
807 if (sc
.flags
& SCOPE
.Cfile
)
810 //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
811 Loc declLoc
; // loc is unnecessary so __xtoHash is never called directly
812 Loc loc
; // internal code should have no loc to prevent coverage
813 auto parameters
= new Parameters();
814 parameters
.push(new Parameter(STC
.ref_ | STC
.const_
, sd
.type
, Id
.p
, null, null));
815 auto tf
= new TypeFunction(ParameterList(parameters
), Type
.thash_t
, LINK
.d
, STC
.nothrow_ | STC
.trusted
);
816 Identifier id
= Id
.xtoHash
;
817 auto fop
= new FuncDeclaration(declLoc
, Loc
.initial
, id
, STC
.static_
, tf
);
818 fop
.generated
= true;
820 /* Do memberwise hashing.
822 * If sd is a nested struct, and if it's nested in a class, the calculated
823 * hash value will also contain the result of parent class's toHash().
826 ".object.size_t h = 0;" ~
827 "foreach (i, T; typeof(p.tupleof))" ~
828 // workaround https://issues.dlang.org/show_bug.cgi?id=17968
829 " static if(is(T* : const(.object.Object)*)) " ~
830 " h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
832 " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
834 fop
.fbody
= new CompileStatement(loc
, new StringExp(loc
, code
));
835 Scope
* sc2
= sc
.push();
837 sc2
.linkage
= LINK
.d
;
838 fop
.dsymbolSemantic(sc2
);
842 //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars());
846 /*****************************************
847 * Create aggregate destructor for struct/class by aggregating
848 * all the destructors in userDtors[] with the destructors for
850 * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
852 * ad = struct or class to build destructor for
855 * Close similarity with StructDeclaration::buildPostBlit(),
856 * and the ordering changes (runs backward instead of forwards).
858 void buildDtors(AggregateDeclaration ad
, Scope
* sc
)
860 //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
861 if (ad
.isUnionDeclaration())
862 return; // unions don't have destructors
864 StorageClass
stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
865 Loc declLoc
= ad
.userDtors
.dim ? ad
.userDtors
[0].loc
: ad
.loc
;
866 Loc loc
; // internal code should have no loc to prevent coverage
867 FuncDeclaration xdtor_fwd
= null;
869 // Build the field destructor (`ad.fieldDtor`), if needed.
870 // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building.
871 const bool dtorIsCppPrototype
= ad
.userDtors
.dim
&& ad
.userDtors
[0].linkage
== LINK
.cpp
&& !ad
.userDtors
[0].fbody
;
872 if (!dtorIsCppPrototype
)
875 for (size_t i
= 0; i
< ad
.fields
.dim
; i
++)
877 auto v
= ad
.fields
[i
];
878 if (v
.storage_class
& STC
.ref_
)
882 auto tv
= v
.type
.baseElemOf();
883 if (tv
.ty
!= Tstruct
)
885 auto sdv
= (cast(TypeStruct
)tv
).sym
;
889 // fix: https://issues.dlang.org/show_bug.cgi?id=17257
890 // braces for shrink wrapping scope of a
892 xdtor_fwd
= sdv
.dtor
; // this dtor is temporary it could be anything
893 auto a
= new AliasDeclaration(Loc
.initial
, Id
.__xdtor
, xdtor_fwd
);
894 a
.addMember(sc
, ad
); // temporarily add to symbol table
897 sdv
.dtor
.functionSemantic();
899 stc = mergeFuncAttrs(stc, sdv
.dtor
);
900 if (stc & STC
.disable
)
907 tv
= v
.type
.toBasetype();
908 if (tv
.ty
== Tstruct
)
912 ex
= new ThisExp(loc
);
913 ex
= new DotVarExp(loc
, ex
, v
);
915 // This is a hack so we can call destructors on const/immutable objects.
916 // Do it as a type 'paint'.
917 ex
= new CastExp(loc
, ex
, v
.type
.mutableOf());
919 stc = (stc & ~STC
.safe
) | STC
.trusted
;
921 ex
= new DotVarExp(loc
, ex
, sdv
.dtor
, false);
922 ex
= new CallExp(loc
, ex
);
926 // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
928 const n
= tv
.numberOfElems(loc
);
932 ex
= new ThisExp(loc
);
933 ex
= new DotVarExp(loc
, ex
, v
);
935 // This is a hack so we can call destructors on const/immutable objects.
936 ex
= new DotIdExp(loc
, ex
, Id
.ptr
);
937 ex
= new CastExp(loc
, ex
, sdv
.type
.pointerTo());
939 stc = (stc & ~STC
.safe
) | STC
.trusted
;
941 ex
= new SliceExp(loc
, ex
, new IntegerExp(loc
, 0, Type
.tsize_t
),
942 new IntegerExp(loc
, n
, Type
.tsize_t
));
943 // Prevent redundant bounds check
944 (cast(SliceExp
)ex
).upperIsInBounds
= true;
945 (cast(SliceExp
)ex
).lowerIsLessThanUpper
= true;
947 ex
= new CallExp(loc
, new IdentifierExp(loc
, Id
.__ArrayDtor
), ex
);
949 e
= Expression
.combine(ex
, e
); // combine in reverse order
952 if (e ||
(stc & STC
.disable
))
954 //printf("Building __fieldDtor(), %s\n", e.toChars());
955 auto dd = new DtorDeclaration(declLoc
, Loc
.initial
, stc, Id
.__fieldDtor
);
957 dd.storage_class |
= STC
.inference
;
958 dd.fbody
= new ExpStatement(loc
, e
);
960 dd.dsymbolSemantic(sc
);
965 // Generate list of dtors to call in that order
966 DtorDeclarations dtors
;
967 foreach_reverse (userDtor
; ad
.userDtors
[])
968 dtors
.push(userDtor
);
970 dtors
.push(ad
.fieldDtor
);
971 if (!dtorIsCppPrototype
)
973 // extern(C++) destructors call into super to destruct the full hierarchy
974 ClassDeclaration cldec
= ad
.isClassDeclaration();
975 if (cldec
&& cldec
.classKind
== ClassKind
.cpp
&& cldec
.baseClass
&& cldec
.baseClass
.aggrDtor
)
976 dtors
.push(cldec
.baseClass
.aggrDtor
);
979 // Set/build `ad.aggrDtor`
986 // Use the single existing dtor directly as aggregate dtor.
987 // Note that this might be `cldec.baseClass.aggrDtor`.
988 ad
.aggrDtor
= dtors
[0];
992 // Build the aggregate destructor, calling all dtors in order.
993 assert(!dtorIsCppPrototype
);
996 stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
997 foreach (FuncDeclaration fd
; dtors
)
999 stc = mergeFuncAttrs(stc, fd
);
1000 if (stc & STC
.disable
)
1005 Expression ex
= new ThisExp(loc
);
1006 ex
= new DotVarExp(loc
, ex
, fd
, false);
1007 CallExp ce
= new CallExp(loc
, ex
);
1008 ce
.directcall
= true;
1009 e
= Expression
.combine(e
, ce
);
1011 auto dd = new DtorDeclaration(declLoc
, Loc
.initial
, stc, Id
.__aggrDtor
);
1012 dd.generated
= true;
1013 dd.storage_class |
= STC
.inference
;
1014 dd.fbody
= new ExpStatement(loc
, e
);
1015 ad
.members
.push(dd);
1016 dd.dsymbolSemantic(sc
);
1021 // Set/build `ad.dtor`.
1022 // On Windows, the dtor in the vtable is a shim with different signature.
1023 ad
.dtor
= (ad
.aggrDtor
&& ad
.aggrDtor
.linkage
== LINK
.cpp
&& !target
.cpp
.twoDtorInVtable
)
1024 ?
buildWindowsCppDtor(ad
, ad
.aggrDtor
, sc
)
1027 // Add an __xdtor alias to make `ad.dtor` accessible
1030 auto _alias
= new AliasDeclaration(Loc
.initial
, Id
.__xdtor
, ad
.dtor
);
1031 _alias
.dsymbolSemantic(sc
);
1032 ad
.members
.push(_alias
);
1034 ad
.symtab
.update(_alias
); // update forward dtor to correct one
1036 _alias
.addMember(sc
, ad
); // add to symbol table
1039 // Set/build `ad.tidtor`
1040 ad
.tidtor
= buildExternDDtor(ad
, sc
);
1044 * build a shim function around the compound dtor that accepts an argument
1045 * that is used to implement the deleting C++ destructor
1048 * ad = the aggregate that contains the destructor to wrap
1049 * dtor = the destructor to wrap
1050 * sc = the scope in which to analyze the new function
1053 * the shim destructor, semantically analyzed and added to the class as a member
1055 private DtorDeclaration
buildWindowsCppDtor(AggregateDeclaration ad
, DtorDeclaration dtor
, Scope
* sc
)
1057 auto cldec
= ad
.isClassDeclaration();
1058 if (!cldec || cldec
.cppDtorVtblIndex
== -1) // scalar deleting dtor not built for non-virtual dtors
1061 // generate deleting C++ destructor corresponding to:
1062 // void* C::~C(int del)
1065 // // TODO: if (del) delete (char*)this;
1066 // return (void*) this;
1068 Parameter delparam
= new Parameter(STC
.undefined_
, Type
.tuns32
, Identifier
.idPool("del"), new IntegerExp(dtor
.loc
, 0, Type
.tuns32
), null);
1069 Parameters
* params
= new Parameters
;
1070 params
.push(delparam
);
1071 auto ftype
= new TypeFunction(ParameterList(params
), Type
.tvoidptr
, LINK
.cpp
, dtor
.storage_class
);
1072 auto func
= new DtorDeclaration(dtor
.loc
, dtor
.loc
, dtor
.storage_class
, Id
.cppdtor
);
1075 // Always generate the function with body, because it is not exported from DLLs.
1076 const loc
= dtor
.loc
;
1077 auto stmts
= new Statements
;
1078 auto call = new CallExp(loc
, dtor
, null);
1079 call.directcall
= true;
1080 stmts
.push(new ExpStatement(loc
, call));
1081 stmts
.push(new ReturnStatement(loc
, new CastExp(loc
, new ThisExp(loc
), Type
.tvoidptr
)));
1082 func
.fbody
= new CompoundStatement(loc
, stmts
);
1083 func
.generated
= true;
1085 auto sc2
= sc
.push();
1086 sc2
.stc &= ~STC
.static_
; // not a static destructor
1087 sc2
.linkage
= LINK
.cpp
;
1089 ad
.members
.push(func
);
1090 func
.addMember(sc2
, ad
);
1091 func
.dsymbolSemantic(sc2
);
1098 * build a shim function around the aggregate dtor that translates
1099 * a C++ destructor to a destructor with extern(D) calling convention
1102 * ad = the aggregate that contains the destructor to wrap
1103 * sc = the scope in which to analyze the new function
1106 * the shim destructor, semantically analyzed and added to the class as a member
1108 private DtorDeclaration
buildExternDDtor(AggregateDeclaration ad
, Scope
* sc
)
1110 auto dtor
= ad
.aggrDtor
;
1114 // Generate shim only when ABI incompatible on target platform
1115 if (ad
.classKind
!= ClassKind
.cpp ||
!target
.cpp
.wrapDtorInExternD
)
1118 // generate member function that adjusts calling convention
1119 // (EAX used for 'this' instead of ECX on Windows/stack on others):
1120 // extern(D) void __ticppdtor()
1124 auto ftype
= new TypeFunction(ParameterList(), Type
.tvoid
, LINK
.d
, dtor
.storage_class
);
1125 auto func
= new DtorDeclaration(dtor
.loc
, dtor
.loc
, dtor
.storage_class
, Id
.ticppdtor
);
1128 auto call = new CallExp(dtor
.loc
, dtor
, null);
1129 call.directcall
= true; // non-virtual call Class.__dtor();
1130 func
.fbody
= new ExpStatement(dtor
.loc
, call);
1131 func
.generated
= true;
1132 func
.storage_class |
= STC
.inference
;
1134 auto sc2
= sc
.push();
1135 sc2
.stc &= ~STC
.static_
; // not a static destructor
1136 sc2
.linkage
= LINK
.d
;
1138 ad
.members
.push(func
);
1139 func
.addMember(sc2
, ad
);
1140 func
.dsymbolSemantic(sc2
);
1141 func
.functionSemantic(); // to infer attributes
1147 /******************************************
1148 * Create inclusive invariant for struct/class by aggregating
1149 * all the invariants in invs[].
1151 * void __invariant() const [pure nothrow @trusted]
1153 * invs[0](), invs[1](), ...;
1157 FuncDeclaration
buildInv(AggregateDeclaration ad
, Scope
* sc
)
1159 switch (ad
.invs
.dim
)
1165 // Don't return invs[0] so it has uniquely generated name.
1169 Expression e
= null;
1170 StorageClass stcx
= 0;
1171 StorageClass
stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
1172 foreach (i
, inv
; ad
.invs
)
1174 stc = mergeFuncAttrs(stc, inv
);
1175 if (stc & STC
.disable
)
1179 const stcy
= (inv
.storage_class
& STC
.synchronized_
) |
1180 (inv
.type
.mod
& MODFlags
.shared_ ? STC
.shared_
: 0);
1183 else if (stcx ^ stcy
)
1187 // currently rejects
1188 ad
.error(inv
.loc
, "mixing invariants with different `shared`/`synchronized` qualifiers is not supported");
1193 e
= Expression
.combine(e
, new CallExp(Loc
.initial
, new VarExp(Loc
.initial
, inv
, false)));
1195 auto inv
= new InvariantDeclaration(ad
.loc
, Loc
.initial
, stc | stcx
,
1196 Id
.classInvariant
, new ExpStatement(Loc
.initial
, e
));
1197 ad
.members
.push(inv
);
1198 inv
.dsymbolSemantic(sc
);
1203 /*****************************************
1204 * Create inclusive postblit for struct by aggregating
1205 * all the postblits in postblits[] with the postblits for
1207 * Note the close similarity with AggregateDeclaration::buildDtor(),
1208 * and the ordering changes (runs forward instead of backwards).
1210 FuncDeclaration
buildPostBlit(StructDeclaration sd
, Scope
* sc
)
1212 //printf("buildPostBlit() %s\n", sd.toChars());
1213 if (sd
.isUnionDeclaration())
1216 const hasUserDefinedPosblit
= sd
.postblits
.dim
&& !sd
.postblits
[0].isDisabled ?
true : false;
1218 // by default, the storage class of the created postblit
1219 StorageClass
stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
1220 Loc declLoc
= sd
.postblits
.dim ? sd
.postblits
[0].loc
: sd
.loc
;
1221 Loc loc
; // internal code should have no loc to prevent coverage
1223 // if any of the postblits are disabled, then the generated postblit
1225 foreach (postblit
; sd
.postblits
)
1226 stc |
= postblit
.storage_class
& STC
.disable
;
1228 VarDeclaration
[] fieldsToDestroy
;
1229 auto postblitCalls
= new Statements();
1230 // iterate through all the struct fields that are not disabled
1231 for (size_t i
= 0; i
< sd
.fields
.dim
&& !(stc & STC
.disable
); i
++)
1233 auto structField
= sd
.fields
[i
];
1234 if (structField
.storage_class
& STC
.ref_
)
1236 if (structField
.overlapped
)
1238 // if it's a struct declaration or an array of structs
1239 Type tv
= structField
.type
.baseElemOf();
1240 if (tv
.ty
!= Tstruct
)
1242 auto sdv
= (cast(TypeStruct
)tv
).sym
;
1243 // which has a postblit declaration
1246 assert(!sdv
.isUnionDeclaration());
1248 // if this field's postblit is not `nothrow`, add a `scope(failure)`
1249 // block to destroy any prior successfully postblitted fields should
1250 // this field's postblit fail
1251 if (fieldsToDestroy
.length
> 0 && !(cast(TypeFunction
)sdv
.postblit
.type
).isnothrow
)
1253 // create a list of destructors that need to be called
1254 Expression
[] dtorCalls
;
1255 foreach(sf
; fieldsToDestroy
)
1258 tv
= sf
.type
.toBasetype();
1259 if (tv
.ty
== Tstruct
)
1263 ex
= new ThisExp(loc
);
1264 ex
= new DotVarExp(loc
, ex
, sf
);
1266 // This is a hack so we can call destructors on const/immutable objects.
1267 ex
= new AddrExp(loc
, ex
);
1268 ex
= new CastExp(loc
, ex
, sf
.type
.mutableOf().pointerTo());
1269 ex
= new PtrExp(loc
, ex
);
1271 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1273 auto sfv
= (cast(TypeStruct
)sf
.type
.baseElemOf()).sym
;
1275 ex
= new DotVarExp(loc
, ex
, sfv
.dtor
, false);
1276 ex
= new CallExp(loc
, ex
);
1282 // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
1284 const length
= tv
.numberOfElems(loc
);
1286 ex
= new ThisExp(loc
);
1287 ex
= new DotVarExp(loc
, ex
, sf
);
1289 // This is a hack so we can call destructors on const/immutable objects.
1290 ex
= new DotIdExp(loc
, ex
, Id
.ptr
);
1291 ex
= new CastExp(loc
, ex
, sdv
.type
.pointerTo());
1293 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1295 auto se
= new SliceExp(loc
, ex
, new IntegerExp(loc
, 0, Type
.tsize_t
),
1296 new IntegerExp(loc
, length
, Type
.tsize_t
));
1297 // Prevent redundant bounds check
1298 se
.upperIsInBounds
= true;
1299 se
.lowerIsLessThanUpper
= true;
1301 ex
= new CallExp(loc
, new IdentifierExp(loc
, Id
.__ArrayDtor
), se
);
1306 fieldsToDestroy
= [];
1308 // aggregate the destructor calls
1309 auto dtors
= new Statements();
1310 foreach_reverse(dc
; dtorCalls
)
1312 dtors
.push(new ExpStatement(loc
, dc
));
1315 // put destructor calls in a `scope(failure)` block
1316 postblitCalls
.push(new ScopeGuardStatement(loc
, TOK
.onScopeFailure
, new CompoundStatement(loc
, dtors
)));
1319 // perform semantic on the member postblit in order to
1320 // be able to aggregate it later on with the rest of the
1322 sdv
.postblit
.functionSemantic();
1324 stc = mergeFuncAttrs(stc, sdv
.postblit
);
1325 stc = mergeFuncAttrs(stc, sdv
.dtor
);
1327 // if any of the struct member fields has disabled
1328 // its postblit, then `sd` is not copyable, so no
1329 // postblit is generated
1330 if (stc & STC
.disable
)
1332 postblitCalls
.setDim(0);
1337 tv
= structField
.type
.toBasetype();
1338 if (tv
.ty
== Tstruct
)
1340 // this.v.__xpostblit()
1342 ex
= new ThisExp(loc
);
1343 ex
= new DotVarExp(loc
, ex
, structField
);
1345 // This is a hack so we can call postblits on const/immutable objects.
1346 ex
= new AddrExp(loc
, ex
);
1347 ex
= new CastExp(loc
, ex
, structField
.type
.mutableOf().pointerTo());
1348 ex
= new PtrExp(loc
, ex
);
1350 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1352 ex
= new DotVarExp(loc
, ex
, sdv
.postblit
, false);
1353 ex
= new CallExp(loc
, ex
);
1357 // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
1359 const length
= tv
.numberOfElems(loc
);
1363 ex
= new ThisExp(loc
);
1364 ex
= new DotVarExp(loc
, ex
, structField
);
1366 // This is a hack so we can call postblits on const/immutable objects.
1367 ex
= new DotIdExp(loc
, ex
, Id
.ptr
);
1368 ex
= new CastExp(loc
, ex
, sdv
.type
.pointerTo());
1370 stc = (stc & ~STC
.safe
) | STC
.trusted
;
1372 auto se
= new SliceExp(loc
, ex
, new IntegerExp(loc
, 0, Type
.tsize_t
),
1373 new IntegerExp(loc
, length
, Type
.tsize_t
));
1374 // Prevent redundant bounds check
1375 se
.upperIsInBounds
= true;
1376 se
.lowerIsLessThanUpper
= true;
1377 ex
= new CallExp(loc
, new IdentifierExp(loc
, Id
.__ArrayPostblit
), se
);
1379 postblitCalls
.push(new ExpStatement(loc
, ex
)); // combine in forward order
1381 /* https://issues.dlang.org/show_bug.cgi?id=10972
1382 * When subsequent field postblit calls fail,
1383 * this field should be destructed for Exception Safety.
1387 sdv
.dtor
.functionSemantic();
1389 // keep a list of fields that need to be destroyed in case
1390 // of a future postblit failure
1391 fieldsToDestroy
~= structField
;
1397 if (sd
.type
.isShared())
1401 // Build our own "postblit" which executes a, but only if needed.
1402 if (postblitCalls
.dim ||
(stc & STC
.disable
))
1404 //printf("Building __fieldPostBlit()\n");
1406 auto dd = new PostBlitDeclaration(declLoc
, Loc
.initial
, stc, Id
.__fieldPostblit
);
1407 dd.generated
= true;
1408 dd.storage_class |
= STC
.inference | STC
.scope_
;
1409 dd.fbody
= (stc & STC
.disable
) ?
null : new CompoundStatement(loc
, postblitCalls
);
1410 sd
.postblits
.shift(dd);
1411 sd
.members
.push(dd);
1412 dd.dsymbolSemantic(sc
);
1415 // create __xpostblit, which is the generated postblit
1416 FuncDeclaration xpostblit
= null;
1417 switch (sd
.postblits
.dim
)
1423 xpostblit
= sd
.postblits
[0];
1427 Expression e
= null;
1428 stc = STC
.safe | STC
.nothrow_ | STC
.pure_ | STC
.nogc
;
1429 foreach (fd
; sd
.postblits
)
1431 stc = mergeFuncAttrs(stc, fd
);
1432 if (stc & STC
.disable
)
1437 Expression ex
= new ThisExp(loc
);
1438 ex
= new DotVarExp(loc
, ex
, fd
, false);
1439 ex
= new CallExp(loc
, ex
);
1440 e
= Expression
.combine(e
, ex
);
1444 auto dd = new PostBlitDeclaration(declLoc
, Loc
.initial
, stc, Id
.__aggrPostblit
);
1445 dd.generated
= true;
1446 dd.storage_class |
= STC
.inference
;
1447 dd.fbody
= new ExpStatement(loc
, e
);
1448 sd
.members
.push(dd);
1449 dd.dsymbolSemantic(sc
);
1454 // Add an __xpostblit alias to make the inclusive postblit accessible
1457 auto _alias
= new AliasDeclaration(Loc
.initial
, Id
.__xpostblit
, xpostblit
);
1458 _alias
.dsymbolSemantic(sc
);
1459 sd
.members
.push(_alias
);
1460 _alias
.addMember(sc
, sd
); // add to symbol table
1465 // we have user defined postblit, so we prioritize it
1466 if (hasUserDefinedPosblit
)
1468 sd
.hasCopyCtor
= false;
1471 // we have fields with postblits, so print deprecations
1472 if (xpostblit
&& !xpostblit
.isDisabled())
1474 deprecation(sd
.loc
, "`struct %s` implicitly-generated postblit hides copy constructor.", sd
.toChars
);
1475 deprecationSupplemental(sd
.loc
, "The field postblit will have priority over the copy constructor.");
1476 deprecationSupplemental(sd
.loc
, "To change this, the postblit should be disabled for `struct %s`", sd
.toChars());
1477 sd
.hasCopyCtor
= false;
1487 * Generates a copy constructor declaration with the specified storage
1488 * class for the parameter and the function.
1491 * sd = the `struct` that contains the copy constructor
1492 * paramStc = the storage class of the copy constructor parameter
1493 * funcStc = the storage class for the copy constructor declaration
1496 * The copy constructor declaration for struct `sd`.
1498 private CtorDeclaration
generateCopyCtorDeclaration(StructDeclaration sd
, const StorageClass paramStc
, const StorageClass funcStc
)
1500 auto fparams
= new Parameters();
1501 auto structType
= sd
.type
;
1502 fparams
.push(new Parameter(paramStc | STC
.ref_ | STC
.return_ | STC
.scope_
, structType
, Id
.p
, null, null));
1503 ParameterList pList
= ParameterList(fparams
);
1504 auto tf
= new TypeFunction(pList
, structType
, LINK
.d
, STC
.ref_
);
1505 auto ccd
= new CtorDeclaration(sd
.loc
, Loc
.initial
, STC
.ref_
, tf
, true);
1506 ccd
.storage_class |
= funcStc
;
1507 ccd
.storage_class |
= STC
.inference
;
1508 ccd
.generated
= true;
1513 * Generates a trivial copy constructor body that simply does memberwise
1516 * this.field1 = rhs.field1;
1517 * this.field2 = rhs.field2;
1521 * sd = the `struct` declaration that contains the copy constructor
1524 * A `CompoundStatement` containing the body of the copy constructor.
1526 private Statement
generateCopyCtorBody(StructDeclaration sd
)
1530 foreach (v
; sd
.fields
)
1532 auto ec
= new AssignExp(loc
,
1533 new DotVarExp(loc
, new ThisExp(loc
), v
),
1534 new DotVarExp(loc
, new IdentifierExp(loc
, Id
.p
), v
));
1535 e
= Expression
.combine(e
, ec
);
1536 //printf("e.toChars = %s\n", e.toChars());
1538 Statement s1
= new ExpStatement(loc
, e
);
1539 return new CompoundStatement(loc
, s1
);
1543 * Determine if a copy constructor is needed for struct sd,
1544 * if the following conditions are met:
1546 * 1. sd does not define a copy constructor
1547 * 2. at least one field of sd defines a copy constructor
1550 * sd = the `struct` for which the copy constructor is generated
1551 * hasCpCtor = set to true if a copy constructor is already present
1554 * `true` if one needs to be generated
1557 private bool needCopyCtor(StructDeclaration sd
, out bool hasCpCtor
)
1562 auto ctor
= sd
.search(sd
.loc
, Id
.ctor
);
1565 if (ctor
.isOverloadSet())
1567 if (auto td
= ctor
.isTemplateDeclaration())
1571 CtorDeclaration cpCtor
;
1572 CtorDeclaration rvalueCtor
;
1577 overloadApply(ctor
, (Dsymbol s
)
1579 if (s
.isTemplateDeclaration())
1581 auto ctorDecl
= s
.isCtorDeclaration();
1583 if (ctorDecl
.isCpCtor
)
1590 auto tf
= ctorDecl
.type
.toTypeFunction();
1591 const dim
= tf
.parameterList
.length
;
1594 auto param
= tf
.parameterList
[0];
1595 if (param
.type
.mutableOf().unSharedOf() == sd
.type
.mutableOf().unSharedOf())
1597 rvalueCtor
= ctorDecl
;
1607 .error(sd
.loc
, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd
.toChars());
1608 errorSupplemental(rvalueCtor
.loc
,"rvalue constructor defined here");
1609 errorSupplemental(cpCtor
.loc
, "copy constructor defined here");
1616 VarDeclaration fieldWithCpCtor
;
1617 // see if any struct members define a copy constructor
1618 foreach (v
; sd
.fields
)
1620 if (v
.storage_class
& STC
.ref_
)
1625 auto ts
= v
.type
.baseElemOf().isTypeStruct();
1628 if (ts
.sym
.hasCopyCtor
)
1630 fieldWithCpCtor
= v
;
1635 if (fieldWithCpCtor
&& rvalueCtor
)
1637 .error(sd
.loc
, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd
.toChars());
1638 errorSupplemental(rvalueCtor
.loc
,"rvalue constructor defined here");
1639 errorSupplemental(fieldWithCpCtor
.loc
, "field with copy constructor defined here");
1642 else if (!fieldWithCpCtor
)
1648 * Generates a copy constructor if needCopyCtor() returns true.
1649 * The generated copy constructor will be of the form:
1650 * this(ref return scope inout(S) rhs) inout
1652 * this.field1 = rhs.field1;
1653 * this.field2 = rhs.field2;
1658 * sd = the `struct` for which the copy constructor is generated
1659 * sc = the scope where the copy constructor is generated
1662 * `true` if `struct` sd defines a copy constructor (explicitly or generated),
1663 * `false` otherwise.
1665 bool buildCopyCtor(StructDeclaration sd
, Scope
* sc
)
1668 if (!needCopyCtor(sd
, hasCpCtor
))
1671 //printf("generating copy constructor for %s\n", sd.toChars());
1672 const MOD paramMod
= MODFlags
.wild
;
1673 const MOD funcMod
= MODFlags
.wild
;
1674 auto ccd
= generateCopyCtorDeclaration(sd
, ModToStc(paramMod
), ModToStc(funcMod
));
1675 auto copyCtorBody
= generateCopyCtorBody(sd
);
1676 ccd
.fbody
= copyCtorBody
;
1677 sd
.members
.push(ccd
);
1678 ccd
.addMember(sc
, sd
);
1679 const errors
= global
.startGagging();
1680 Scope
* sc2
= sc
.push();
1682 sc2
.linkage
= LINK
.d
;
1683 ccd
.dsymbolSemantic(sc2
);
1686 //printf("ccd semantic: %s\n", ccd.type.toChars());
1688 if (global
.endGagging(errors
) || sd
.isUnionDeclaration())
1690 ccd
.storage_class |
= STC
.disable
;