[gcc r11-7483] c++: ICE with deduction guide in checking type-dep [PR99009, PR97034]
Marek Polacek
mpolacek@gcc.gnu.org
Wed Mar 3 14:53:35 GMT 2021
https://gcc.gnu.org/g:1dabbfb0f4a9fbdc77e1ea4db7302586f00895e1
commit r11-7483-g1dabbfb0f4a9fbdc77e1ea4db7302586f00895e1
Author: Marek Polacek <polacek@redhat.com>
Date: Fri Feb 12 12:21:15 2021 -0500
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.
Diff:
---
gcc/cp/pt.c | 36 ++++++++++++++++++++--
gcc/testsuite/g++.dg/cpp1z/class-deduction81.C | 20 ++++++++++++
gcc/testsuite/g++.dg/cpp1z/class-deduction82.C | 12 ++++++++
.../g++.dg/cpp2a/class-deduction-aggr10.C | 21 +++++++++++++
gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C | 19 ++++++++++++
gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C | 18 +++++++++++
6 files changed, 123 insertions(+), 3 deletions(-)
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 8d65a6e5bd2..a4686e0affb 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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))
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction81.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction81.C
new file mode 100644
index 00000000000..86a68248157
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction81.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction82.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction82.C
new file mode 100644
index 00000000000..238024c508f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction82.C
@@ -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; };
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr10.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr10.C
new file mode 100644
index 00000000000..be922bbfb73
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr10.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C
new file mode 100644
index 00000000000..399061100ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr8.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C
new file mode 100644
index 00000000000..245a04cd5f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr9.C
@@ -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 ();
+}
More information about the Gcc-cvs
mailing list