On Wed, 21 Apr 2021, Patrick Palka wrote: > On Wed, 21 Apr 2021, Jason Merrill wrote: > > > On 4/12/21 1:20 PM, Patrick Palka wrote: > > > Here we're crashing during deduction for a template placeholder from a > > > dependent initializer because one of the initializer's elements has an > > > empty TREE_TYPE, something which resolve_args and later unify_one_argument > > > don't expect. And if the deduction from a dependent initializer > > > otherwise fails, we prematurely issue an error rather than reattempting > > > the deduction at instantiation time. > > > > > > This patch makes do_class_deduction more tolerant about dependent > > > initializers, in a manner similar to what do_auto_deduction does: if > > > deduction from a dependent initializer fails, just return the original > > > placeholder unchanged. > > > > Why doesn't the type_dependent_expression_p check in do_auto_deduction catch > > this already? > > That check applies only when context != adc_unify, but here we have > context == adc_unify since we're being called from > convert_template_argument. > > And currently, when 'auto' deduction fails for a dependent initializer, > do_auto_deduction will just silently return the original placeholder: > > int val = type_unification_real (tparms, targs, parms, &init, 1, 0, > DEDUCE_CALL, > NULL, /*explain_p=*/false); > if (val > 0) > { > if (processing_template_decl) > /* Try again at instantiation time. */ > return type; > > so I suppose this patch just makes do_class_deduction behave more > similarly to do_auto_deduction in this situation.
On second thought, I think attempting CTAD a dependent initializer as the patch does might sometimes give us the wrong answer. If e.g. the class template in question has the deduction guides template <class T> A(T) -> A<char>; A(int) -> A<void>; then ahead-of-time CTAD for A{v} where v is type-dependent will succeed and resolve to A<char>, but at instantiation time the type of v might be int. So perhaps we should just have do_class_deduction punt on all type-dependent expressions, e.g. -- >8 -- gcc/cp/ChangeLog: PR c++/89565 PR c++/93383 PR c++/99200 * pt.c (do_class_deduction): Give up if the initializer is type-dependent. gcc/testsuite/ChangeLog: PR c++/89565 PR c++/93383 PR c++/99200 * g++.dg/cpp2a/nontype-class39.C: Remove dg-ice. * g++.dg/cpp2a/nontype-class45.C: New test. * g++.dg/cpp2a/nontype-class46.C: New test. --- gcc/cp/pt.c | 5 +++ gcc/testsuite/g++.dg/cpp2a/nontype-class39.C | 2 -- gcc/testsuite/g++.dg/cpp2a/nontype-class45.C | 32 ++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/nontype-class46.C | 11 +++++++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class45.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class46.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 7bcbe6dc3ce..6673f935ab6 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -29362,6 +29362,11 @@ do_class_deduction (tree ptype, tree tmpl, tree init, return error_mark_node; } + /* If the initializer is dependent, we can't resolve the class template + placeholder ahead of time. */ + if (type_dependent_expression_p (init)) + return ptype; + tree type = TREE_TYPE (tmpl); bool try_list_ctor = false; diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class39.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class39.C index 512afad8e4f..9b4da4f02ea 100644 --- a/gcc/testsuite/g++.dg/cpp2a/nontype-class39.C +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class39.C @@ -1,7 +1,5 @@ // PR c++/89565 // { dg-do compile { target c++20 } } -// { dg-additional-options "-fchecking" } -// { dg-ice "resolve_args" } template <auto> struct N{}; diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class45.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class45.C new file mode 100644 index 00000000000..e7addf5f291 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class45.C @@ -0,0 +1,32 @@ +// PR c++/99200 +// { dg-do compile { target c++20 } } + +template <int N> +struct A +{ + constexpr A (const char (&s)[N]) { for (int i = 0; i < N; i++) v[i] = s[i]; v[N] = 0; } + char v[N + 1]; +}; + +template <A s> +struct B +{ + constexpr operator const char *() { return s.v; } +}; + +template <typename T> +const char * +foo () +{ + return B<__PRETTY_FUNCTION__>{}; +} + +template <typename T> +const char * +bar () +{ + return B<__FUNCTION__>{}; +} + +auto a = foo <int> (); +auto b = bar <double> (); diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class46.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class46.C new file mode 100644 index 00000000000..d91e800424f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class46.C @@ -0,0 +1,11 @@ +// PR c++/93383 +// { dg-do compile { target c++20 } } + +template <int> struct A {}; + +template <A a> struct B { + void foo(B<+a>); + void bar(B<a.x>); + template <class T> using type = B<T{}>; + template <class> static inline auto y = A{0}; // { dg-error "deduction|no match" } +}; -- 2.31.1.362.g311531c9de