From 62c25d7adb1a5664982449dda0e7f9ca63cf4735 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Thu, 9 Apr 2020 16:31:59 -0400 Subject: [PATCH] c++: make __is_constructible work with paren-init of aggrs [PR94149] In C++20 this is well-formed: using T = int[2]; T t(1, 2); which means that std::is_constructible_v should be true. But constructible_expr immediately returned the error_mark_node when it saw a list with more than one element. To give accurate results in C++20, we have to try initializing the aggregate from a parenthesized list of values. To not repeat the same mistake as in c++/93790, if there's only one element, I'm trying {} only when () didn't succeed. is_constructible5.C verifies this. In paren-init24.C std::is_nothrow_constructible_v doesn't work due to error: invalid 'static_cast' from type 'int' to type 'int [1]' and error: functional cast to array type 'int [2]' This needs to be fixed in libstdc++. PR c++/94149 * method.c (constructible_expr): In C++20, try using parenthesized initialization of aggregates to determine the result of __is_constructible. * g++.dg/cpp2a/paren-init24.C: New test. * g++.dg/cpp2a/paren-init25.C: New test. * g++.dg/ext/is_constructible5.C: New test. --- gcc/cp/ChangeLog | 7 ++++ gcc/cp/method.c | 42 ++++++++++++++++++-- gcc/testsuite/ChangeLog | 7 ++++ gcc/testsuite/g++.dg/cpp2a/paren-init24.C | 26 ++++++++++++ gcc/testsuite/g++.dg/cpp2a/paren-init25.C | 25 ++++++++++++ gcc/testsuite/g++.dg/ext/is_constructible5.C | 16 ++++++++ 6 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/paren-init24.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/paren-init25.C create mode 100644 gcc/testsuite/g++.dg/ext/is_constructible5.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 38f86cd3e87..b619593aa23 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2020-04-10 Marek Polacek + + PR c++/94149 + * method.c (constructible_expr): In C++20, try using parenthesized + initialization of aggregates to determine the result of + __is_constructible. + 2020-04-10 Bin Cheng * coroutines.cc (co_await_expander): Simplify. diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 9a21bfc1f66..2fb0de288a2 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1799,12 +1799,48 @@ constructible_expr (tree to, tree from) { if (from == NULL_TREE) return build_value_init (strip_array_types (to), tf_none); - else if (TREE_CHAIN (from)) - return error_mark_node; // too many initializers - from = build_stub_object (TREE_VALUE (from)); + const int len = list_length (from); + if (len > 1) + { + if (cxx_dialect < cxx2a) + /* Too many initializers. */ + return error_mark_node; + + /* In C++20 this is well-formed: + using T = int[2]; + T t(1, 2); + which means that std::is_constructible_v + should be true. */ + vec *v; + vec_alloc (v, len); + for (tree t = from; t; t = TREE_CHAIN (t)) + { + tree stub = build_stub_object (TREE_VALUE (t)); + constructor_elt elt = { NULL_TREE, stub }; + v->quick_push (elt); + } + from = build_constructor (init_list_type_node, v); + CONSTRUCTOR_IS_DIRECT_INIT (from) = true; + CONSTRUCTOR_IS_PAREN_INIT (from) = true; + } + else + from = build_stub_object (TREE_VALUE (from)); expr = perform_direct_initialization_if_possible (to, from, /*cast*/false, tf_none); + /* If t(e) didn't work, maybe t{e} will. */ + if (expr == NULL_TREE + && len == 1 + && cxx_dialect >= cxx2a) + { + from = build_constructor_single (init_list_type_node, NULL_TREE, + from); + CONSTRUCTOR_IS_DIRECT_INIT (from) = true; + CONSTRUCTOR_IS_PAREN_INIT (from) = true; + expr = perform_direct_initialization_if_possible (to, from, + /*cast*/false, + tf_none); + } } return expr; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 407bfef453f..2a3b1c2325d 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2020-04-10 Marek Polacek + + PR c++/94149 + * g++.dg/cpp2a/paren-init24.C: New test. + * g++.dg/cpp2a/paren-init25.C: New test. + * g++.dg/ext/is_constructible5.C: New test. + 2020-04-10 Fritz Reese * gfortran.dg/asynchronous_5.f03: Fix typo in testcase and add diff --git a/gcc/testsuite/g++.dg/cpp2a/paren-init24.C b/gcc/testsuite/g++.dg/cpp2a/paren-init24.C new file mode 100644 index 00000000000..a636a28ee6d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/paren-init24.C @@ -0,0 +1,26 @@ +// PR c++/94149 - make __is_constructible work with paren-init of aggrs. +// { dg-do compile { target c++2a } } + +#include + +int main() +{ + using T = int[1]; + T t(1); + + static_assert(__is_constructible(T, int)); + static_assert(!__is_constructible(T, int, int)); + static_assert(std::is_constructible_v); + //FIXME: libstdc++ problem? + //static_assert(std::is_nothrow_constructible_v); + + using T2 = int[2]; + T2 t2(1); + T2 t3(1, 2); + + static_assert(__is_constructible(T2, int)); + static_assert(__is_constructible(T2, int, int)); + static_assert(std::is_constructible_v); + // FIXME libstdc++ problem? + //static_assert(std::is_nothrow_constructible_v); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/paren-init25.C b/gcc/testsuite/g++.dg/cpp2a/paren-init25.C new file mode 100644 index 00000000000..53855a9ef9e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/paren-init25.C @@ -0,0 +1,25 @@ +// PR c++/94149 - make __is_constructible work with paren-init of aggrs. +// { dg-do compile { target c++2a } } + +struct nonaggr { + nonaggr() {} + int i; + int j; +}; + +struct aggr { + int i; + int j; +}; + +static_assert(__is_constructible(aggr, int, int)); +static_assert(__is_constructible(aggr, int)); +static_assert(!__is_constructible(nonaggr, int, int)); + +using T = aggr[2]; +static_assert(__is_constructible(T, aggr)); +static_assert(__is_constructible(T, aggr, aggr)); + +using N = nonaggr[2]; +static_assert(__is_constructible(N, nonaggr)); +static_assert(__is_constructible(N, nonaggr, nonaggr)); diff --git a/gcc/testsuite/g++.dg/ext/is_constructible5.C b/gcc/testsuite/g++.dg/ext/is_constructible5.C new file mode 100644 index 00000000000..93062aba8cd --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/is_constructible5.C @@ -0,0 +1,16 @@ +// PR c++/94149 - make __is_constructible work with paren-init of aggrs. +// { dg-do compile { target c++11 } } + +struct S { }; + +struct W { + S& r; + W(S& r_) : r(r_) {} + operator S&() { return r; } +}; + +S s; +W w(s); +S& s2(w); + +static_assert(__is_constructible(S&, W), ""); -- 2.30.2