On 2/28/21 12:55 PM, Patrick Palka wrote:
On Fri, 12 Feb 2021, Jason Merrill wrote:

On 2/11/21 5:14 PM, Patrick Palka wrote:
On Thu, 11 Feb 2021, Jason Merrill wrote:

On 2/8/21 2:03 PM, Patrick Palka wrote:
This fixes the way we check satisfaction of constraints on placeholder
types in various contexts, and in particular when the constraint is
dependent.

Firstly, when evaluating the return type requirement of a compound
requirement, we currently substitute the outer template arguments into
the constraint before checking satisfaction. But we should instead be
passing in the complete set of template arguments to satisfaction and
not do a prior separate substitution.  Our current approach leads to us
incorrectly rejecting the testcase concepts-return-req2.C below.

Secondly, when checking the constraints on a placeholder variable or
return type, we don't substitute the template arguments of the enclosing
context at all.  This leads to bogus errors during satisfaction when the
constraint is dependent as in the testcase concepts-placeholder3.C
below.

In order to fix these two issues, we need to be able to properly
normalize the constraints on a placeholder 'auto', which in turn
requires us to know the template parameters that were in-scope where an
'auto' was introduced.  This information currently doesn't seem to be
easily available when we need it, so this patch adds an auxiliary hash
table that keeps track of the value of current_template_parms when each
constrained 'auto' was formed.

This patch also removes some seemingly wrong handling of placeholder
type arguments from tsubst_parameter_mapping.  The code doesn't trigger
with the example used in the comments, because type_uses_auto doesn't
look inside non-deduced contexts such as the operand of decltype.  And
the call to do_auto_deduction seems confused because if 'arg' is a type,
then so is 'parm', and therefore 'init' too is a type, but
do_auto_deduction expects it to be an expression.  Before this patch,
this code was dead (as far as our testsuite can tell), but now it breaks
other parts of this patch, so let's remove it.

gcc/cp/ChangeLog:

        PR c++/96443
        * constraint.cc (type_deducible_p): Don't substitute into the
        constraints, and instead just pass 'args' to do_auto_deduction
        as the outer template arguments.
        (tsubst_parameter_mapping): Remove confused code for handling
        placeholder type arguments.
        (normalize_placeholder_type_constraint): Define.
        (satisfy_constraint_expression): Use it to handle placeholder
        'auto' types.
        * cp-tree.h (get_constrained_auto_context): Declare.
        * pt.c (constrained_auto_context_map): Define.
        (get_placeholder_type_constraint_context): Define.
        (set_placeholder_type_constraints): Define.
        (copy_placeholder_type_constraints): Define.
        (tsubst) <case TEMPLATE_TYPE_PARM>: Use
        copy_placeholder_type_constraints.
        (make_constrained_placeholder_type): Use
        set_placeholder_type_constraints.
        (do_auto_deduction): Clarify comments about the outer_targs
        parameter.  Rework satisfaction of a placeholder type constraint
        to pass in the complete set of template arguments directly to
        constraints_satisfied_p.
        (splice_late_return_type): Use copy_placeholder_type_constraints.

gcc/testsuite/ChangeLog:

        PR c++/96443
        * g++.dg/cpp2a/concepts-placeholder3.C: New test.
        * g++.dg/cpp2a/concepts-return-req2.C: New test.
        * g++.dg/concepts-ts1.C: Add dg-bogus directive to the call to
        f15 that we expect to accept.
---
    gcc/cp/constraint.cc                          | 106
