diff mbox series

[v6] c++: Fix constrained auto deduction in templ spec scopes [PR114915]

Message ID 20240718160322.133427-1-sska1377@gmail.com
State New
Headers show
Series [v6] c++: Fix constrained auto deduction in templ spec scopes [PR114915] | expand

Commit Message

Seyed Sajad Kahani July 18, 2024, 4:03 p.m. UTC
When deducing auto for `adc_return_type`, `adc_variable_type`, and
`adc_decomp_type` contexts (at the usage time), we try to resolve the outermost
template arguments to be used for satisfaction. This is done by one of the
following, depending on the scope:

1. Checking the `DECL_TEMPLATE_INFO` of the current function scope and
extracting DECL_TI_ARGS from it for function scope deductions (pt.cc:31236).
2. Checking the `DECL_TEMPLATE_INFO` of the declaration (alongside with other
conditions) for non-function scope variable declaration deductions
(decl.cc:8527).

Then, we do not retrieve the deeper layers of the template arguments; instead,
we fill the missing levels with dummy levels (pt.cc:31260).

The problem (that is shown in PR114915) is that we do not consider the case
where the deduction happens in a template specialization scope. In this case,
the type is not dependent on the outermost template arguments (which are
the specialization arguments). Yet, we still resolve the outermost template
arguments, and then the number of layers in the template arguments exceeds the
number of levels in the type. This causes the missing levels to be negative.
This leads to the rejection of valid code and ICEs (like segfault) in the
release mode. In the debug mode, it is possible to show as an assertion failure
(when creating a tree_vec with a negative size).

This patch resolves PR114915 by replacing the logic that fills in the
missing levels in do_auto_deduction in cp/pt.cc.
The new approach now trims targs if the depth of targs is deeper than desired
(this will only happen in specific contexts), and still fills targs with empty
layers if it has fewer depths than expected.

	PR c++/114915

gcc/cp/ChangeLog:

	* pt.cc (do_auto_deduction): Handle excess outer template
	arguments during constrained auto satisfaction.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-placeholder14.C: New test.
	* g++.dg/cpp2a/concepts-placeholder15.C: New test.
	* g++.dg/cpp2a/concepts-placeholder16.C: New test.
---
 gcc/cp/pt.cc                                  | 20 ++++++++---
 .../g++.dg/cpp2a/concepts-placeholder14.C     | 19 +++++++++++
 .../g++.dg/cpp2a/concepts-placeholder15.C     | 26 +++++++++++++++
 .../g++.dg/cpp2a/concepts-placeholder16.C     | 33 +++++++++++++++++++
 4 files changed, 94 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C

Comments

Jason Merrill July 19, 2024, 2:16 a.m. UTC | #1
On 7/18/24 12:03 PM, Seyed Sajad Kahani wrote:
> When deducing auto for `adc_return_type`, `adc_variable_type`, and
> `adc_decomp_type` contexts (at the usage time), we try to resolve the outermost
> template arguments to be used for satisfaction. This is done by one of the
> following, depending on the scope:
> 
> 1. Checking the `DECL_TEMPLATE_INFO` of the current function scope and
> extracting DECL_TI_ARGS from it for function scope deductions (pt.cc:31236).
> 2. Checking the `DECL_TEMPLATE_INFO` of the declaration (alongside with other
> conditions) for non-function scope variable declaration deductions
> (decl.cc:8527).
> 
> Then, we do not retrieve the deeper layers of the template arguments; instead,
> we fill the missing levels with dummy levels (pt.cc:31260).
> 
> The problem (that is shown in PR114915) is that we do not consider the case
> where the deduction happens in a template specialization scope. In this case,
> the type is not dependent on the outermost template arguments (which are
> the specialization arguments). Yet, we still resolve the outermost template
> arguments, and then the number of layers in the template arguments exceeds the
> number of levels in the type. This causes the missing levels to be negative.
> This leads to the rejection of valid code and ICEs (like segfault) in the
> release mode. In the debug mode, it is possible to show as an assertion failure
> (when creating a tree_vec with a negative size).
> 
> This patch resolves PR114915 by replacing the logic that fills in the
> missing levels in do_auto_deduction in cp/pt.cc.
> The new approach now trims targs if the depth of targs is deeper than desired
> (this will only happen in specific contexts), and still fills targs with empty
> layers if it has fewer depths than expected.

I would prefer to set outer_targs correctly in the first place, where 
it's currently set a few lines above.  And to factor that out so other 
callers can use it as well instead of DECL_TI_ARGS.

It seems like we want something close to outer_template_args, but it 
doesn't currently handle function scope decls or full specializations.

Jason
Seyed Sajad Kahani Aug. 11, 2024, 6:11 p.m. UTC | #2
Hi.

Based on your comments here, and the discussions in another thread
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/657703.html
I have made another patch to resolve this issue (as well as another
bug) that I will be sending right now.


