Message ID | 20210622184514.1337889-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: CTAD and deduction guide selection [PR86439] | expand |
On 6/22/21 2:45 PM, Patrick Palka wrote: > 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? OK. > 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(), > {})), >
On Tue, 22 Jun 2021 at 19:45, Patrick Palka wrote: > 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. Do all the problematic cases have a corresponding case that doesn't use {} but uses an actual type? If not, we might want to add such cases, to ensure we're still covering all the cases that really *should* work.
On Tue, 22 Jun 2021, Jonathan Wakely wrote: > On Tue, 22 Jun 2021 at 19:45, Patrick Palka wrote: > > 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. > > Do all the problematic cases have a corresponding case that doesn't > use {} but uses an actual type? > > If not, we might want to add such cases, to ensure we're still > covering all the cases that really *should* work. Ah, it looks like most tests have one such corresponding case, but since the {} can potentially be an allocator or a function, for maximum coverage we should have two corresponding cases. So the revised patch below instead replaces each problematic CTAD example with the other untested case. Interestingly two of these new CTAD examples for std::set and std::multiset end up triggering an unrelated CTAD bug PR101174 on trunk, so the patch comments out these adjusted examples for now. -- >8 -- PR c++/86439 PR c++/101174 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: Replace ambiguous CTAD examples. * testsuite/23_containers/multimap/cons/deduction.cc: Likewise. * testsuite/23_containers/multiset/cons/deduction.cc: Likewise. Mention one of the replaced examples is broken due to PR101174. * testsuite/23_containers/set/cons/deduction.cc: Likewise. * testsuite/23_containers/unordered_map/cons/deduction.cc: Replace ambiguous CTAD examples. * 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 | 18 ++++++++ .../g++.dg/cpp1z/class-deduction89.C | 15 +++++++ .../g++.dg/cpp1z/class-deduction90.C | 16 ++++++++ .../23_containers/map/cons/deduction.cc | 8 ++-- .../23_containers/multimap/cons/deduction.cc | 8 ++-- .../23_containers/multiset/cons/deduction.cc | 8 ++-- .../23_containers/set/cons/deduction.cc | 8 ++-- .../unordered_map/cons/deduction.cc | 17 ++++++-- .../unordered_multimap/cons/deduction.cc | 17 ++++++-- .../unordered_multiset/cons/deduction.cc | 14 ++++++- .../unordered_set/cons/deduction.cc | 14 ++++++- 14 files changed, 170 insertions(+), 52 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..be38fed2901 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction88.C @@ -0,0 +1,18 @@ +// 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>; + +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..e72033c38a6 100644 --- a/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc @@ -40,7 +40,7 @@ static_assert(std::is_same_v< */ static_assert(std::is_same_v< - decltype(std::map{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, {}}), + decltype(std::map{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, std::less<int>{}}), std::map<int, double>>); /* This is not deducible, ambiguous candidates: @@ -92,7 +92,7 @@ void f() static_assert(std::is_same_v< decltype(std::map(x.begin(), x.end(), - {})), + std::less<int>{})), std::map<int, double>>); static_assert(std::is_same_v< @@ -145,7 +145,7 @@ void g() static_assert(std::is_same_v< decltype(std::map(x.begin(), x.end(), - {})), + std::less<int>{})), std::map<int, double>>); static_assert(std::is_same_v< @@ -195,7 +195,7 @@ void h() static_assert(std::is_same_v< decltype(std::map(x.begin(), x.end(), - {})), + std::less<int>{})), std::map<int, double>>); static_assert(std::is_same_v< diff --git a/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc index 791cc963479..ffc7502d60c 100644 --- a/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc @@ -42,7 +42,7 @@ static_assert(std::is_same_v< static_assert(std::is_same_v< decltype(std::multimap{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, - {}}), + std::less<int>{}}), std::multimap<int, double>>); static_assert(std::is_same_v< @@ -77,7 +77,7 @@ void f() static_assert(std::is_same_v< decltype(std::multimap(x.begin(), x.end(), - {})), + std::less<int>{})), std::multimap<int, double>>); static_assert(std::is_same_v< @@ -119,7 +119,7 @@ void g() static_assert(std::is_same_v< decltype(std::multimap(x.begin(), x.end(), - {})), + std::less<int>{})), std::multimap<int, double>>); static_assert(std::is_same_v< @@ -158,7 +158,7 @@ void h() static_assert(std::is_same_v< decltype(std::multimap(x.begin(), x.end(), - {})), + std::less<int>{})), std::multimap<int, double>>); static_assert(std::is_same_v< diff --git a/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc index ad12755ccc6..a4ccc6fa467 100644 --- a/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/multiset/cons/deduction.cc @@ -19,9 +19,11 @@ static_assert(std::is_same_v< decltype(std::multiset{{1, 2, 3}, std::less<int>{}, {}}), std::multiset<int>>); +/* FIXME: GCC 12 rejects this due to PR c++/101174 static_assert(std::is_same_v< - decltype(std::multiset{{1, 2, 3}, {}}), + decltype(std::multiset{{1, 2, 3}, std::less<int>{}}), std::multiset<int>>); +*/ static_assert(std::is_same_v< decltype(std::multiset{{1, 2, 3}, SimpleAllocator<int>{}}), @@ -52,7 +54,7 @@ void f() static_assert(std::is_same_v< decltype(std::multiset(x.begin(), x.end(), - {})), + std::less<int>{})), std::multiset<int>>); static_assert(std::is_same_v< @@ -103,7 +105,7 @@ void g() static_assert(std::is_same_v< decltype(std::multiset(x.begin(), x.end(), - {})), + std::less<int>{})), std::multiset<int>>); static_assert(std::is_same_v< diff --git a/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc index 89a2c43b937..0ae4c2a5c5f 100644 --- a/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/set/cons/deduction.cc @@ -20,10 +20,12 @@ static_assert(std::is_same_v< std::less<int>{}, {}}), std::set<int>>); +/* FIXME: GCC 12 rejects this due to PR c++/101174 static_assert(std::is_same_v< decltype(std::set{{1, 2, 3}, - {}}), + std::less<int>{}}), std::set<int>>); +*/ static_assert(std::is_same_v< decltype(std::set{{1, 2, 3}, @@ -58,7 +60,7 @@ void f() static_assert(std::is_same_v< decltype(std::set(x.begin(), x.end(), - {})), + std::less<int>{})), std::set<int>>); static_assert(std::is_same_v< @@ -104,7 +106,7 @@ void g() static_assert(std::is_same_v< decltype(std::set(x.begin(), x.end(), - {})), + std::less<int>{})), std::set<int>>); static_assert(std::is_same_v< 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..0785447a81b 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc @@ -24,7 +24,13 @@ static_assert(std::is_same_v< 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::hash<int>{}, std::equal_to<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}}, + {}, std::hash<int>{}, std::allocator<std::pair<const int, double>>{}}), std::unordered_map<int, double>>); static_assert(std::is_same_v< @@ -59,9 +65,14 @@ void f() static_assert(std::is_same_v< decltype(std::unordered_map{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), + {}, std::hash<int>{}, std::equal_to<int>{}}), std::unordered_map<int, double>>); - + + static_assert(std::is_same_v< + decltype(std::unordered_map{x.begin(), x.end(), + {}, std::hash<int>{}, 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(), {})), 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..d8a6f5136c9 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc @@ -18,7 +18,13 @@ static_assert(std::is_same_v< 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::hash<int>{}, std::equal_to<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}}, + {}, std::hash<int>{}, std::allocator<std::pair<const int, double>>{}}), std::unordered_multimap<int, double>>); static_assert(std::is_same_v< @@ -68,9 +74,14 @@ void f() static_assert(std::is_same_v< decltype(std::unordered_multimap{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), + {}, std::hash<int>{}, std::equal_to<int>{}}), std::unordered_multimap<int, double>>); - + + static_assert(std::is_same_v< + decltype(std::unordered_multimap{x.begin(), x.end(), + {}, std::hash<int>{}, 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(), {})), 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..25c2715ea26 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc @@ -11,7 +11,12 @@ static_assert(std::is_same_v< static_assert(std::is_same_v< decltype(std::unordered_multiset{{1, 2, 3}, - 0, std::hash<int>{}, {}}), + 0, std::hash<int>{}, std::equal_to<int>{}}), + std::unordered_multiset<int>>); + +static_assert(std::is_same_v< + decltype(std::unordered_multiset{{1, 2, 3}, + 0, std::hash<int>{}, std::allocator<int>{}}), std::unordered_multiset<int>>); static_assert(std::is_same_v< @@ -78,7 +83,12 @@ void f() static_assert(std::is_same_v< decltype(std::unordered_multiset{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), + {}, std::hash<int>{}, std::equal_to<int>{}}), + std::unordered_multiset<int>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multiset{x.begin(), x.end(), + {}, std::hash<int>{}, std::allocator<int>{}}), std::unordered_multiset<int>>); static_assert(std::is_same_v< 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..b8c45d2993d 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc @@ -11,7 +11,12 @@ static_assert(std::is_same_v< static_assert(std::is_same_v< decltype(std::unordered_set{{1, 2, 3}, - 0, std::hash<int>{}, {}}), + 0, std::hash<int>{}, std::equal_to<int>{}}), + std::unordered_set<int>>); + +static_assert(std::is_same_v< + decltype(std::unordered_set{{1, 2, 3}, + 0, std::hash<int>{}, std::allocator<int>{}}), std::unordered_set<int>>); static_assert(std::is_same_v< @@ -73,7 +78,12 @@ void f() static_assert(std::is_same_v< decltype(std::unordered_set{x.begin(), x.end(), - {}, std::hash<int>{}, {}}), + {}, std::hash<int>{}, std::equal_to<int>{}}), + std::unordered_set<int>>); + + static_assert(std::is_same_v< + decltype(std::unordered_set{x.begin(), x.end(), + {}, std::hash<int>{}, std::allocator<int>{}}), std::unordered_set<int>>); static_assert(std::is_same_v<
On Wed, 23 Jun 2021, 05:20 Patrick Palka via Libstdc++, < libstdc++@gcc.gnu.org> wrote: > On Tue, 22 Jun 2021, Jonathan Wakely wrote: > > > On Tue, 22 Jun 2021 at 19:45, Patrick Palka wrote: > > > 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. > > > > Do all the problematic cases have a corresponding case that doesn't > > use {} but uses an actual type? > > > > If not, we might want to add such cases, to ensure we're still > > covering all the cases that really *should* work. > > Ah, it looks like most tests have one such corresponding case, but since > the {} can potentially be an allocator or a function, for maximum > coverage we should have two corresponding cases. So the revised patch > below instead replaces each problematic CTAD example with the other > untested case. > > Interestingly two of these new CTAD examples for std::set and std::multiset > end up triggering an unrelated CTAD bug PR101174 on trunk, so the patch > comments out these adjusted examples for now. > OK, thanks.
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(), {})),