++++++++----------
    gcc/cp/cp-tree.h                              |   1 +
    gcc/cp/pt.c                                   | 101 +++++++++++------
    .../g++.dg/cpp2a/concepts-placeholder3.C      |  19 ++++
    .../g++.dg/cpp2a/concepts-return-req2.C       |  13 +++
    gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C     |   2 +-
    6 files changed, 146 insertions(+), 96 deletions(-)
    create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C
    create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 56134f8b2bf..53588047d44 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2007,39 +2007,19 @@ type_deducible_p (tree expr, tree type, tree
placeholder, tree args,
         references are preserved in the result.  */
      expr = force_paren_expr_uneval (expr);
    -  /* Replace the constraints with the instantiated constraints. This
-     substitutes args into any template parameters in the trailing
-     result type.  */
-  tree saved_constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder);
-  tree subst_constr
-    = tsubst_constraint (saved_constr,
-                        args,
-                        info.complain | tf_partial,
-                        info.in_decl);
-
-  if (subst_constr == error_mark_node)
-    return false;
-
-  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = subst_constr;
-
-  /* Temporarily unlink the canonical type.  */
-  tree saved_type = TYPE_CANONICAL (placeholder);
-  TYPE_CANONICAL (placeholder) = NULL_TREE;
-
-  tree deduced_type
-    = do_auto_deduction (type,
-                        expr,
-                        placeholder,
-                        info.complain,
-                        adc_requirement);
-
-  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr;
-  TYPE_CANONICAL (placeholder) = saved_type;
+  /* When args is empty, we're evaluating a non-templated requires
expression,
+     but even those are parsed under processing_template_decl == 1, and
so
the
+     placeholder 'auto' inside this return-type-requirement has level
2.
In
+     order to have all parms and arguments match up for satisfaction,
we
need
+     to pass a single level as OUTER_TARGS in this case.  */
+  if (!args)
+    args = make_tree_vec (0);
    -  if (deduced_type == error_mark_node)
-    return false;
+  tree deduced_type = do_auto_deduction (type, expr, placeholder,
+                                        info.complain, adc_requirement,
+                                        /*outer_targs=*/args);
    -  return true;
+  return deduced_type != error_mark_node;
    }
      /* True if EXPR can not be converted to TYPE.  */
@@ -2304,35 +2284,10 @@ tsubst_parameter_mapping (tree map, tree args,
subst_info info)
            return error_mark_node;
          tree parm = TREE_VALUE (p);
          tree arg = TREE_PURPOSE (p);
-      tree new_arg = NULL_TREE;
-      if (TYPE_P (arg))
-        {
-          /* If a template parameter is declared with a placeholder, we
can
-             get those in the argument list if decltype is applied to
the
-             placeholder. For example:
-
-               template<auto T>
-                 requires C<decltype(T)>
-               void f() { }
-
-            The normalized argument for C will be an auto type, so we'll
-             need to deduce the actual argument from the corresponding
-             initializer (whatever argument is provided for T), and use
-             that result in the instantiated parameter mapping.  */
-          if (tree auto_node = type_uses_auto (arg))
-            {
-              int level;
-              int index;
-             template_parm_level_and_index (parm, &level, &index);
-             tree init = TMPL_ARG (args, level, index);
-              new_arg = do_auto_deduction (arg, init, auto_node,
-                                          complain, adc_variable_type,
-                                          make_tree_vec (0));
-            }
-        }
-      else if (ARGUMENT_PACK_P (arg))
+      tree new_arg;
+      if (ARGUMENT_PACK_P (arg))
        new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
-      if (!new_arg)
+      else
        {
          new_arg = tsubst_template_arg (arg, args, complain,
in_decl);
          if (TYPE_P (new_arg))
@@ -3038,6 +2993,31 @@ satisfy_associated_constraints (tree t, tree
args,
sat_info info)
      return satisfy_constraint (t, args, info);
    }
    +/* Normalize the constraints on the placeholder 'auto' type T.  */
+
+tree
+normalize_placeholder_type_constraints (tree t, bool diag)
+{
+  gcc_assert (is_auto (t));
+  tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t);
+  if (!constr)
+    return NULL_TREE;
+
+  tree initial_parms = get_constrained_auto_context (t);
+  /* The 'auto' itself is used as the first argument in its own
constraints,
+     and its level is one greater than its template context, so in
order to
+     capture all used template parameters we need to add an extra level
of
+     template parameters to the context; a dummy level suffices.  */
+  initial_parms
+    = tree_cons (size_int (initial_parms
+                          ? TMPL_PARMS_DEPTH (initial_parms) + 1 : 1),
+                make_tree_vec (0), initial_parms);
+
+  norm_info info (diag ? tf_norm : tf_none);
+  info.initial_parms = initial_parms;
+  return normalize_constraint_expression (constr, info);
+}
+
    /* Evaluate EXPR as a constraint expression using ARGS, returning a
       satisfaction value. */
    @@ -3047,8 +3027,6 @@ satisfy_constraint_expression (tree t, tree
args,
sat_info info)
      if (t == error_mark_node)
        return error_mark_node;
    -  gcc_assert (EXPR_P (t));