On Thu, 2024-07-18 at 22:16 -0400, Jason Merrill wrote:
> On 7/18/24 12:03 PM, Seyed Sajad Kahani wrote:
> > When deducing auto for `adc_return_type`, `adc_variable_type`, and
> > `adc_decomp_type` contexts (at the usage time), we try to resolve
> > the outermost
> > template arguments to be used for satisfaction. This is done by one
> > of the
> > following, depending on the scope:
> > 
> > 1. Checking the `DECL_TEMPLATE_INFO` of the current function scope
> > and
> > extracting DECL_TI_ARGS from it for function scope deductions
> > (pt.cc:31236).
> > 2. Checking the `DECL_TEMPLATE_INFO` of the declaration (alongside
> > with other
> > conditions) for non-function scope variable declaration deductions
> > (decl.cc:8527).
> > 
> > Then, we do not retrieve the deeper layers of the template
> > arguments; instead,
> > we fill the missing levels with dummy levels (pt.cc:31260).
> > 
> > The problem (that is shown in PR114915) is that we do not consider
> > the case
> > where the deduction happens in a template specialization scope. In
> > this case,
> > the type is not dependent on the outermost template arguments
> > (which are
> > the specialization arguments). Yet, we still resolve the outermost
> > template
> > arguments, and then the number of layers in the template arguments
> > exceeds the
> > number of levels in the type. This causes the missing levels to be
> > negative.
> > This leads to the rejection of valid code and ICEs (like segfault)
> > in the
> > release mode. In the debug mode, it is possible to show as an
> > assertion failure
> > (when creating a tree_vec with a negative size).
> > 
> > This patch resolves PR114915 by replacing the logic that fills in
> > the
> > missing levels in do_auto_deduction in cp/pt.cc.
> > The new approach now trims targs if the depth of targs is deeper
> > than desired
> > (this will only happen in specific contexts), and still fills targs
> > with empty
> > layers if it has fewer depths than expected.
> 
> I would prefer to set outer_targs correctly in the first place, where
> it's currently set a few lines above.  And to factor that out so
> other 
> callers can use it as well instead of DECL_TI_ARGS.
> 
> It seems like we want something close to outer_template_args, but it 
> doesn't currently handle function scope decls or full
> specializations.
> 
> Jason
>
diff mbox series

Patch

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 32640f8e9..484dd5952 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -31253,6 +31253,19 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 	full_targs = add_outermost_template_args (tmpl, full_targs);
       full_targs = add_to_template_args (full_targs, targs);
 
+      int want = TEMPLATE_TYPE_ORIG_LEVEL (auto_node);
+      int have = TMPL_ARGS_DEPTH (full_targs);
+
+      if (want < have)
+	{
+	  /* If a constrained auto is declared in an explicit specialization. */
+	  gcc_assert (context == adc_variable_type || context == adc_return_type
+		      || context == adc_decomp_type);
+	  tree trimmed_full_args 
+	    = get_innermost_template_args (full_targs, want);
+	  full_targs = trimmed_full_args;
+	}
+
       /* HACK: Compensate for callers not always communicating all levels of
 	 outer template arguments by filling in the outermost missing levels
 	 with dummy levels before checking satisfaction.  We'll still crash
@@ -31260,11 +31273,10 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 	 these missing levels, but this hack otherwise allows us to handle a
 	 large subset of possible constraints (including all non-dependent
 	 constraints).  */
-      if (int missing_levels = (TEMPLATE_TYPE_ORIG_LEVEL (auto_node)
-				- TMPL_ARGS_DEPTH (full_targs)))
+      if (want > have)
 	{
-	  tree dummy_levels = make_tree_vec (missing_levels);
-	  for (int i = 0; i < missing_levels; ++i)
+	  tree dummy_levels = make_tree_vec (want - have);
+	  for (int i = 0; i < want - have; ++i)
 	    TREE_VEC_ELT (dummy_levels, i) = make_tree_vec (0);
 	  full_targs = add_to_template_args (dummy_levels, full_targs);
 	}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
new file mode 100644
index 000000000..fcdbd7608
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
@@ -0,0 +1,19 @@ 
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+concept C = __is_same(T, int);
+
+template<typename T>
+void f() {
+}
+
+template<>
+void f<int>() {
+  C auto x = 1;
+}
+
+int main() {
+  f<int>();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
new file mode 100644
index 000000000..b507e4165
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
@@ -0,0 +1,26 @@ 
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template<typename T>
+int x = 0;
+
+template<>
+C<double> auto x<double> = 1.0;
+
+template <typename T>
+struct S {};
+
+template<typename T>
+int y = 0;
+
+template<typename T>
+C<char> auto y<S<T>> = 'c';
+
+int main() {
+  if (y<S<int>> != 'c')
+      return 1;
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
new file mode 100644
index 000000000..f808ef1b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
@@ -0,0 +1,33 @@ 
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template<typename T>
+struct A
+{ 
+    template<typename U>
+    void f() {
+    }
+};
+ 
+template<>
+template<>
+void A<int>::f<int>() {
+  C<int> auto x = 1;
+}
+
+template<>
+template<typename U>
+void A<bool>::f() {
+  C<int> auto x = 1;
+}
+
+int main() {
+  A<bool> a;
+  a.f<char>();
+  A<int> b;
+  b.f<int>();
+  return 0;
+}