On Wed, 28 Apr 2021, Jason Merrill wrote:

> On 4/28/21 2:24 PM, Patrick Palka wrote:
> > This makes tsubst_arg_types substitute into a function's parameter types
> > in left-to-right order instead of right-to-left order, in accordance with
> > DR 1227.
> > 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> > trunk?  [ diff generated with -w to hide noisy whitespace changes ]
> 
> OK. We'll still substitute lambda default args in reverse order, but that
> shouldn't happen in sfinae context, so that shouldn't be a problem.  Maybe add
> an assert (complain & tf_error) in the lambda case?

Apparently cpp2a/lambda-uneval2.C trips over such an assert during
deduction for:

  template <class T>
  auto j(T t) -> decltype([](auto x) -> decltype(x.invalid) { } (t));

which makes sense, I suppose.

It looks like we can just move up the default argument processing to
before the recursive call to tsubst_arg_types, as in the below.
Bootstrapped and regtested on x86_64-pc-linux-gnu.

BTW, now that LAMBDA_EXPR has LAMBDA_EXPR_REGEN_INFO I think we have
enough information to defer substituting into lambda default arguments
until they're actually needed.  Would that be something to look into as
a followup patch?

-- >8 --

Subject: [PATCH] c++: Substitute into function parms in lexical order
 [PR96560]

This makes tsubst_arg_types substitute into a function's parameter types
in left-to-right order instead of right-to-left order, in accordance with
DR 1227.

gcc/cp/ChangeLog:

        DR 1227
        PR c++/96560
        * pt.c (tsubst_arg_types): Rearrange so that we substitute into
        TYPE_ARG_TYPES in forward order while short circuiting
        appropriately.  Adjust formatting.

gcc/testsuite/ChangeLog:

        DR 1227
        PR c++/96560
        * g++.dg/template/sfinae-dr1227.C: New test.
---
 gcc/cp/pt.c                                   | 51 ++++++++++---------
 gcc/testsuite/g++.dg/template/sfinae-dr1227.C | 23 +++++++++
 2 files changed, 51 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/sfinae-dr1227.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index eaf46659f85..e6d65595e2f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15068,20 +15068,13 @@ tsubst_arg_types (tree arg_types,
                  tsubst_flags_t complain,
                  tree in_decl)
 {
-  tree remaining_arg_types;
   tree type = NULL_TREE;
-  int i = 1;
+  int len = 1;
   tree expanded_args = NULL_TREE;
-  tree default_arg;
 
   if (!arg_types || arg_types == void_list_node || arg_types == end)
     return arg_types;
 
-  remaining_arg_types = tsubst_arg_types (TREE_CHAIN (arg_types),
-                                         args, end, complain, in_decl);
-  if (remaining_arg_types == error_mark_node)
-    return error_mark_node;
-
   if (PACK_EXPANSION_P (TREE_VALUE (arg_types)))
     {
       /* For a pack expansion, perform substitution on the
@@ -15092,7 +15085,7 @@ tsubst_arg_types (tree arg_types,
 
       if (TREE_CODE (expanded_args) == TREE_VEC)
         /* So that we'll spin through the parameters, one by one.  */
-        i = TREE_VEC_LENGTH (expanded_args);
+       len = TREE_VEC_LENGTH (expanded_args);
       else
         {
           /* We only partially substituted into the parameter
@@ -15101,14 +15094,15 @@ tsubst_arg_types (tree arg_types,
           expanded_args = NULL_TREE;
         }
     }
+  else
+    type = tsubst (TREE_VALUE (arg_types), args, complain, in_decl);
 
-  while (i > 0) {
-    --i;
-
+  /* Check if a substituted type is erroneous before substituting into
+     the rest of the chain.  */
+  for (int i = 0; i < len; i++)
+    {
       if (expanded_args)
        type = TREE_VEC_ELT (expanded_args, i);
-    else if (!type)
-      type = tsubst (TREE_VALUE (arg_types), args, complain, in_decl);
 
       if (type == error_mark_node)
        return error_mark_node;
@@ -15122,15 +15116,12 @@ tsubst_arg_types (tree arg_types,
            }
          return error_mark_node;
        }
-
-    /* Do array-to-pointer, function-to-pointer conversion, and ignore
-       top-level qualifiers as required.  */
-    type = cv_unqualified (type_decays_to (type));
+    }
 
   /* We do not substitute into default arguments here.  The standard
      mandates that they be instantiated only when needed, which is
      done in build_over_call.  */
-    default_arg = TREE_PURPOSE (arg_types);
+  tree default_arg = TREE_PURPOSE (arg_types);
 
   /* Except that we do substitute default arguments under tsubst_lambda_expr,
      since the new op() won't have any associated template arguments for us
@@ -15139,20 +15130,34 @@ tsubst_arg_types (tree arg_types,
     default_arg = tsubst_copy_and_build (default_arg, args, complain, in_decl,
                                         false/*fn*/, false/*constexpr*/);
 
+  tree remaining_arg_types = tsubst_arg_types (TREE_CHAIN (arg_types),
+                                              args, end, complain, in_decl);
+  if (remaining_arg_types == error_mark_node)
+    return error_mark_node;
+
+  for (int i = len-1; i >= 0; i--)
+    {
+      if (expanded_args)
+       type = TREE_VEC_ELT (expanded_args, i);
+
+      /* Do array-to-pointer, function-to-pointer conversion, and ignore
+        top-level qualifiers as required.  */
+      type = cv_unqualified (type_decays_to (type));
+
       if (default_arg && TREE_CODE (default_arg) == DEFERRED_PARSE)
        {
          /* We've instantiated a template before its default arguments
             have been parsed.  This can happen for a nested template
             class, and is not an error unless we require the default
             argument in a call of this function.  */
-        remaining_arg_types = 
-          tree_cons (default_arg, type, remaining_arg_types);
+         remaining_arg_types
+           = tree_cons (default_arg, type, remaining_arg_types);
          vec_safe_push (DEFPARSE_INSTANTIATIONS (default_arg),
                         remaining_arg_types);
        }
       else
-      remaining_arg_types =
-        hash_tree_cons (default_arg, type, remaining_arg_types);
+       remaining_arg_types
+         = hash_tree_cons (default_arg, type, remaining_arg_types);
     }
 
   return remaining_arg_types;
diff --git a/gcc/testsuite/g++.dg/template/sfinae-dr1227.C 
b/gcc/testsuite/g++.dg/template/sfinae-dr1227.C
new file mode 100644
index 00000000000..821ff0313b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/sfinae-dr1227.C
@@ -0,0 +1,23 @@
+// PR c++/96560
+// DR 1227
+// Test that we substitute function parameter types in lexical order.
+
+template <class T>
+struct A { typedef typename T::type type; }; // { dg-error "void" }
+
+template <class T> void f(typename T::type, typename A<T>::type);
+template <class T> long f(...);
+
+long x = f<int>(0, 0); // { dg-bogus "" } OK
+
+
+template <class T> void g(T, typename A<T>::type);
+template <class T> long g(...);
+
+long y = g<void>(0, 0); // { dg-bogus "" } OK
+
+
+template <class T> void h(typename A<T>::type, T);
+template <class T> long h(...);
+
+long z = h<void>(0, 0); // { dg-message "required from here" } hard error
-- 
2.31.1.362.g311531c9de

Reply via email to