-
      /* Get the normalized constraints.  */
      tree norm;
      if (args == NULL_TREE && concept_check_p (t))
@@ -3058,8 +3036,12 @@ satisfy_constraint_expression (tree t, tree args,
sat_info info)
          tree tmpl = get_concept_check_template (id);
          norm = normalize_concept_definition (tmpl, info.noisy ());
        }
-  else
+  else if (EXPR_P (t))
        norm = normalize_constraint_expression (t, info.noisy ());
+  else if (is_auto (t))
+    norm = normalize_placeholder_type_constraints (t, info.noisy ());
+  else
+    gcc_unreachable ();
        /* Perform satisfaction.  */
      return satisfy_constraint (norm, args, info);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 26fbf1eb663..ca5550ccb6d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7090,6 +7090,7 @@ extern tree make_auto
(void);
    extern tree make_decltype_auto                      (void);
    extern tree make_constrained_auto           (tree, tree);
    extern tree make_constrained_decltype_auto  (tree, tree);
+extern tree get_constrained_auto_context       (tree);
    extern tree make_template_placeholder               (tree);
    extern bool template_placeholder_p          (tree);
    extern bool ctad_template_p                 (tree);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index bceb942e79a..bcf9c9a3d9a 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15364,6 +15364,46 @@ tsubst_tree_list (tree t, tree args,
tsubst_flags_t
complain, tree in_decl)
      return chain;
    }
    +/* A hash table mapping a constrained 'auto' to the set of in-scope
+   template parameters from where the 'auto' was introduced.  */
+
+static GTY((cache)) decl_tree_cache_map *constrained_auto_context_map;

How about turning PLACEHOLDER_TYPE_CONSTRAINTS into a TREE_LIST instead of
introducing a new hash table?

Nice, this cleaner approach works well, and was quite straightforward to
implement.  How does this look?  Tested on x86_64-pc-linux-gnu,
range-v3  and cmcstl2.

Looks good.

Here's v3 of this patch, which correctly addresses the FIXME in
splice_late_return_type added by r11-7409, and also adds a testcase
abbrev9.C which verifies this.

 From the r11-7409 thread:
+       /* FIXME: We should also rebuild the constraint to refer to the new
+          auto.  */
+       PLACEHOLDER_TYPE_CONSTRAINTS (new_auto)
+         = PLACEHOLDER_TYPE_CONSTRAINTS (auto_node);
+       TYPE_CANONICAL (new_auto) = canonical_type_parameter (new_auto);

For the FIXME it would be nice to adjust
make_constrained_auto/_placeholder_type to handle this for you.

Hmm, I tried, but I couldn't see a good way to adjust these functions to
handle rebuilding the constraint for us.

Ah, well.  The patch is OK.

-- >8 --

Subject: [PATCH 4/6] c++: Fix satisfaction of placeholder type constraints
  [PR96443]

This fixes the way we check satisfaction of constraints on placeholder
types in various deduction contexts, and in particular when the
constraint is dependent.

Firstly, when evaluating the return type requirement of a compound
requirement, we currently substitute the outer template arguments into
the constraint before checking satisfaction. But we should instead be
passing in the complete set of template arguments to satisfaction and
not do a prior separate substitution.  Our current approach leads to us
incorrectly rejecting the testcase concepts-return-req2.C below.

Secondly, when checking the constraints on a placeholder variable or
return type, we don't consider the template arguments of the enclosing
context at all.  This leads to bogus errors during satisfaction when the
constraint is dependent as in the testcase concepts-placeholder3.C
below.

