My recent r11-7454 changed the way do_auto_deduction handles constrained
placeholders during template argument deduction (context == adc_unify)
when processing_template_decl != 0.

Before the patch, when processing_template_decl != 0 we would just
ignore the constraints on the placeholder in this situation, and proceed
with deduction:

  /* Check any placeholder constraints against the deduced type. */
  if (flag_concepts && !processing_template_decl)
    if (tree check = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
      {
        ...

After the patch, we now punt and return the original placeholder type:

  /* Check any placeholder constraints against the deduced type. */
  if (flag_concepts)
    if (NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
      {
        if (processing_template_decl)
          /* In general we can't check satisfaction until we know all
             template arguments.  */
          return type;
        ...

While this change fixed instances where we'd prematurely resolve a
constrained placeholder return or variable type with non-dependent
initializer at template parse time (such as PR96444), it broke the
adc_unify callers that rely on this previous behavior.

So this patch restores the previous behavior during adc_unify deduction
while retaining the new behavior only during adc_variable_type or
adc_return_type deduction.

We additionally now need to pass outer template arguments to
do_auto_deduction during unify, for sake of constraint checking.
But we don't want do_auto_deduction to substitute these outer arguments
into type if it's already been done, hence the added TEMPLATE_TYPE_LEVEL
check.

This fixes partial specializations of non-nested templates with
constrained 'auto' template parameters, but nested templates are still
broken, ultimately because most_specialized_partial_spec passes only the
innermost template arguments to get_partial_spec_bindings, and so
outer_targs during do_auto_deduction (called from unify) contains only
the innermost template arguments which makes satisfaction unhappy.
Fixing this might be too invasive at this stage, perhaps..  (Seems we
need to make most_specialized_partial_spec pass all template arguments
to get_partial_spec_bindings.)

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?  Also tested on range-v3 and cmcstl2.

gcc/cp/ChangeLog:

        PR c++/99365
        * pt.c (do_auto_deduction): When processing_template_decl != 0
        and context is adc_unify and we have constraints, pretend the
        constraints are satisfied instead of punting.  Add some
        clarifying sanity checks.  Don't substitute outer_targs into
        type if not needed.

gcc/testsuite/ChangeLog:

        PR c++/99365
        * g++.dg/cpp2a/concepts-partial-spec9.C: New test.
---
 gcc/cp/pt.c                                   | 98 +++++++++++--------
 .../g++.dg/cpp2a/concepts-partial-spec9.C     | 24 +++++
 2 files changed, 79 insertions(+), 43 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index a4686e0affb..ce537e4529a 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -23693,7 +23693,8 @@ unify (tree tparms, tree targs, tree parm, tree arg, 
int strict,
 
          if (tree a = type_uses_auto (tparm))
            {
-             tparm = do_auto_deduction (tparm, arg, a, complain, adc_unify);
+             tparm = do_auto_deduction (tparm, arg, a,
+                                        complain, adc_unify, targs);
              if (tparm == error_mark_node)
                return 1;
            }
@@ -29619,52 +29620,63 @@ do_auto_deduction (tree type, tree init, tree 
auto_node,
     }
 
   /* Check any placeholder constraints against the deduced type. */
-  if (flag_concepts)
-    if (NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
-      {
-       if (processing_template_decl)
-         /* In general we can't check satisfaction until we know all
-            template arguments.  */
+  if (processing_template_decl && context == adc_unify)
+    /* Pretend constraints are satisfied.  */;
+  else if (flag_concepts
+          && NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
+    {
+      if (processing_template_decl)
+       {
+         /* Even though the initializer is non-dependent, we need to wait until
+            instantiation time to resolve this constrained placeholder variable
+            or return type, since the constraint itself may be dependent.  */
+         gcc_checking_assert (context == adc_variable_type
+                              || context == adc_return_type);
+         gcc_checking_assert (!type_dependent_expression_p (init));
          return type;
+       }
 
-       if ((context == adc_return_type || context == adc_variable_type)
-           && current_function_decl
-           && DECL_TEMPLATE_INFO (current_function_decl))
-         outer_targs = DECL_TI_ARGS (current_function_decl);
+      if ((context == adc_return_type || context == adc_variable_type)
+         && current_function_decl
+         && DECL_TEMPLATE_INFO (current_function_decl))
+       outer_targs = DECL_TI_ARGS (current_function_decl);
 
-       tree full_targs = add_to_template_args (outer_targs, targs);
-       if (!constraints_satisfied_p (auto_node, full_targs))
-          {
-            if (complain & tf_warning_or_error)
-              {
-               auto_diagnostic_group d;
-                switch (context)
-                  {
-                  case adc_unspecified:
-                 case adc_unify:
-                    error("placeholder constraints not satisfied");
-                    break;
-                  case adc_variable_type:
-                 case adc_decomp_type:
-                    error ("deduced initializer does not satisfy "
-                           "placeholder constraints");
-                    break;
-                  case adc_return_type:
-                    error ("deduced return type does not satisfy "
-                           "placeholder constraints");
-                    break;
-                  case adc_requirement:
-                   error ("deduced expression type does not satisfy "
-                           "placeholder constraints");
-                    break;
-                  }
-               diagnose_constraints (input_location, auto_node, full_targs);
-              }
-            return error_mark_node;
-          }
-      }
+      tree full_targs = add_to_template_args (outer_targs, targs);
+      if (!constraints_satisfied_p (auto_node, full_targs))
+       {
+         if (complain & tf_warning_or_error)
+           {
+             auto_diagnostic_group d;
+             switch (context)
+               {
+               case adc_unspecified:
+               case adc_unify:
+                 error("placeholder constraints not satisfied");
+                 break;
+               case adc_variable_type:
+               case adc_decomp_type:
+                 error ("deduced initializer does not satisfy "
+                        "placeholder constraints");
+                 break;
+               case adc_return_type:
+                 error ("deduced return type does not satisfy "
+                        "placeholder constraints");
+                 break;
+               case adc_requirement:
+                 error ("deduced expression type does not satisfy "
+                        "placeholder constraints");
+                 break;
+               }
+             diagnose_constraints (input_location, auto_node, full_targs);
+           }
+         return error_mark_node;
+       }
+    }
 
-  if (context == adc_unify)
+  if (TEMPLATE_TYPE_LEVEL (auto_node) == 1)
+    /* The outer template arguments are already substituted into type
+       (but we still may have used them for constraint checking above).  */;
+  else if (context == adc_unify)
     targs = add_to_template_args (outer_targs, targs);
   else if (processing_template_decl)
     targs = add_to_template_args (current_template_args (), targs);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C
new file mode 100644
index 00000000000..b79d12b6f17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-partial-spec9.C
@@ -0,0 +1,24 @@
+// PR c++/99365
+// { dg-do compile { target c++20 } }
+
+template <class> concept C = true;
+template <class T, class U> concept D = C<T> && __is_same(T, U);
+
+template <class, C auto> struct A { static const int i = 0; };
+template <class T, D<T> auto V> struct A<T, V> { static const int i = 1; };
+
+static_assert(A<int, 0>::i == 1);
+static_assert(A<char, 0>::i == 0);
+static_assert(A<int, '0'>::i == 0);
+static_assert(A<char, '0'>::i == 1);
+
+/* Partial specialization of nested class template with constrained 'auto'
+   template parameter still broken:
+
+template <class> struct O {
+  template <C auto> struct B { static const int i = 0; };
+  template <D auto V> struct B<V> { static const int i = 1; };
+};
+
+static_assert(O<void>::B<int, 0>::i == 0);
+static_assert(O<void>::B<int, '0'>::i == 1);  */
-- 
2.31.0.rc0.75.gec125d1bc1

Reply via email to