diff --git a/src/dmd/dstruct.d b/src/dmd/dstruct.d index 688e1486244f..0be791207b8d 100644 --- a/src/dmd/dstruct.d +++ b/src/dmd/dstruct.d @@ -591,6 +591,23 @@ extern (C++) class StructDeclaration : AggregateDeclaration return (ispod == StructPOD.yes); } + /*************************************** + * Determine if `struct` has an elaborate copy constructor. + * + * A `struct` has an elaborate copy constructor if: + * $(OL + * $(LI has a copy constructor) + * $(LI or has postblit) + * ) + * + * Returns: + * `true` if struct has an elaborate copy constructor + */ + final bool hasElaborateCopyCtor() + { + return postblit || hasCopyCtor; + } + override final inout(StructDeclaration) isStructDeclaration() inout { return this; diff --git a/src/dmd/expression.d b/src/dmd/expression.d index edd428b7590c..f25a29ac4bb2 100644 --- a/src/dmd/expression.d +++ b/src/dmd/expression.d @@ -431,7 +431,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) if (auto ts = e.type.baseElemOf().isTypeStruct()) { StructDeclaration sd = ts.sym; - if (sd.postblit || sd.hasCopyCtor) + if (sd.hasElaborateCopyCtor()) { /* Create a variable tmp, and replace the argument e with: * (tmp = e),tmp diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index 682856e05903..064fa25d0b94 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -8409,7 +8409,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - if (sd.postblit || sd.hasCopyCtor) + if (sd.hasElaborateCopyCtor()) { /* We have a copy constructor for this */ diff --git a/src/dmd/id.d b/src/dmd/id.d index 8c66c4c15dfb..16395937d90b 100644 --- a/src/dmd/id.d +++ b/src/dmd/id.d @@ -431,6 +431,7 @@ immutable Msgtable[] msgtable = { "isZeroInit" }, { "getTargetInfo" }, { "getLocation" }, + { "hasElaborateCopyConstructor" }, // For C++ mangling { "allocator" }, diff --git a/src/dmd/traits.d b/src/dmd/traits.d index 1890422f4ba2..aa145a33dbd0 100644 --- a/src/dmd/traits.d +++ b/src/dmd/traits.d @@ -144,6 +144,7 @@ shared static this() "isZeroInit", "getTargetInfo", "getLocation", + "hasElaborateCopyConstructor" ]; traitsStringTable._init(names.length); @@ -617,6 +618,27 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } return True(); } + if (e.ident == Id.hasElaborateCopyConstructor) + { + if (dim != 1) + return dimError(1); + + auto o = (*e.args)[0]; + auto t = isType(o); + if (!t) + { + e.error("type expected as second argument of __traits `%s` instead of `%s`", + e.ident.toChars(), o.toChars()); + return new ErrorExp(); + } + + Type tb = t.baseElemOf(); + if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null) + { + return sd.hasElaborateCopyCtor() ? True() : False(); + } + return False(); + } if (e.ident == Id.isNested) { if (dim != 1) diff --git a/test/compilable/traits.d b/test/compilable/traits.d index 27c9595ccd17..0240cf44a0df 100644 --- a/test/compilable/traits.d +++ b/test/compilable/traits.d @@ -86,3 +86,60 @@ struct Outer static assert(__traits(getLocation, Outer.Nested)[1] == 82); static assert(__traits(getLocation, Outer.method)[1] == 84); +/******************************************/ +// https://issues.dlang.org/show_bug.cgi?id=19902 +// Define hasElaborateCopyConstructor trait + +struct S +{ + this (ref S rhs) {} +} + +struct OuterS +{ + struct S + { + this (ref S rhs) {} + } + + S s; +} + +void foo(T)() +{ + struct S(U) + { + this (ref S rhs) {} + } + static assert (__traits(hasElaborateCopyConstructor, S!int)); +} + +struct U(T) +{ + this (ref U rhs) {} +} + +struct SPostblit +{ + this(this) {} +} + +struct NoCpCtor { } +class C19902 { } + +static assert(__traits(hasElaborateCopyConstructor, S)); +static assert(__traits(hasElaborateCopyConstructor, OuterS.S)); +static assert(__traits(hasElaborateCopyConstructor, OuterS)); +static assert(__traits(compiles, foo!int)); +static assert(__traits(compiles, foo!S)); +static assert(__traits(hasElaborateCopyConstructor, U!int)); +static assert(__traits(hasElaborateCopyConstructor, U!S)); +static assert(__traits(hasElaborateCopyConstructor, SPostblit)); + +static assert(!__traits(hasElaborateCopyConstructor, NoCpCtor)); +static assert(!__traits(hasElaborateCopyConstructor, C19902)); +static assert(!__traits(hasElaborateCopyConstructor, int)); + +// Check that invalid use cases don't compile +static assert(!__traits(compiles, __traits(hasElaborateCopyConstructor))); +static assert(!__traits(compiles, __traits(hasElaborateCopyConstructor, S())));