In order to fix these two issues, we need to be able to normalize the
constraints on a placeholder 'auto', which in turn requires us to know
the template parameters that were in scope where the 'auto' was
introduced.  This information currently doesn't seem to be easily
available when we need it, so this patch turns PLACEHOLDER_TYPE_CONSTRAINTS
into a TREE_LIST whose TREE_PURPOSE additionally holds the value of
current_template_parms whence a constrained 'auto' was formed.

This patch also removes some seemingly wrong handling of placeholder
type arguments from tsubst_parameter_mapping.  The code doesn't trigger
with the example used in the comments, because type_uses_auto doesn't
look inside non-deduced contexts such as the operand of decltype.  And
the call to do_auto_deduction seems confused because if 'arg' is a type,
then so is 'parm', and therefore 'init' too is a type, but
do_auto_deduction expects it to be an expression.  Before this patch,
this code was dead (as far as our testsuite can tell), but now it breaks
other parts of this patch, so let's remove it.

gcc/cp/ChangeLog:

        PR c++/96443
        PR c++/96960
        * constraint.cc (type_deducible_p): Don't substitute into the
        constraints, and instead just pass 'args' to do_auto_deduction
        as the outer template arguments.
        (tsubst_parameter_mapping): Remove confused code for handling
        placeholder type arguments.
        (normalize_placeholder_type_constraint): Define.
        (satisfy_constraint_expression): Use it to handle placeholder
        'auto' types.
        * cp-tree.h (PLACEHOLDER_TYPE_CONSTRAINTS_INFO): Define.
        (PLACEHOLDER_TYPE_CONSTRAINTS): Redefine in terms of the above.
        * pt.c (tsubst) <case TEMPLATE_TYPE_PARM>: Use
        PLACEHOLDER_TYPE_CONSTRAINTS_INFO instead.
        (make_constrained_placeholder_type): Set
        PLACEHOLDER_TYPE_CONSTRAINTS_INFO instead.
        (do_auto_deduction): Clarify comments about the outer_targs
        parameter.  Rework satisfaction of a placeholder type constraint
        to pass in the complete set of template arguments directly to
        constraints_satisfied_p.
        (splice_late_return_type): Use PLACEHOLDER_TYPE_CONSTRAINTS_INFO
        instead.  Also rebuild the the constraint info on the new auto.

gcc/testsuite/ChangeLog:

        PR c++/96443
        PR c++/96960
        * g++.dg/concepts/abbrev9.C: New test.
        * g++.dg/cpp2a/concepts-lambda15.C: New test.
        * g++.dg/cpp2a/concepts-placeholder3.C: New test.
        * g++.dg/cpp2a/concepts-return-req2.C: New test.
        * g++.dg/concepts-ts1.C: Add dg-bogus directive to the call to
        f15 that we expect to accept.
---
  gcc/cp/constraint.cc                          | 111 ++++++++----------
  gcc/cp/cp-tree.h                              |  16 ++-
  gcc/cp/pt.c                                   |  73 ++++++------
  gcc/testsuite/g++.dg/concepts/abbrev9.C       |  26 ++++
  .../g++.dg/cpp2a/concepts-lambda15.C          |  16 +++
  .../g++.dg/cpp2a/concepts-placeholder3.C      |  19 +++
  .../g++.dg/cpp2a/concepts-return-req2.C       |  13 ++
  gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C     |   2 +-
  8 files changed, 172 insertions(+), 104 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/concepts/abbrev9.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda15.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index fcb249a642f..2b61ad8d9ea 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1989,39 +1989,19 @@ type_deducible_p (tree expr, tree type, tree 
placeholder, tree args,
       references are preserved in the result.  */
    expr = force_paren_expr_uneval (expr);
- /* Replace the constraints with the instantiated constraints. This
-     substitutes args into any template parameters in the trailing
-     result type.  */
-  tree saved_constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder);
-  tree subst_constr
-    = tsubst_constraint (saved_constr,
-                        args,
-                        info.complain | tf_partial,
-                        info.in_decl);
-
-  if (subst_constr == error_mark_node)
-    return false;
-
-  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = subst_constr;
-
-  /* Temporarily unlink the canonical type.  */
-  tree saved_type = TYPE_CANONICAL (placeholder);
-  TYPE_CANONICAL (placeholder) = NULL_TREE;
-
-  tree deduced_type
-    = do_auto_deduction (type,
-                        expr,
-                        placeholder,
-                        info.complain,
-                        adc_requirement);
-
-  PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr;
-  TYPE_CANONICAL (placeholder) = saved_type;
+  /* When args is empty, we're evaluating a non-templated requires expression,
+     but even those are parsed under processing_template_decl == 1, and so the
+     placeholder 'auto' inside this return-type-requirement has level 2.  In
+     order to have all parms and arguments match up for satisfaction, we need
+     to pass a single level of OUTER_TARGS in this case.  */
+  if (!args)
+    args = make_tree_vec (0);
- if (deduced_type == error_mark_node)
-    return false;
+  tree deduced_type = do_auto_deduction (type, expr, placeholder,
+                                        info.complain, adc_requirement,
+                                        /*outer_targs=*/args);
- return true;
+  return deduced_type != error_mark_node;
  }
