diff mbox series

c++: Fix constrained auto deduction templ parms resolution [PR114915, PR115030]

Message ID 20240811194152.461060-1-sska1377@gmail.com
State New
Headers show
Series c++: Fix constrained auto deduction templ parms resolution [PR114915, PR115030] | expand

Commit Message

Seyed Sajad Kahani Aug. 11, 2024, 7:41 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).

Note that `DECL_TI_ARGS` for partial and explicit specializations will yield the
arguments with respect to the most_general_template, which is the primary
template. This can lead to rejection of valid code or acceptance of invalid code
(PR115030) in a partial specialization context. For an explicitly specialized
case, due to the mismatch between the desired depth and the actual depth of
args, it can lead to ICEs (PR114915) where we intend to fill the missing levels
with dummy levels (pt.cc:31260), while the missing levels are negative.

This patch resolves PR114915 and PR115030 by replacing the logic of extracting
args for the declaration in those two places with `outer_template_args`.
`outer_template_args` is an existing function that was used in limited contexts to
do so. Now, it is extended to handle partial and explicit specializations and
lambda functions as well. A few inevitable changes are also made to the
signature of some functions, relaxing `const_tree` to `tree`.

	PR c++/114915
	PR c++/115030

gcc/cp/ChangeLog:

	* constraint.cc (maybe_substitute_reqs_for): Relax the argument type to
	be compatible with outer_template_args.
	* cp-tree.h (outer_template_args): Relax the argument type and add an
	optional argument.
	(maybe_substitute_reqs_for): Relax the argument type to be compatible
	with outer_template_args.
	* decl.cc (cp_finish_decl): Replace the logic of extracting args with
	outer_template_args.
	* pt.cc (outer_template_args): Handle partial and explicit
	specializations and lambda functions.
	(do_auto_deduction): Replace the logic of extracting args with
	outer_template_args.

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.
	* g++.dg/cpp2a/concepts-placeholder17.C: New test.
---
 gcc/cp/constraint.cc                          |  2 +-
 gcc/cp/cp-tree.h                              |  4 +-
 gcc/cp/decl.cc                                |  4 +-
 gcc/cp/pt.cc                                  | 71 ++++++++++++-------
 .../g++.dg/cpp2a/concepts-placeholder14.C     | 19 +++++
 .../g++.dg/cpp2a/concepts-placeholder15.C     | 26 +++++++
 .../g++.dg/cpp2a/concepts-placeholder16.C     | 33 +++++++++
 .../g++.dg/cpp2a/concepts-placeholder17.C     | 21 ++++++
 8 files changed, 150 insertions(+), 30 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
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
diff mbox series

Patch

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index ebf4255e5..a1c3962c4 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1332,7 +1332,7 @@  remove_constraints (tree t)
    for declaration matching.  */
 
 tree
-maybe_substitute_reqs_for (tree reqs, const_tree decl)
+maybe_substitute_reqs_for (tree reqs, tree decl)
 {
   if (reqs == NULL_TREE)
     return NULL_TREE;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9a8c86591..2d6733f57 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7215,7 +7215,7 @@  extern tree maybe_set_retval_sentinel		(void);
 extern tree template_parms_to_args		(tree);
 extern tree template_parms_level_to_args	(tree);
 extern tree generic_targs_for			(tree);
-extern tree outer_template_args			(const_tree);
+extern tree outer_template_args			(tree, bool = true);
 
 /* in expr.cc */
 extern tree cplus_expand_constant		(tree);
@@ -8560,7 +8560,7 @@  extern void remove_constraints                  (tree);
 extern tree current_template_constraints	(void);
 extern tree associate_classtype_constraints     (tree);
 extern tree build_constraints                   (tree, tree);
-extern tree maybe_substitute_reqs_for		(tree, const_tree);
+extern tree maybe_substitute_reqs_for		(tree, tree);
 extern tree get_trailing_function_requirements	(tree);
 extern tree get_shorthand_constraints           (tree);
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index a139b293e..6b68d5f39 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8557,10 +8557,10 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	  && DECL_LANG_SPECIFIC (decl)
 	  && DECL_TEMPLATE_INFO (decl)
 	  && !DECL_FUNCTION_SCOPE_P (decl))
-	/* The outer template arguments might be needed for satisfaction.
+       /* The outer template arguments might be needed for satisfaction.
 	   (For function scope variables, do_auto_deduction will obtain the
 	   outer template arguments from current_function_decl.)  */
