From: Marek Polacek Date: Fri, 12 Feb 2021 17:21:15 +0000 (-0500) Subject: c++: ICE with deduction guide in checking type-dep [PR99009, PR97034] X-Git-Tag: upstream/12.2.0~9471 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1dabbfb0f4a9fbdc77e1ea4db7302586f00895e1;p=platform%2Fupstream%2Fgcc.git 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 G(T)-> E::G we deduce T and create a partial instantiation: G(T) -> E::G [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 (), 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 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 --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 8d65a6e..a4686e0 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 *args) if (init == NULL_TREE) return NULL_TREE; + /* We might be creating a guide for a class member template, e.g., + + template struct A { + template struct B { T t; }; + }; + + At this point, A will have been instantiated. Below, we need to + use both A::B (TEMPLATE_TYPE) and A::B (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 *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 0000000..86a6824 --- /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 +struct E { + template + struct G { + T t; + G(T) { } + }; + + void fn() { G{1}; } +}; + +void +g () +{ + E 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 0000000..238024c --- /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 struct B { + B(int = A()) {} + template struct A; +}; + +template struct X { + template 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 0000000..be922bb --- /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 struct S { + template S(T, U); +}; +} // namespace N +template struct E { + template struct M { + template struct G { T t; }; + void fn() { G{N::S{'a', 1}}; } + }; +}; + +void +g () +{ + E<1>::M 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 0000000..3990611 --- /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 struct S { + template S(T, U); +}; +} // namespace N +template struct E { + template struct G { T t; }; + void fn() { G{N::S{'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 0000000..245a04c --- /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 +struct E { + template + struct G { + T t; + }; + + void fn() { G{1}; } +}; + +void +g () { + E e; + e.fn (); +}