/* True if EXPR can not be converted to TYPE. */
@@ -2286,35 +2266,10 @@ tsubst_parameter_mapping (tree map, tree args, 
subst_info info)
          return error_mark_node;
        tree parm = TREE_VALUE (p);
        tree arg = TREE_PURPOSE (p);
-      tree new_arg = NULL_TREE;
-      if (TYPE_P (arg))
-        {
-          /* If a template parameter is declared with a placeholder, we can
-             get those in the argument list if decltype is applied to the
-             placeholder. For example:
-
-               template<auto T>
-                 requires C<decltype(T)>
-               void f() { }
-
-            The normalized argument for C will be an auto type, so we'll
-             need to deduce the actual argument from the corresponding
-             initializer (whatever argument is provided for T), and use
-             that result in the instantiated parameter mapping.  */
-          if (tree auto_node = type_uses_auto (arg))
-            {
-              int level;
-              int index;
-             template_parm_level_and_index (parm, &level, &index);
-             tree init = TMPL_ARG (args, level, index);
-              new_arg = do_auto_deduction (arg, init, auto_node,
-                                          complain, adc_variable_type,
-                                          make_tree_vec (0));
-            }
-        }
-      else if (ARGUMENT_PACK_P (arg))
+      tree new_arg;
+      if (ARGUMENT_PACK_P (arg))
        new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
-      if (!new_arg)
+      else
        {
          new_arg = tsubst_template_arg (arg, args, complain, in_decl);
          if (TYPE_P (new_arg))
@@ -3020,6 +2975,36 @@ satisfy_associated_constraints (tree t, tree args, 
sat_info info)
    return satisfy_constraint (t, args, info);
  }
+/* Return the normal form of the constraints on the placeholder 'auto'
+   type T.  */
+
+static tree
+normalize_placeholder_type_constraints (tree t, bool diag)
+{
+  gcc_assert (is_auto (t));
+  tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t);
+  if (!ci)
+    return NULL_TREE;
+
+  tree constr = TREE_VALUE (ci);
+  /* The TREE_PURPOSE contains the set of template parameters that were in
+     scope for this placeholder type; use them as the initial template
+     parameters for normalization.  */
+  tree initial_parms = TREE_PURPOSE (ci);
+  /* The 'auto' itself is used as the first argument in its own constraints,
+     and its level is one greater than its template depth.  So in order to
+     capture all used template parameters, we need to add an extra level of
+     template parameters to the context; a dummy level suffices.  */
+  initial_parms
+    = tree_cons (size_int (initial_parms
+                          ? TMPL_PARMS_DEPTH (initial_parms) + 1 : 1),
+                make_tree_vec (0), initial_parms);
+
+  norm_info info (diag ? tf_norm : tf_none);
+  info.initial_parms = initial_parms;
+  return normalize_constraint_expression (constr, info);
+}
+
  /* Evaluate EXPR as a constraint expression using ARGS, returning a
     satisfaction value. */
