c++: ICE with deduction guide in checking type-dep [PR99009, PR97034]

We represent deduction guides with FUNCTION_DECLs, but they are built
without DECL_CONTEXT, leading to an ICE in type_dependent_expression_p
on the assert that the type of a function template with no dependent
(innermost!) template arguments must be non-dependent.  Consider the
attached class-deduction79.C: we create a deduction guide:

  template<class T> G(T)-> E<Z>::G<T>

we deduce T and create a partial instantiation:

  G(T) -> E<Z>::G<T> [with T = int]

And then do_class_deduction wants to create a CALL_EXPR from the above
using build_new_function_call -> build_over_call which calls mark_used
-> maybe_instantiate_noexcept -> type_dependent_expression_p.

There, the innermost template arguments are non-dependent (<int>), but
the fntype is dependent -- the return type is a TYPENAME_TYPE, and
since we have no DECL_CONTEXT, this check holds:

  /* Otherwise, if the function decl isn't from a dependent scope, it can't be
     type-dependent.  Checking this is important for functions with auto return
     type, which looks like a dependent type.  */
  if (TREE_CODE (expression) == FUNCTION_DECL
      && !(DECL_CLASS_SCOPE_P (expression)
           && dependent_type_p (DECL_CONTEXT (expression)))

whereupon we ICE.

This patch fixes it by deferring the class deduction until the
enclosing scope is non-dependent.  build_deduction_guide and maybe_aggr_guide
needed a little tweaking to make the deduction work in a member
template.

Co-Authored-By: Jason Merrill <jason@redhat.com>

gcc/cp/ChangeLog:

	PR c++/97034
	PR c++/99009
	* pt.c (build_deduction_guide): Use INNERMOST_TEMPLATE_ARGS.
	(maybe_aggr_guide): Use the original template type where needed.  In
	a class member template, partially instantiate the result of
	collect_ctor_idx_types.
	(do_class_deduction): Defer the deduction until the enclosing
	scope is non-dependent.

gcc/testsuite/ChangeLog:

	PR c++/97034
	PR c++/99009
	* g++.dg/cpp1z/class-deduction81.C: New test.
	* g++.dg/cpp1z/class-deduction82.C: New test.
	* g++.dg/cpp2a/class-deduction-aggr8.C: New test.
	* g++.dg/cpp2a/class-deduction-aggr9.C: New test.
	* g++.dg/cpp2a/class-deduction-aggr10.C: New test.
This commit is contained in:
Marek Polacek 2021-02-12 12:21:15 -05:00
parent 15cf7fe355
commit 1dabbfb0f4
6 changed files with 123 additions and 3 deletions

View File

@ -28643,7 +28643,7 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com
tree ctmpl = CLASSTYPE_TI_TEMPLATE (type);
tparms = DECL_TEMPLATE_PARMS (ctmpl);
targs = CLASSTYPE_TI_ARGS (type);
targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
ci = NULL_TREE;
fargs = NULL_TREE;
loc = DECL_SOURCE_LOCATION (ctmpl);
@ -28866,8 +28866,22 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
if (init == NULL_TREE)
return NULL_TREE;
/* We might be creating a guide for a class member template, e.g.,
template<typename U> struct A {
template<typename T> struct B { T t; };
};
At this point, A will have been instantiated. Below, we need to
use both A<U>::B<T> (TEMPLATE_TYPE) and A<int>::B<T> (TYPE) types. */
const bool member_template_p
= (DECL_TEMPLATE_INFO (tmpl)
&& DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (tmpl)));
tree type = TREE_TYPE (tmpl);
if (!CP_AGGREGATE_TYPE_P (type))
tree template_type = (member_template_p
? TREE_TYPE (DECL_TI_TEMPLATE (tmpl))
: type);
if (!CP_AGGREGATE_TYPE_P (template_type))
return NULL_TREE;
/* No aggregate candidate for copy-initialization. */
@ -28884,10 +28898,21 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
tree parms = NULL_TREE;
if (BRACE_ENCLOSED_INITIALIZER_P (init))
{
init = reshape_init (type, init, complain);
init = reshape_init (template_type, init, complain);
if (init == error_mark_node)
return NULL_TREE;
parms = collect_ctor_idx_types (init, parms);
/* If we're creating a deduction guide for a member class template,
we've used the original template pattern type for the reshape_init
above; this is done because we want PARMS to be a template parameter
type, something that can be deduced when used as a function template
parameter. At this point the outer class template has already been
partially instantiated (we deferred the deduction until the enclosing
scope is non-dependent). Therefore we have to partially instantiate
PARMS, so that its template level is properly reduced and we don't get
mismatches when deducing types using the guide with PARMS. */
if (member_template_p)
parms = tsubst (parms, DECL_TI_ARGS (tmpl), complain, init);
}
else if (TREE_CODE (init) == TREE_LIST)
{
@ -29225,6 +29250,11 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))
return ptype;
/* Wait until the enclosing scope is non-dependent. */
if (DECL_CLASS_SCOPE_P (tmpl)
&& dependent_type_p (DECL_CONTEXT (tmpl)))
return ptype;
/* Initializing one placeholder from another. */
if (init && TREE_CODE (init) == TEMPLATE_PARM_INDEX
&& is_auto (TREE_TYPE (init))

View File

@ -0,0 +1,20 @@
// PR c++/97034
// { dg-do compile { target c++17 } }
template <typename Z>
struct E {
template <typename T>
struct G {
T t;
G(T) { }
};
void fn() { G{1}; }
};
void
g ()
{
E<int> e;
e.fn ();
}

View File

@ -0,0 +1,12 @@
// PR c++/99009
// { dg-do compile { target c++17 } }
template<typename> struct B {
B(int = A()) {}
template <typename ...> struct A;
};
template<typename T> struct X {
template <T...> struct Y;
X() { Y y; };
};

View File

@ -0,0 +1,21 @@
// PR c++/97034
// { dg-do compile { target c++20 } }
namespace N {
template <typename, typename> struct S {
template <typename T, typename U> S(T, U);
};
} // namespace N
template <int I> struct E {
template<typename U> struct M {
template <typename T> struct G { T t; };
void fn() { G{N::S<char, int>{'a', 1}}; }
};
};
void
g ()
{
E<1>::M<int> m;
m.fn ();
}

View File

@ -0,0 +1,19 @@
// PR c++/97034
// { dg-do compile { target c++20 } }
namespace N {
template <typename, typename> struct S {
template <typename T, typename U> S(T, U);
};
} // namespace N
template <int> struct E {
template <typename T> struct G { T t; };
void fn() { G{N::S<char, int>{'a', 1}}; }
};
void
g ()
{
E<1> e;
e.fn ();
}

View File

@ -0,0 +1,18 @@
// PR c++/97034
// { dg-do compile { target c++20 } }
template<typename>
struct E {
template <typename T>
struct G {
T t;
};
void fn() { G{1}; }
};
void
g () {
E<int> e;
e.fn ();
}