From 6e23b3e2a16d4751f4ef7adae12daa8059acb64f Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Wed, 31 Jul 2019 15:07:51 +0300 Subject: [PATCH 1/4] Fix Issue 19902 - hasElaborateCopyConstructor doesn't know about copy constructors --- src/core/internal/traits.d | 251 ++++++++++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 1 deletion(-) diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d index 80139415bd..09f5efbee2 100644 --- a/src/core/internal/traits.d +++ b/src/core/internal/traits.d @@ -258,7 +258,28 @@ template hasElaborateCopyConstructor(S) } else static if (is(S == struct)) { - enum hasElaborateCopyConstructor = __traits(hasMember, S, "__xpostblit"); + enum hasElaborateCopyConstructor = __traits(hasMember, S, "__xpostblit") || + () { + static if (__traits(hasMember, S, "__ctor")) + { + static foreach (f; __traits(getOverloads, S, "__ctor")) + {{ + enum isVoid(alias T) = is (T == void); + static if (Parameters!f.length == 1 || + (Parameters!f.length > 1 && !anySatisfy!(isVoid, ParameterDefaults!f[1 .. $]))) + { + bool r = is (typeof((Parameters!f x) { + static if (!(__traits(isRef, x[0]) && is (Parameters!f[0] : S))) + { + static assert(0); + } + })); + if (r) return true; + } + }} + } + return false; + }(); } else { @@ -266,6 +287,35 @@ template hasElaborateCopyConstructor(S) } } +@safe unittest +{ + static struct S + { + int x; + this(return scope ref typeof(this) rhs) { } + this(int x, int y) {} + } + + static assert(hasElaborateCopyConstructor!S); + + static struct S2 + { + int x; + this(int x, int y) {} + } + + static assert(!hasElaborateCopyConstructor!S2); + + static struct S3 + { + int x; + this(return scope ref typeof(this) rhs, int x = 42) { } + this(int x, int y) {} + } + + static assert(hasElaborateCopyConstructor!S3); +} + template hasElaborateAssign(S) { static if (__traits(isStaticArray, S) && S.length) @@ -284,6 +334,205 @@ template hasElaborateAssign(S) } } +// std.traits.isFunctionPointer +/** +Detect whether symbol or type `T` is a function pointer. + */ +template isFunctionPointer(T...) +if (T.length == 1) +{ + static if (is(T[0] U) || is(typeof(T[0]) U)) + { + static if (is(U F : F*) && is(F == function)) + enum bool isFunctionPointer = true; + else + enum bool isFunctionPointer = false; + } + else + enum bool isFunctionPointer = false; +} + +/// +@safe unittest +{ + static void foo() {} + void bar() {} + + auto fpfoo = &foo; + static assert( isFunctionPointer!fpfoo); + static assert( isFunctionPointer!(void function())); + + auto dgbar = &bar; + static assert(!isFunctionPointer!dgbar); + static assert(!isFunctionPointer!(void delegate())); + static assert(!isFunctionPointer!foo); + static assert(!isFunctionPointer!bar); + + static assert( isFunctionPointer!((int a) {})); +} + +// std.traits.isDelegate +/** +Detect whether symbol or type `T` is a delegate. +*/ +template isDelegate(T...) +if (T.length == 1) +{ + static if (is(typeof(& T[0]) U : U*) && is(typeof(& T[0]) U == delegate)) + { + // T is a (nested) function symbol. + enum bool isDelegate = true; + } + else static if (is(T[0] W) || is(typeof(T[0]) W)) + { + // T is an expression or a type. Take the type of it and examine. + enum bool isDelegate = is(W == delegate); + } + else + enum bool isDelegate = false; +} + +/// +@safe unittest +{ + static void sfunc() { } + int x; + void func() { x++; } + + int delegate() dg; + assert(isDelegate!dg); + assert(isDelegate!(int delegate())); + assert(isDelegate!(typeof(&func))); + + int function() fp; + assert(!isDelegate!fp); + assert(!isDelegate!(int function())); + assert(!isDelegate!(typeof(&sfunc))); +} + +// std.traits.ParameterIdentifierTuple +/** +Get, as a tuple, the identifiers of the parameters to a function symbol. + */ +template ParameterIdentifierTuple(func...) +if (func.length == 1/* && isCallable!func*/) +{ + static if (is(FunctionTypeOf!func PT == __parameters)) + { + template Get(size_t i) + { + static if (!isFunctionPointer!func && !isDelegate!func + // Unnamed parameters yield CT error. + && is(typeof(__traits(identifier, PT[i .. i+1]))) + // Filter out unnamed args, which look like (Type) instead of (Type name). + && PT[i].stringof != PT[i .. i+1].stringof[1..$-1]) + { + enum Get = __traits(identifier, PT[i .. i+1]); + } + else + { + enum Get = ""; + } + } + } + else + { + static assert(0, func[0].stringof ~ "is not a function"); + + // Define dummy entities to avoid pointless errors + template Get(size_t i) { enum Get = ""; } + alias PT = AliasSeq!(); + } + + template Impl(size_t i = 0) + { + static if (i == PT.length) + alias Impl = AliasSeq!(); + else + alias Impl = AliasSeq!(Get!i, Impl!(i+1)); + } + + alias ParameterIdentifierTuple = Impl!(); +} + +/// +@safe unittest +{ + int foo(int num, string name, int); + static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]); +} + +// std.traits.ParameterDefaults +/** +Get, as a tuple, the default value of the parameters to a function symbol. +If a parameter doesn't have the default value, `void` is returned instead. + */ +template ParameterDefaults(func...) +if (func.length == 1/* && isCallable!func*/) +{ + alias param_names = ParameterIdentifierTuple!func; + static if (is(FunctionTypeOf!(func[0]) PT == __parameters)) + { + template Get(size_t i) + { + // `PT[i .. i+1]` declares a parameter with an arbitrary name. + // To avoid a name clash, generate local names that are distinct + // from the parameter name, and mix them in. + enum name = param_names[i]; + enum args = "args" ~ (name == "args" ? "_" : ""); + enum val = "val" ~ (name == "val" ? "_" : ""); + enum ptr = "ptr" ~ (name == "ptr" ? "_" : ""); + mixin(" + // workaround scope escape check, see + // https://issues.dlang.org/show_bug.cgi?id=16582 + // should use return scope once available + enum get = (PT[i .. i+1] " ~ args ~ ") @trusted + { + // If the parameter is lazy, we force it to be evaluated + // like this. + auto " ~ val ~ " = " ~ args ~ "[0]; + auto " ~ ptr ~ " = &" ~ val ~ "; + // workaround Bugzilla 16582 + return *" ~ ptr ~ "; + }; + "); + static if (is(typeof(get()))) + enum Get = get(); + else + alias Get = void; + // If default arg doesn't exist, returns void instead. + } + } + else + { + static assert(0, func[0].stringof ~ "is not a function"); + + // Define dummy entities to avoid pointless errors + template Get(size_t i) { enum Get = ""; } + alias PT = AliasSeq!(); + } + + template Impl(size_t i = 0) + { + static if (i == PT.length) + alias Impl = AliasSeq!(); + else + alias Impl = AliasSeq!(Get!i, Impl!(i+1)); + } + + alias ParameterDefaults = Impl!(); +} + +/// +@safe unittest +{ + int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0); + static assert(is(ParameterDefaults!foo[0] == void)); + static assert( ParameterDefaults!foo[1] == "hello"); + static assert( ParameterDefaults!foo[2] == [1,2,3]); + static assert( ParameterDefaults!foo[3] == 0); +} + // std.meta.Filter template Filter(alias pred, TList...) { From 55bcfb8e8561dcba639c954a672ee9349bfd3065 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 5 Aug 2019 15:06:35 +0300 Subject: [PATCH 2/4] Refactor --- src/core/internal/traits.d | 222 ++----------------------------------- 1 file changed, 11 insertions(+), 211 deletions(-) diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d index 09f5efbee2..38d6aab425 100644 --- a/src/core/internal/traits.d +++ b/src/core/internal/traits.d @@ -264,18 +264,17 @@ template hasElaborateCopyConstructor(S) { static foreach (f; __traits(getOverloads, S, "__ctor")) {{ - enum isVoid(alias T) = is (T == void); - static if (Parameters!f.length == 1 || - (Parameters!f.length > 1 && !anySatisfy!(isVoid, ParameterDefaults!f[1 .. $]))) - { - bool r = is (typeof((Parameters!f x) { - static if (!(__traits(isRef, x[0]) && is (Parameters!f[0] : S))) - { - static assert(0); - } - })); - if (r) return true; - } + bool r = __traits(compiles, { + auto p = &f; + + // Check if ctor is callable with lval or rval + S s; + (*p)(s); + + // Check that ctor is't callable with rval + static assert (!__traits(compiles, (*p)(S()) )); + }); + if (r) return true; }} } return false; @@ -334,205 +333,6 @@ template hasElaborateAssign(S) } } -// std.traits.isFunctionPointer -/** -Detect whether symbol or type `T` is a function pointer. - */ -template isFunctionPointer(T...) -if (T.length == 1) -{ - static if (is(T[0] U) || is(typeof(T[0]) U)) - { - static if (is(U F : F*) && is(F == function)) - enum bool isFunctionPointer = true; - else - enum bool isFunctionPointer = false; - } - else - enum bool isFunctionPointer = false; -} - -/// -@safe unittest -{ - static void foo() {} - void bar() {} - - auto fpfoo = &foo; - static assert( isFunctionPointer!fpfoo); - static assert( isFunctionPointer!(void function())); - - auto dgbar = &bar; - static assert(!isFunctionPointer!dgbar); - static assert(!isFunctionPointer!(void delegate())); - static assert(!isFunctionPointer!foo); - static assert(!isFunctionPointer!bar); - - static assert( isFunctionPointer!((int a) {})); -} - -// std.traits.isDelegate -/** -Detect whether symbol or type `T` is a delegate. -*/ -template isDelegate(T...) -if (T.length == 1) -{ - static if (is(typeof(& T[0]) U : U*) && is(typeof(& T[0]) U == delegate)) - { - // T is a (nested) function symbol. - enum bool isDelegate = true; - } - else static if (is(T[0] W) || is(typeof(T[0]) W)) - { - // T is an expression or a type. Take the type of it and examine. - enum bool isDelegate = is(W == delegate); - } - else - enum bool isDelegate = false; -} - -/// -@safe unittest -{ - static void sfunc() { } - int x; - void func() { x++; } - - int delegate() dg; - assert(isDelegate!dg); - assert(isDelegate!(int delegate())); - assert(isDelegate!(typeof(&func))); - - int function() fp; - assert(!isDelegate!fp); - assert(!isDelegate!(int function())); - assert(!isDelegate!(typeof(&sfunc))); -} - -// std.traits.ParameterIdentifierTuple -/** -Get, as a tuple, the identifiers of the parameters to a function symbol. - */ -template ParameterIdentifierTuple(func...) -if (func.length == 1/* && isCallable!func*/) -{ - static if (is(FunctionTypeOf!func PT == __parameters)) - { - template Get(size_t i) - { - static if (!isFunctionPointer!func && !isDelegate!func - // Unnamed parameters yield CT error. - && is(typeof(__traits(identifier, PT[i .. i+1]))) - // Filter out unnamed args, which look like (Type) instead of (Type name). - && PT[i].stringof != PT[i .. i+1].stringof[1..$-1]) - { - enum Get = __traits(identifier, PT[i .. i+1]); - } - else - { - enum Get = ""; - } - } - } - else - { - static assert(0, func[0].stringof ~ "is not a function"); - - // Define dummy entities to avoid pointless errors - template Get(size_t i) { enum Get = ""; } - alias PT = AliasSeq!(); - } - - template Impl(size_t i = 0) - { - static if (i == PT.length) - alias Impl = AliasSeq!(); - else - alias Impl = AliasSeq!(Get!i, Impl!(i+1)); - } - - alias ParameterIdentifierTuple = Impl!(); -} - -/// -@safe unittest -{ - int foo(int num, string name, int); - static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]); -} - -// std.traits.ParameterDefaults -/** -Get, as a tuple, the default value of the parameters to a function symbol. -If a parameter doesn't have the default value, `void` is returned instead. - */ -template ParameterDefaults(func...) -if (func.length == 1/* && isCallable!func*/) -{ - alias param_names = ParameterIdentifierTuple!func; - static if (is(FunctionTypeOf!(func[0]) PT == __parameters)) - { - template Get(size_t i) - { - // `PT[i .. i+1]` declares a parameter with an arbitrary name. - // To avoid a name clash, generate local names that are distinct - // from the parameter name, and mix them in. - enum name = param_names[i]; - enum args = "args" ~ (name == "args" ? "_" : ""); - enum val = "val" ~ (name == "val" ? "_" : ""); - enum ptr = "ptr" ~ (name == "ptr" ? "_" : ""); - mixin(" - // workaround scope escape check, see - // https://issues.dlang.org/show_bug.cgi?id=16582 - // should use return scope once available - enum get = (PT[i .. i+1] " ~ args ~ ") @trusted - { - // If the parameter is lazy, we force it to be evaluated - // like this. - auto " ~ val ~ " = " ~ args ~ "[0]; - auto " ~ ptr ~ " = &" ~ val ~ "; - // workaround Bugzilla 16582 - return *" ~ ptr ~ "; - }; - "); - static if (is(typeof(get()))) - enum Get = get(); - else - alias Get = void; - // If default arg doesn't exist, returns void instead. - } - } - else - { - static assert(0, func[0].stringof ~ "is not a function"); - - // Define dummy entities to avoid pointless errors - template Get(size_t i) { enum Get = ""; } - alias PT = AliasSeq!(); - } - - template Impl(size_t i = 0) - { - static if (i == PT.length) - alias Impl = AliasSeq!(); - else - alias Impl = AliasSeq!(Get!i, Impl!(i+1)); - } - - alias ParameterDefaults = Impl!(); -} - -/// -@safe unittest -{ - int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0); - static assert(is(ParameterDefaults!foo[0] == void)); - static assert( ParameterDefaults!foo[1] == "hello"); - static assert( ParameterDefaults!foo[2] == [1,2,3]); - static assert( ParameterDefaults!foo[3] == 0); -} - // std.meta.Filter template Filter(alias pred, TList...) { From 57f066741efab8233069bb93b7e6118a58f3f2e1 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 5 Aug 2019 16:20:57 +0300 Subject: [PATCH 3/4] Fix static struct issue --- src/core/internal/traits.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d index 38d6aab425..0021721cd9 100644 --- a/src/core/internal/traits.d +++ b/src/core/internal/traits.d @@ -268,7 +268,7 @@ template hasElaborateCopyConstructor(S) auto p = &f; // Check if ctor is callable with lval or rval - S s; + S s = S.init; (*p)(s); // Check that ctor is't callable with rval @@ -288,7 +288,7 @@ template hasElaborateCopyConstructor(S) @safe unittest { - static struct S + struct S { int x; this(return scope ref typeof(this) rhs) { } @@ -297,7 +297,7 @@ template hasElaborateCopyConstructor(S) static assert(hasElaborateCopyConstructor!S); - static struct S2 + struct S2 { int x; this(int x, int y) {} @@ -305,7 +305,7 @@ template hasElaborateCopyConstructor(S) static assert(!hasElaborateCopyConstructor!S2); - static struct S3 + struct S3 { int x; this(return scope ref typeof(this) rhs, int x = 42) { } From 75d28ea31235c141e887071fa5141bef5201f899 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 5 Aug 2019 16:23:34 +0300 Subject: [PATCH 4/4] Disabled postblit means no elaborate copy constructor --- src/core/internal/traits.d | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d index 0021721cd9..60194a40c4 100644 --- a/src/core/internal/traits.d +++ b/src/core/internal/traits.d @@ -258,8 +258,13 @@ template hasElaborateCopyConstructor(S) } else static if (is(S == struct)) { - enum hasElaborateCopyConstructor = __traits(hasMember, S, "__xpostblit") || - () { + static if (__traits(hasMember, S, "__xpostblit")) + { + enum hasElaborateCopyConstructor = !__traits(isDisabled, S.__xpostblit); + } + else + { + enum hasElaborateCopyConstructor = () { static if (__traits(hasMember, S, "__ctor")) { static foreach (f; __traits(getOverloads, S, "__ctor")) @@ -278,7 +283,8 @@ template hasElaborateCopyConstructor(S) }} } return false; - }(); + }(); + } } else { @@ -313,6 +319,14 @@ template hasElaborateCopyConstructor(S) } static assert(hasElaborateCopyConstructor!S3); + + struct S4 + { + int x; + @disable this(this); + } + + static assert(!hasElaborateCopyConstructor!S4); } template hasElaborateAssign(S)