@@ -3029,8 +3014,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info)
    if (t == error_mark_node)
      return error_mark_node;
- gcc_assert (EXPR_P (t));
-
    /* Get the normalized constraints.  */
    tree norm;
    if (args == NULL_TREE && concept_check_p (t))
@@ -3054,6 +3037,12 @@ satisfy_constraint_expression (tree t, tree args, 
sat_info info)
        norm_info ninfo (info.noisy () ? tf_norm : tf_none);
        norm = normalize_constraint_expression (t, ninfo);
      }
+  else if (is_auto (t))
+    {
+      norm = normalize_placeholder_type_constraints (t, info.noisy ());
+      if (!norm)
+       return boolean_true_node;
+    }
    else
      gcc_unreachable ();
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 38b31e3908f..544e99538a4 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1578,11 +1578,19 @@ check_constraint_info (tree t)
  #define COMPOUND_REQ_NOEXCEPT_P(NODE) \
    TREE_LANG_FLAG_0 (TREE_CHECK (NODE, COMPOUND_REQ))
-/* The constraints on an 'auto' placeholder type, used in an argument deduction
-   constraint.  */
-#define PLACEHOLDER_TYPE_CONSTRAINTS(NODE) \
+/* A TREE_LIST whose TREE_VALUE is the constraints on the 'auto' placeholder
+   type NODE, used in an argument deduction constraint.  The TREE_PURPOSE
+   holds the set of template parameters that were in-scope when this 'auto'
+   was formed.  */
+#define PLACEHOLDER_TYPE_CONSTRAINTS_INFO(NODE) \
    DECL_SIZE_UNIT (TYPE_NAME (NODE))
+/* The constraints on the 'auto' placeholder type NODE. */
+#define PLACEHOLDER_TYPE_CONSTRAINTS(NODE)                \
+  (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (NODE)               \
+   ? TREE_VALUE (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (NODE)) \
+   : NULL_TREE)
+
  /* True if NODE is a constraint.  */
  #define CONSTR_P(NODE)                  \
    (TREE_CODE (NODE) == ATOMIC_CONSTR    \
@@ -8420,7 +8428,7 @@ set_implicit_rvalue_p (tree ot)
  inline bool
  is_constrained_auto (const_tree t)
  {
-  return is_auto (t) && PLACEHOLDER_TYPE_CONSTRAINTS (t);
+  return is_auto (t) && PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t);
  }
/* RAII class to push/pop class scope T; if T is not a class, do nothing. */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f324f6a1e1b..0c7fd496935 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15710,15 +15710,15 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
                               ? tf_ignore_bad_quals : 0));
              }
            else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM
-                    && PLACEHOLDER_TYPE_CONSTRAINTS (t)
+                    && PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)
                     && (r = (TEMPLATE_PARM_DESCENDANTS
                              (TEMPLATE_TYPE_PARM_INDEX (t))))
                     && (r = TREE_TYPE (r))
-                    && !PLACEHOLDER_TYPE_CONSTRAINTS (r))
+                    && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r))
              /* Break infinite recursion when substituting the constraints
                 of a constrained placeholder.  */;
            else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM
-                    && !PLACEHOLDER_TYPE_CONSTRAINTS (t)
+                    && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)
                     && !CLASS_PLACEHOLDER_TEMPLATE (t)
                     && (arg = TEMPLATE_TYPE_PARM_INDEX (t),
                         r = TEMPLATE_PARM_DESCENDANTS (arg))
