During CTAD, we select the best viable deduction guide via build_new_function_call, which performs overload resolution on the set of candidate guides and then forms a call to the guide. As the PR points out, this latter step is unnecessary and occasionally gives us the wrong answer since a call to the selected guide may be ill-formed, or forming the call may have side effects such as prematurely deducing the type of a {}.
This patch introduces a specialized subroutine modeled off of build_new_function_call that stops short of building a call to the selected function, and makes do_class_deduction use this subroutine instead. And since we no longer build a call, do_class_deduction doesn't need to set tf_decltype or cp_unevaluated_operand. This change causes us to reject some container CTAD examples in the libstdc++ testsuite due to deduction failure for {}, which AFAICT is the correct behavior. Previously, in the case of e.g. the first removed example for std::map, the type of {} would be deduced to less<int> as a side effect of forming the call to the selected guide template<typename _Key, typename _Tp, typename _Compare = less<_Key>, typename _Allocator = allocator<pair<const _Key, _Tp>>, typename = _RequireNotAllocator<_Compare>, typename = _RequireAllocator<_Allocator>> map(initializer_list<pair<_Key, _Tp>>, _Compare = _Compare(), _Allocator = _Allocator()) -> map<_Key, _Tp, _Compare, _Allocator>; which made later overload resolution for the constructor call unambiguous. Now, the type of {} remains undeduced until constructor overload resolution, and we complain about ambiguity with the two constructors map(initializer_list<value_type> __l, const _Compare& __comp = _Compare(), const allocator_type& __a = allocator_type()) map(initializer_list<value_type> __l, const allocator_type& __a) This patch just removes these problematic container CTAD examples. Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk? PR c++/86439 gcc/cp/ChangeLog: * call.c (print_error_for_call_failure): Constify 'args' parameter. (perform_dguide_overload_resolution): Define. * cp-tree.h: (perform_dguide_overload_resolution): Declare. * pt.c (do_class_deduction): Use perform_dguide_overload_resolution instead of build_new_function_call. Don't use tf_decltype or set cp_unevaluated_operand. Remove unnecessary NULL_TREE tests. libstdc++-v3/ChangeLog: * testsuite/23_containers/map/cons/deduction.cc: Remove ambiguous CTAD constructs. * testsuite/23_containers/multimap/cons/deduction.cc: Likewise. * testsuite/23_containers/multiset/cons/deduction.cc: Likewise. * testsuite/23_containers/set/cons/deduction.cc: Likewise. * testsuite/23_containers/unordered_map/cons/deduction.cc: Likewise. * testsuite/23_containers/unordered_multimap/cons/deduction.cc: Likewise. * testsuite/23_containers/unordered_multiset/cons/deduction.cc: Likewise. * testsuite/23_containers/unordered_set/cons/deduction.cc: Likewise. gcc/testsuite/ChangeLog: * g++.dg/cpp1z/class-deduction88.C: New test. * g++.dg/cpp1z/class-deduction89.C: New test. * g++.dg/cpp1z/class-deduction90.C: New test. --- gcc/cp/call.c | 36 +++++++++++++++- gcc/cp/cp-tree.h | 2 + gcc/cp/pt.c | 41 +++++++------------ .../g++.dg/cpp1z/class-deduction88.C | 20 +++++++++ .../g++.dg/cpp1z/class-deduction89.C | 15 +++++++ .../g++.dg/cpp1z/class-deduction90.C | 16 ++++++++ .../23_containers/map/cons/deduction.cc | 19 --------- .../23_containers/multimap/cons/deduction.cc | 20 --------- .../23_containers/multiset/cons/deduction.cc | 14 ------- .../23_containers/set/cons/deduction.cc | 15 ------- .../unordered_map/cons/deduction.cc | 16 -------- .../unordered_multimap/cons/deduction.cc | 16 -------- .../unordered_multiset/cons/deduction.cc | 10 ----- .../unordered_set/cons/deduction.cc | 10 ----- 14 files changed, 102 insertions(+), 148 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction88.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction89.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction90.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 9f03534c20c..aafc7acca24 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4629,7 +4629,7 @@ perform_overload_resolution (tree fn, functions. */ static void -print_error_for_call_failure (tree fn, vec<tree, va_gc> *args, +print_error_for_call_failure (tree fn, const vec<tree, va_gc> *args, struct z_candidate *candidates) { tree targs = NULL_TREE; @@ -4654,6 +4654,40 @@ print_error_for_call_failure (tree fn, vec<tree, va_gc> *args, print_z_candidates (loc, candidates); } +/* Perform overload resolution on the set of deduction guides DGUIDES + using ARGS. Returns the selected deduction guide, or error_mark_node + if overload resolution fails. */ + +tree +perform_dguide_overload_resolution (tree dguides, const vec<tree, va_gc> *args, + tsubst_flags_t complain) +{ + z_candidate *candidates; + bool any_viable_p; + tree result; + + gcc_assert (deduction_guide_p (OVL_FIRST (dguides))); + + /* Get the high-water mark for the CONVERSION_OBSTACK. */ + void *p = conversion_obstack_alloc (0); + + z_candidate *cand = perform_overload_resolution (dguides, args, &candidates, + &any_viable_p, complain); + if (!cand) + { + if (complain & tf_error) + print_error_for_call_failure (dguides, args, candidates); + result = error_mark_node; + } + else + result = cand->fn; + + /* Free all the conversions we allocated. */ + obstack_free (&conversion_obstack, p); + + return result; +} + /* Return an expression for a call to FN (a namespace-scope function, or a static member function) with the ARGS. This may change ARGS. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 36f99ccf189..6f713719589 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6437,6 +6437,8 @@ extern void complain_about_bad_argument (location_t arg_loc, tree from_type, tree to_type, tree fndecl, int parmnum); extern void maybe_inform_about_fndecl_for_bogus_argument_init (tree, int); +extern tree perform_dguide_overload_resolution (tree, const vec<tree, va_gc> *, + tsubst_flags_t); /* A class for recording information about access failures (e.g. private diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 15947b2c812..732fb405adf 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -29382,7 +29382,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, if (tree guide = maybe_aggr_guide (tmpl, init, args)) cands = lookup_add (guide, cands); - tree call = error_mark_node; + tree fndecl = error_mark_node; /* If this is list-initialization and the class has a list constructor, first try deducing from the list as a single argument, as [over.match.list]. */ @@ -29396,11 +29396,9 @@ do_class_deduction (tree ptype, tree tmpl, tree init, } if (list_cands) { - ++cp_unevaluated_operand; - call = build_new_function_call (list_cands, &args, tf_decltype); - --cp_unevaluated_operand; + fndecl = perform_dguide_overload_resolution (list_cands, args, tf_none); - if (call == error_mark_node) + if (fndecl == error_mark_node) { /* That didn't work, now try treating the list as a sequence of arguments. */ @@ -29416,31 +29414,22 @@ do_class_deduction (tree ptype, tree tmpl, tree init, "user-declared constructors", type); return error_mark_node; } - else if (!cands && call == error_mark_node) + else if (!cands && fndecl == error_mark_node) { error ("cannot deduce template arguments of %qT, as it has no viable " "deduction guides", type); return error_mark_node; } - if (call == error_mark_node) - { - ++cp_unevaluated_operand; - call = build_new_function_call (cands, &args, tf_decltype); - --cp_unevaluated_operand; - } + if (fndecl == error_mark_node) + fndecl = perform_dguide_overload_resolution (cands, args, tf_none); - if (call == error_mark_node) + if (fndecl == error_mark_node) { if (complain & tf_warning_or_error) { error ("class template argument deduction failed:"); - - ++cp_unevaluated_operand; - call = build_new_function_call (cands, &args, - complain | tf_decltype); - --cp_unevaluated_operand; - + perform_dguide_overload_resolution (cands, args, complain); if (elided) inform (input_location, "explicit deduction guides not considered " "for copy-initialization"); @@ -29451,8 +29440,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, constructor is chosen, the initialization is ill-formed. */ else if (flags & LOOKUP_ONLYCONVERTING) { - tree fndecl = cp_get_callee_fndecl_nofold (call); - if (fndecl && DECL_NONCONVERTING_P (fndecl)) + if (DECL_NONCONVERTING_P (fndecl)) { if (complain & tf_warning_or_error) { @@ -29470,12 +29458,10 @@ do_class_deduction (tree ptype, tree tmpl, tree init, /* If CTAD succeeded but the type doesn't have any explicit deduction guides, this deduction might not be what the user intended. */ - if (call != error_mark_node && !any_dguides_p) + if (fndecl != error_mark_node && !any_dguides_p) { - tree fndecl = cp_get_callee_fndecl_nofold (call); - if (fndecl != NULL_TREE - && (!DECL_IN_SYSTEM_HEADER (fndecl) - || global_dc->dc_warn_system_headers) + if ((!DECL_IN_SYSTEM_HEADER (fndecl) + || global_dc->dc_warn_system_headers) && warning (OPT_Wctad_maybe_unsupported, "%qT may not intend to support class template argument " "deduction", type)) @@ -29483,7 +29469,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, "warning"); } - return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype)); + return cp_build_qualified_type (TREE_TYPE (TREE_TYPE (fndecl)), + cp_type_quals (ptype)); } /* Replace occurrences of 'auto' in TYPE with the appropriate type deduced diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction88.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction88.C new file mode 100644 index 00000000000..f8fea966696 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction88.C @@ -0,0 +1,20 @@ +// PR c++/86439 +// { dg-do compile { target c++17 } } + +struct NC { + NC() = default; + NC(NC const&) = delete; + NC& operator=(NC const&) = delete; +}; + +template <int> +struct C { + C(NC const&); +}; + +C(NC) -> C<0>; + +int main() { + NC nc; + C c(nc); +} diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction89.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction89.C new file mode 100644 index 00000000000..dd898573022 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction89.C @@ -0,0 +1,15 @@ +// PR c++/86439 +// { dg-do compile { target c++17 } } + +struct B { }; +struct C { }; + +template<class T> +struct A { + A(T, B); +}; + +template<class T> +A(T, C) -> A<T>; + +A a(0, {}); diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction90.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction90.C new file mode 100644 index 00000000000..8b93193c7b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction90.C @@ -0,0 +1,16 @@ +// PR c++/86439 +// { dg-do compile { target c++17 } } + +struct less { }; +struct allocator { }; + +template<class T, class U = less, class V = allocator> +struct A { + A(T, U); + A(T, V); +}; + +template<class T, class U = less> +A(T, U) -> A<T>; + +A a(0, {}); // { dg-error "ambiguous" } diff --git a/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc index e9628c4ac32..8def11ed574 100644 --- a/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc @@ -39,10 +39,6 @@ static_assert(std::is_same_v< std::map<int, double>>); */ -static_assert(std::is_same_v< - decltype(std::map{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, {}}), - std::map<int, double>>); - /* This is not deducible, ambiguous candidates: * map(initializer_list<value_type>, const Compare&, const _Allocator& = {}) * map(initializer_list<value_type>, const _Allocator&) @@ -90,11 +86,6 @@ void f() std::less<int>{}, {}}), std::map<int, double>>); - static_assert(std::is_same_v< - decltype(std::map(x.begin(), x.end(), - {})), - std::map<int, double>>); - static_assert(std::is_same_v< decltype(std::map{x.begin(), x.end(), std::allocator<value_type>{}}), @@ -143,11 +134,6 @@ void g() std::less<int>{}, {}}), std::map<int, double>>); - static_assert(std::is_same_v< - decltype(std::map(x.begin(), x.end(), - {})), - std::map<int, double>>); - static_assert(std::is_same_v< decltype(std::map{x.begin(), x.end(), std::allocator<value_type>{}}), @@ -193,11 +179,6 @@ void h() std::less<int>{}, {}}), std::map<int, double>>); - static_assert(std::is_same_v< - decltype(std::map(x.begin(), x.end(), - {})), - std::map<int, double>>); - static_assert(std::is_same_v< decltype(std::map{x.begin(), x.end(), std::allocator<value_type>{}}), diff --git a/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc index 791cc963479..ff855081ab3 100644 --- a/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc @@ -40,11 +40,6 @@ static_assert(std::is_same_v< std::multimap<int, double>>); */ -static_assert(std::is_same_v< - decltype(std::multimap{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, - {}}), - std::multimap<int, double>>); - static_assert(std::is_same_v< decltype(std::multimap{{value_type{1, 2.0}, {2, 3.0}, {3, 4.0}}, {}, SimpleAllocator<value_type>{}}), @@ -75,11 +70,6 @@ void f() std::less<int>{}, {}}), std::multimap<int, double>>); - static_assert(std::is_same_v< - decltype(std::multimap(x.begin(), x.end(), - {})), - std::multimap<int, double>>); - static_assert(std::is_same_v< decltype(std::multimap{x.begin(), x.end(), {}, @@ -117,11 +107,6 @@ void g() std::less<int>{}, {}}), std::multimap<int, double>>); - static_assert(std::is_same_v< - decltype(std::multimap(x.begin(), x.end(), - {})), - std::multimap<int, double>>); - static_assert(std::is_same_v< decltype(std::multimap{x.begin(), x.end(), {}, @@ -156,11 +141,6 @@ void h() std::less<int>{}, {}}), std::multimap<int, double>>); - static_assert(std::is_same_v< - decltype(std::multimap(x.begin(), x.end(), - {})), - std::multimap<int, double>>); - static_assert(std::is_same_v< decltype(std::multimap{x.begin(), x.end(), {}, diff --git a/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc index ad12755ccc6..be7ca237e78 100644 --- a/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc @@ -19,10 +19,6 @@ static_assert(std::is_same_v< decltype(std::multiset{{1, 2, 3}, std::less<int>{}, {}}), std::multiset<int>>); -static_assert(std::is_same_v< - decltype(std::multiset{{1, 2, 3}, {}}), - std::multiset<int>>); - static_assert(std::is_same_v< decltype(std::multiset{{1, 2, 3}, SimpleAllocator<int>{}}), std::multiset<int, std::less<int>, SimpleAllocator<int>>>); @@ -50,11 +46,6 @@ void f() std::less<int>{}, {}}), std::multiset<int>>); - static_assert(std::is_same_v< - decltype(std::multiset(x.begin(), x.end(), - {})), - std::multiset<int>>); - static_assert(std::is_same_v< decltype(std::multiset{x.begin(), x.end(), std::allocator<int>{}}), @@ -101,11 +92,6 @@ void g() std::less<int>{}, {}}), std::multiset<int>>); - static_assert(std::is_same_v< - decltype(std::multiset(x.begin(), x.end(), - {})), - std::multiset<int>>); - static_assert(std::is_same_v< decltype(std::multiset{x.begin(), x.end(), std::allocator<value_type>{}}), diff --git a/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc index 89a2c43b937..48d1a3ebbe2 100644 --- a/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc @@ -20,11 +20,6 @@ static_assert(std::is_same_v< std::less<int>{}, {}}), std::set<int>>); -static_assert(std::is_same_v< - decltype(std::set{{1, 2, 3}, - {}}), - std::set<int>>); - static_assert(std::is_same_v< decltype(std::set{{1, 2, 3}, SimpleAllocator<int>{}}), @@ -56,11 +51,6 @@ void f() std::less<int>{}, {}}), std::set<int>>); - static_assert(std::is_same_v< - decltype(std::set(x.begin(), x.end(), - {})), - std::set<int>>); - static_assert(std::is_same_v< decltype(std::set{x.begin(), x.end(), {}, @@ -102,11 +92,6 @@ void g() std::less<int>{}, {}}), std::set<int>>); - static_assert(std::is_same_v< - decltype(std::set(x.begin(), x.end(), - {})), - std::set<int>>); - static_assert(std::is_same_v< decltype(std::set{x.begin(), x.end(), std::allocator<value_type>{}}), diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc index d8489b23f8a..bd266492b2a 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc @@ -21,12 +21,6 @@ static_assert(std::is_same_v< 1}), std::unordered_map<int, double>>); -static_assert(std::is_same_v< - decltype(std::unordered_map{{std::pair{1, 2.0}, - {2, 3.0}, {3, 4.0}}, - {}, std::hash<int>{}, {}}), - std::unordered_map<int, double>>); - static_assert(std::is_same_v< decltype(std::unordered_map{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, @@ -57,16 +51,6 @@ void f() std::allocator<std::pair<const int, double>>{}}), std::unordered_map<int, double>>); - static_assert(std::is_same_v< - decltype(std::unordered_map{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), - std::unordered_map<int, double>>); - - static_assert(std::is_same_v< - decltype(std::unordered_map(x.begin(), x.end(), - {})), - std::unordered_map<int, double>>); - static_assert(std::is_same_v< decltype(std::unordered_map{x.begin(), x.end(), 1}), std::unordered_map<int, double>>); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc index 13f54d43451..74a0165574d 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc @@ -15,12 +15,6 @@ static_assert(std::is_same_v< {2, 3.0}, {3, 4.0}}}), std::unordered_multimap<int, double>>); -static_assert(std::is_same_v< - decltype(std::unordered_multimap{{std::pair{1, 2.0}, - {2, 3.0}, {3, 4.0}}, - {}, std::hash<int>{}, {}}), - std::unordered_multimap<int, double>>); - static_assert(std::is_same_v< decltype(std::unordered_multimap{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, @@ -66,16 +60,6 @@ void f() std::allocator<std::pair<const int, double>>{}}), std::unordered_multimap<int, double>>); - static_assert(std::is_same_v< - decltype(std::unordered_multimap{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), - std::unordered_multimap<int, double>>); - - static_assert(std::is_same_v< - decltype(std::unordered_multimap(x.begin(), x.end(), - {})), - std::unordered_multimap<int, double>>); - static_assert(std::is_same_v< decltype(std::unordered_multimap(x.begin(), x.end(), 1)), std::unordered_multimap<int, double>>); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc index 1850237e44c..e3006fdbfe3 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc @@ -9,11 +9,6 @@ static_assert(std::is_same_v< decltype(std::unordered_multiset{1, 2, 3}), std::unordered_multiset<int>>); -static_assert(std::is_same_v< - decltype(std::unordered_multiset{{1, 2, 3}, - 0, std::hash<int>{}, {}}), - std::unordered_multiset<int>>); - static_assert(std::is_same_v< decltype(std::unordered_multiset{{1, 2, 3}, {}}), @@ -76,11 +71,6 @@ void f() std::allocator<int>{}}), std::unordered_multiset<int>>); - static_assert(std::is_same_v< - decltype(std::unordered_multiset{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), - std::unordered_multiset<int>>); - static_assert(std::is_same_v< decltype(std::unordered_multiset(x.begin(), x.end(), {})), diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc index a745dce0fba..69922cd92e7 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc @@ -9,11 +9,6 @@ static_assert(std::is_same_v< decltype(std::unordered_set{1, 2, 3}), std::unordered_set<int>>); -static_assert(std::is_same_v< - decltype(std::unordered_set{{1, 2, 3}, - 0, std::hash<int>{}, {}}), - std::unordered_set<int>>); - static_assert(std::is_same_v< decltype(std::unordered_set{{1, 2, 3}, {}}), @@ -71,11 +66,6 @@ void f() std::allocator<int>{}}), std::unordered_set<int>>); - static_assert(std::is_same_v< - decltype(std::unordered_set{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), - std::unordered_set<int>>); - static_assert(std::is_same_v< decltype(std::unordered_set(x.begin(), x.end(), {})), -- 2.32.0.93.g670b81a890