-	outer_targs = DECL_TI_ARGS (decl);
+        tree outer_targs = outer_template_args (decl, false);
       type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, auto_node,
 						   tf_warning_or_error, adc,
 						   outer_targs, flags);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 32640f8e9..e9bad58f3 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -4998,23 +4998,57 @@  generic_targs_for (tree tmpl)
 /* Return the template arguments corresponding to the template parameters of
    DECL's enclosing scope.  When DECL is a member of a partial specialization,
    this returns the arguments for the partial specialization as opposed to those
-   for the primary template, which is the main difference between this function
-   and simply using e.g. the TYPE_TI_ARGS of DECL's DECL_CONTEXT.  */
-
+   for the primary template, and for a full specialization, it returns null.
+   STRIP_CURRENT specifies whether it should include currently declared 
+   templates or not.  */
 tree
-outer_template_args (const_tree decl)
+outer_template_args (tree decl, bool strip_current /* = true */) 
 {
   if (TREE_CODE (decl) == TEMPLATE_DECL)
     decl = DECL_TEMPLATE_RESULT (decl);
   tree ti = get_template_info (decl);
+  tree args = NULL_TREE;
+
   if (!ti)
-    return NULL_TREE;
-  tree args = TI_ARGS (ti);
-  if (!PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
-    return args;
-  if (TMPL_ARGS_DEPTH (args) == 1)
-    return NULL_TREE;
-  return strip_innermost_template_args (args, 1);
+    {
+      if (DECL_FUNCTION_SCOPE_P (decl))
+	args = outer_template_args (DECL_CONTEXT (decl), false);
+    }
+  else
+    {
+      if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)) && TI_PARTIAL_INFO (ti))
+	ti = TI_PARTIAL_INFO (ti);
+
+      /* For an explicitly specialized declaration. */
+      if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_SPECIALIZATION (decl))
+	args = NULL_TREE;
+      else
+	{
+	  args = TI_ARGS (ti);
+
+	  int parms_depth = 
+	    TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (TI_TEMPLATE (ti)));
+	  /* If any of outer scopes are explicitly specialized. */
+	  if (TMPL_ARGS_DEPTH(args) > parms_depth)
+	    args = get_innermost_template_args (args, parms_depth);
+
+	  if (strip_current && PRIMARY_TEMPLATE_P (TI_TEMPLATE (ti)))
+	    {
+	      if (TMPL_ARGS_DEPTH (args) == 1)
+		return NULL_TREE;
+	      return strip_innermost_template_args (args, 1);
+	    }
+	}
+    }
+
+  /* Special treatment of lambda functions. */
+  if (LAMBDA_FUNCTION_P (decl) && !strip_current)
+    {
+	tree regen_targs = lambda_regenerating_args (decl);
+	args =  add_to_template_args (regen_targs, args);
+    }
+
+  return args;
 }
 
 /* Update the declared TYPE by doing any lookups which were thought to be
@@ -31233,20 +31267,7 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 	  || context == adc_variable_type
 	  || context == adc_decomp_type)
 	if (tree fn = current_function_decl)
-	  if (DECL_TEMPLATE_INFO (fn) || LAMBDA_FUNCTION_P (fn))
-	    {
-	      outer_targs = DECL_TEMPLATE_INFO (fn)
-		? DECL_TI_ARGS (fn) : NULL_TREE;
-	      if (LAMBDA_FUNCTION_P (fn))
-		{
-		  /* As in satisfy_declaration_constraints.  */
-		  tree regen_args = lambda_regenerating_args (fn);
-		  if (outer_targs)
-		    outer_targs = add_to_template_args (regen_args, outer_targs);
-		  else
-		    outer_targs = regen_args;
-		}
-	    }
+	  outer_targs = outer_template_args(fn, false);
 
       tree full_targs = outer_targs;
       if (context == adc_unify && tmpl)
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;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
new file mode 100644
index 000000000..4a8dcb3d5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
@@ -0,0 +1,21 @@ 
+
+// PR c++/115030
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template <typename T>
+struct s {
+};
+
+template <typename T>
+char v = 'a';
+
+template<typename T>
+C<T> auto v<s<T>> = 'c';
+
+int main() {
+  v<s<char>> = 'b';
+  return 0;
+}
\ No newline at end of file