@@ -15741,8 +15741,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
                  {
                    /* Propagate constraints on placeholders since they are
                       only instantiated during satisfaction.  */
-                   if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t))
-                     PLACEHOLDER_TYPE_CONSTRAINTS (r) = constr;
+                   if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t))
+                     PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r) = ci;
                    else if (tree pl = CLASS_PLACEHOLDER_TEMPLATE (t))
                      {
                        pl = tsubst_copy (pl, args, complain, in_decl);
@@ -28183,7 +28183,8 @@ make_constrained_placeholder_type (tree type, tree con, 
tree args)
    expr = build_concept_check (expr, type, args, tf_warning_or_error);
    --processing_template_decl;
- PLACEHOLDER_TYPE_CONSTRAINTS (type) = expr;
+  PLACEHOLDER_TYPE_CONSTRAINTS_INFO (type)
+    = build_tree_list (current_template_parms, expr);
/* Our canonical type depends on the constraint. */
    TYPE_CANONICAL (type) = canonical_type_parameter (type);
@@ -29423,9 +29424,11 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
     from INIT.  AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE.
     The CONTEXT determines the context in which auto deduction is performed
     and is used to control error diagnostics.  FLAGS are the LOOKUP_* flags.
-   OUTER_TARGS are used during template argument deduction
-   (context == adc_unify) to properly substitute the result, and is ignored
-   in other contexts.
+
+   OUTER_TARGS is used during template argument deduction (context == 
adc_unify)
+   to properly substitute the result.  It's also used in the adc_unify and
+   adc_requirement contexts to communicate the the necessary template arguments
+   to satisfaction.  OUTER_TARGS is ignored in other contexts.
For partial-concept-ids, extra args may be appended to the list of deduced
     template arguments prior to determining constraint satisfaction.  */
@@ -29586,30 +29589,21 @@ do_auto_deduction (tree type, tree init, tree 
auto_node,
      }
/* Check any placeholder constraints against the deduced type. */
-  if (flag_concepts && !processing_template_decl)
-    if (tree check = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
+  if (flag_concepts)
+    if (NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
        {
-        /* Use the deduced type to check the associated constraints. If we
-           have a partial-concept-id, rebuild the argument list so that
-           we check using the extra arguments. */
-       check = unpack_concept_check (check);
-       gcc_assert (TREE_CODE (check) == TEMPLATE_ID_EXPR);
-       tree cdecl = TREE_OPERAND (check, 0);
-       if (OVL_P (cdecl))
-         cdecl = OVL_FIRST (cdecl);
-        tree cargs = TREE_OPERAND (check, 1);
-        if (TREE_VEC_LENGTH (cargs) > 1)
-          {
-            cargs = copy_node (cargs);
-            TREE_VEC_ELT (cargs, 0) = TREE_VEC_ELT (targs, 0);
-          }
-        else
-          cargs = targs;
+       if (processing_template_decl)
+         /* In general we can't check satisfaction until we know all
+            template arguments.  */
+         return type;
- /* Rebuild the check using the deduced arguments. */
-       check = build_concept_check (cdecl, cargs, tf_none);
+       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 (!constraints_satisfied_p (check))
+       tree complete_targs = add_to_template_args (outer_targs, targs);
+       if (!constraints_satisfied_p (auto_node, complete_targs))
            {
              if (complain & tf_warning_or_error)
                {
@@ -29634,15 +29628,16 @@ do_auto_deduction (tree type, tree init, tree 
auto_node,
                             "placeholder constraints");
                      break;
                    }
-               diagnose_constraints (input_location, check, targs);
+               diagnose_constraints (input_location, auto_node, 
complete_targs);
                }
              return error_mark_node;
            }
        }
- if (processing_template_decl && context != adc_unify)
-    outer_targs = current_template_args ();
-  targs = add_to_template_args (outer_targs, targs);
+  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);
    return tsubst (type, targs, complain, NULL_TREE);
  }
@@ -29669,10 +29664,12 @@ splice_late_return_type (tree type, tree late_return_type)
        TREE_VEC_ELT (auto_vec, 0) = new_auto;
        tree targs = add_outermost_template_args (current_template_args (),
                                                  auto_vec);
-       /* FIXME: We should also rebuild the constraint to refer to the new
-          auto.  */
-       PLACEHOLDER_TYPE_CONSTRAINTS (new_auto)
-         = PLACEHOLDER_TYPE_CONSTRAINTS (auto_node);
+       /* Also rebuild the constraint info in terms of the new auto.  */
+       if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (auto_node))
+         PLACEHOLDER_TYPE_CONSTRAINTS_INFO (new_auto)
+           = build_tree_list (current_template_parms,
+                              tsubst_constraint (TREE_VALUE (ci), targs,
+                                                 tf_none, NULL_TREE));
        TYPE_CANONICAL (new_auto) = canonical_type_parameter (new_auto);
        return tsubst (type, targs, tf_none, NULL_TREE);
        }
diff --git a/gcc/testsuite/g++.dg/concepts/abbrev9.C 
b/gcc/testsuite/g++.dg/concepts/abbrev9.C
new file mode 100644
index 00000000000..865b44c6a63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/abbrev9.C
@@ -0,0 +1,26 @@
+// { dg-do compile { target concepts } }
+
+template <class T, class U> concept same_as = __is_same(T, U);
+
+same_as<int> auto f(auto, auto y) {
+  return y; // { dg-error "deduced return type" }
+}
+
+template <class>
+struct A {
+  static auto g(auto x, auto y) -> same_as<decltype(x)> auto {
+    return y; // { dg-error "deduced return type" }
+  }
+};
+
+int main() {
+  f(0, 0);   // { dg-bogus "" }
+  f("", 0);  // { dg-bogus "" }
+  f(0, "");  // { dg-message "required from here" }
+  f("", ""); // { dg-message "required from here" }
+
+  A<void>::g(0, 0);   // { dg-bogus "" }
+  A<void>::g("", 0);  // { dg-message "required from here" }
+  A<void>::g(0, "");  // { dg-message "required from here" }
+  A<void>::g("", ""); // { dg-bogus "" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda15.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda15.C
new file mode 100644
index 00000000000..29df5d0b1ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda15.C
@@ -0,0 +1,16 @@
+// PR c++/96960
+// { dg-do compile { target c++20 } }
+
+template <class, class> concept C0 = true;
+
+template <class T>
+concept C = requires(T t) {
+  { 42 } -> C0<char [([] { return 42; }())]>;
+};
+
+static_assert(C<int>);
+
+C0<char [([] { return 42; }())]> auto x = 42;
+
+int f(C0<char [([] { return 42; }())]> auto x);
+int y = f(42);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C
new file mode 100644
index 00000000000..87e3c093e28
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder3.C
@@ -0,0 +1,19 @@
+// PR c++/96443
+// { dg-do compile { target c++20 } }
+
+template <class T, class U> concept same_as = __is_same(T, U);
+
+auto f(auto x) -> same_as<decltype(x)> auto { return 0; }; // { dg-error 
"constraints" }
+void g(auto x) { same_as<decltype(x)> auto y = 0; } // { dg-error 
"constraints" }
+auto h(auto x) -> same_as<decltype(x.missing)> auto { return 0; } // { dg-error 
"constraints|missing" }
+template <class T, same_as<T> auto N> void i() {}
+
+int main() {
+  f(0); // { dg-bogus "" }
+  f(true); // { dg-message "required from here" }
+  g(0); // { dg-bogus "" }
+  g(true); // { dg-message "required from here" }
+  h(0); // { dg-message "required from here" }
+  i<int, 0>(); // { dg-bogus "" }
+  i<int, true>(); // { dg-error "no match|constraints" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C
new file mode 100644
index 00000000000..77208bb7069
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-return-req2.C
@@ -0,0 +1,13 @@
+// Verify we check return-type-requirements by passing the entire set of
+// template arguments to normalization rather than first substituting into
+// the constraint.  The latter approach would induce a substitution failure and
+// cause the requires-expression to evaluate to false here.
+// { dg-do compile { target c++20 } }
+
+template <class, class>
+concept C1 = true;
+
+template <class T>
+concept C2 = requires { { 0 } -> C1<typename T::type>; };
+
+static_assert(C2<int>);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C
index 1cefe3b243f..a116cac4ea4 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ts1.C
@@ -40,7 +40,7 @@ void driver()
    f3('a'); // { dg-error "" }
    f4(0, 0);
    f4(0, 'a'); // { dg-error "" }
-  f15(0);
+  f15(0); // { dg-bogus "" }
    f15('a'); // { dg-message "" }
  }

Reply via email to