diff mbox series

[5/6] c++: Clean up normalization / satisfaction routines

Message ID 20210228175838.1286138-1-ppalka@redhat.com
State New
Headers show
Series [1/4] c++: Avoid building garbage trees from tsubst_requires_expr | expand

Commit Message

Patrick Palka Feb. 28, 2021, 5:58 p.m. UTC
This patch mostly performs some straightforward refactoring:

  - Renamed satisfy_constraint to satisfy_normalized_constraints
  - Renamed the three-parameter version of satisfy_constraint_expression
    to satisfy_nondeclaration_constraints
  - Removed normalize_(non)?template_requirements
  - Removed satisfy_associated_constraints (and made its callers
    check for dependent template args sooner, before normalization)
  - Removed the tsubst_flags_t parameter of evaluate_concept_check
  - Combined the two versions of constraint_satisfaction_value
  - Combined the two versions of constraint_satisfied_p

Additionally, this patch removes the handling of bare
constraint-expressions from satisfy_nondeclaration_constraints, and
hence constraints_satisfied_p and constraint_satisfaction_value now only
take things that carry their own template information needed for
normalization.  In practice, this only means it's no longer possible to
evaluate bare REQUIRES_EXPRs via the satisfaction routines, and so this
patch adjusts the affected callers to instead use tsubst_requires_expr.
For convenience, the function diagnose_constraints continues to accept
REQUIRES_EXPRs, but it now handles them by calling diagnose_require_expr
directly.

(That we used to evaluate REQUIRES_EXPR via satisfaction might even be a
correctness issue: since we cache satisfaction in special ways that don't
apply to regular evaluation, going through satisfaction could in theory
cause us to reuse a cached value for a REQUIRES_EXPR when we shouldn't
have.)

gcc/cp/ChangeLog:

	* constexpr.c (cxx_eval_call_expression): Adjust call to
	evaluate_concept_check.
	(cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use
	tsubst_requires_expr instead of satisfy_constraint_expression.
	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
	* constraint.cc (struct sat_info): Adjust comment about which
	satisfaction entrypoints use noisy-unsat.
	(normalize_template_requirements): Remove (and adjust callers
	appropriately).
	(normalize_nontemplate_requirements): Likewise.
	(tsubst_nested_requirement): Use constraint_satisfaction_value
	instead of satisfy_constraint_expression, which'll do the
	noisy replaying of ill-formed quiet satisfaction for us.
	(decl_satisfied_cache): Adjust comment.
	(satisfy_constraint): Rename to ...
	(satisfy_normalized_constraints): ... this.
	(satisfy_associated_constraints): Remove (and make its
	callers check for dependent arguments).
	(satisfy_constraint_expression): Rename to ...
	(satisfy_nondeclaration_constraints): ... this.  Assert that
	'args' is empty when 't' is a concept-id.  Removing handling
	bare constraint-expressions.  Adjust comment accordingly.
	(satisfy_declaration_constraints): Assert in the two-parameter
	version that 't' is not a TEMPLATE_DECL.  Adjust following
	removal of normalize_(non)?template_requirements and
	satisfy_asociated_constraints.
	(constraint_satisfaction_value): Combine the two- and
	three-parameter versions in the natural way.
	(constraints_satisfied_p): Combine the one- and two-parameter
	versions in the natural way.  Improve documentation.
	(evaluate_concept_check): Remove 'complain' parameter.  Use
	constraint_satisfaction_value instead of
	satisfy_constraint_expression.
	(diagnose_nested_requirement): Adjust following renaming of
	satisfy_constraint_expression.
	(diagnose_constraints): Handle REQUIRES_EXPR by going through
	diagnose_requires_expr directly instead of treating it as a
	constraint-expression.  Improve documentation.
	* cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call
	to evaluate_concept_check.
	<case REQUIRES_EXPR>: Use tsubst_requires_expr instead of
	constraints_satisfied_p.
	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
	* cp-tree.h (evaluate_concept_check): Remove tsubst_flag_t
	parameter.
	(satisfy_constraint_expression): Remove declaration.
	(constraints_satisfied_p): Remove one-parameter declaration.
	Add a default argument to the two-parameter declaration.
	* cvt.c (convert_to_void): Adjust call to
	evaluate_concept_check.
---
 gcc/cp/constexpr.c   |   6 +-
 gcc/cp/constraint.cc | 210 ++++++++++++++++---------------------------
 gcc/cp/cp-gimplify.c |   7 +-
 gcc/cp/cp-tree.h     |   6 +-
 gcc/cp/cvt.c         |   2 +-
 5 files changed, 85 insertions(+), 146 deletions(-)

Comments

Jason Merrill March 1, 2021, 10:50 p.m. UTC | #1
On 2/28/21 12:58 PM, Patrick Palka wrote:
> This patch mostly performs some straightforward refactoring:
> 
>    - Renamed satisfy_constraint to satisfy_normalized_constraints
>    - Renamed the three-parameter version of satisfy_constraint_expression
>      to satisfy_nondeclaration_constraints
>    - Removed normalize_(non)?template_requirements
>    - Removed satisfy_associated_constraints (and made its callers
>      check for dependent template args sooner, before normalization)
>    - Removed the tsubst_flags_t parameter of evaluate_concept_check
>    - Combined the two versions of constraint_satisfaction_value
>    - Combined the two versions of constraint_satisfied_p
> 
> Additionally, this patch removes the handling of bare
> constraint-expressions from satisfy_nondeclaration_constraints, and
> hence constraints_satisfied_p and constraint_satisfaction_value now only
> take things that carry their own template information needed for
> normalization.  In practice, this only means it's no longer possible to
> evaluate bare REQUIRES_EXPRs via the satisfaction routines, and so this
> patch adjusts the affected callers to instead use tsubst_requires_expr.

It's probably better to have a different entry point than 
tsubst_requires_expr for callers that have nothing to do with templates. 
  Whether that's a one-line wrapper for the call to tsubst_requires_expr 
("evaluate_requires_expr"?) or calling tsubst_requires_expr from 
satisfy_nondeclaration_constraints, I don't have an opinion either way.

> For convenience, the function diagnose_constraints continues to accept
> REQUIRES_EXPRs, but it now handles them by calling diagnose_require_expr
> directly.

This might argue for the latter choice above.

> (That we used to evaluate REQUIRES_EXPR via satisfaction might even be a
> correctness issue: since we cache satisfaction in special ways that don't
> apply to regular evaluation, going through satisfaction could in theory
> cause us to reuse a cached value for a REQUIRES_EXPR when we shouldn't
> have.)

> gcc/cp/ChangeLog:
> 
> 	* constexpr.c (cxx_eval_call_expression): Adjust call to
> 	evaluate_concept_check.
> 	(cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use
> 	tsubst_requires_expr instead of satisfy_constraint_expression.
> 	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
> 	* constraint.cc (struct sat_info): Adjust comment about which
> 	satisfaction entrypoints use noisy-unsat.
> 	(normalize_template_requirements): Remove (and adjust callers
> 	appropriately).
> 	(normalize_nontemplate_requirements): Likewise.
> 	(tsubst_nested_requirement): Use constraint_satisfaction_value
> 	instead of satisfy_constraint_expression, which'll do the
> 	noisy replaying of ill-formed quiet satisfaction for us.
> 	(decl_satisfied_cache): Adjust comment.
> 	(satisfy_constraint): Rename to ...
> 	(satisfy_normalized_constraints): ... this.
> 	(satisfy_associated_constraints): Remove (and make its
> 	callers check for dependent arguments).
> 	(satisfy_constraint_expression): Rename to ...
> 	(satisfy_nondeclaration_constraints): ... this.  Assert that
> 	'args' is empty when 't' is a concept-id.  Removing handling
> 	bare constraint-expressions.  Adjust comment accordingly.
> 	(satisfy_declaration_constraints): Assert in the two-parameter
> 	version that 't' is not a TEMPLATE_DECL.  Adjust following
> 	removal of normalize_(non)?template_requirements and
> 	satisfy_asociated_constraints.
> 	(constraint_satisfaction_value): Combine the two- and
> 	three-parameter versions in the natural way.
> 	(constraints_satisfied_p): Combine the one- and two-parameter
> 	versions in the natural way.  Improve documentation.
> 	(evaluate_concept_check): Remove 'complain' parameter.  Use
> 	constraint_satisfaction_value instead of
> 	satisfy_constraint_expression.
> 	(diagnose_nested_requirement): Adjust following renaming of
> 	satisfy_constraint_expression.
> 	(diagnose_constraints): Handle REQUIRES_EXPR by going through
> 	diagnose_requires_expr directly instead of treating it as a
> 	constraint-expression.  Improve documentation.
> 	* cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call
> 	to evaluate_concept_check.
> 	<case REQUIRES_EXPR>: Use tsubst_requires_expr instead of
> 	constraints_satisfied_p.
> 	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
> 	* cp-tree.h (evaluate_concept_check): Remove tsubst_flag_t
> 	parameter.
> 	(satisfy_constraint_expression): Remove declaration.
> 	(constraints_satisfied_p): Remove one-parameter declaration.
> 	Add a default argument to the two-parameter declaration.
> 	* cvt.c (convert_to_void): Adjust call to
> 	evaluate_concept_check.
> ---
>   gcc/cp/constexpr.c   |   6 +-
>   gcc/cp/constraint.cc | 210 ++++++++++++++++---------------------------
>   gcc/cp/cp-gimplify.c |   7 +-
>   gcc/cp/cp-tree.h     |   6 +-
>   gcc/cp/cvt.c         |   2 +-
>   5 files changed, 85 insertions(+), 146 deletions(-)
> 
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index cd0a68e9fd6..f940e3e5985 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -2257,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
>   {
>     /* Handle concept checks separately.  */
>     if (concept_check_p (t))
> -    return evaluate_concept_check (t, tf_warning_or_error);
> +    return evaluate_concept_check (t);
>   
>     location_t loc = cp_expr_loc_or_input_loc (t);
>     tree fun = get_function_named_in_call (t);
> @@ -6905,7 +6905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>            '!requires (T t) { ... }' which is not transformed into
>            a constraint.  */
>         if (!processing_template_decl)
> -        return satisfy_constraint_expression (t);
> +	return tsubst_requires_expr (t, NULL_TREE, tf_none, NULL_TREE);
>         else
>           *non_constant_p = true;
>         return t;
> @@ -6941,7 +6941,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>   
>   	if (!processing_template_decl
>   	    && !uid_sensitive_constexpr_evaluation_p ())
> -	  r = evaluate_concept_check (t, tf_warning_or_error);
> +	  r = evaluate_concept_check (t);
>   	else
>   	  *non_constant_p = true;
>   
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 2b61ad8d9ea..cf319b34da0 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -107,10 +107,9 @@ struct subst_info
>      a constraint is not satisfied.
>   
>      The entrypoints to satisfaction for which we set noisy+unsat are
> -   diagnose_constraints and diagnose_nested_requirement.  The entrypoints for
> -   which we set noisy-unsat are the replays inside constraint_satisfaction_value,
> -   evaluate_concept_check and tsubst_nested_requirement.  In other entrypoints,
> -   e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags
> +   diagnose_constraints and diagnose_nested_requirement.  The entrypoint for
> +   which we set noisy-unsat is the replay inside constraint_satisfaction_value.
> +   From constraints_satisfied_p, we enter satisfaction quietly (both flags
>      cleared).  */
>   
>   struct sat_info : subst_info
> @@ -133,7 +132,7 @@ struct sat_info : subst_info
>     bool diagnose_unsatisfaction;
>   };
>   
> -static tree satisfy_constraint_expression (tree, tree, sat_info);
> +static tree constraint_satisfaction_value (tree, tree, sat_info);
>   
>   /* True if T is known to be some type other than bool. Note that this
>      is false for dependent types and errors.  */
> @@ -958,22 +957,6 @@ normalize_concept_definition (tree tmpl, bool diag = false)
>     return norm;
>   }
>   
> -/* Returns the normal form of TMPL's requirements.  */
> -
> -static tree
> -normalize_template_requirements (tree tmpl, bool diag = false)
> -{
> -  return get_normalized_constraints_from_decl (tmpl, diag);
> -}
> -
> -/* Returns the normal form of TMPL's requirements.  */
> -
> -static tree
> -normalize_nontemplate_requirements (tree decl, bool diag = false)
> -{
> -  return get_normalized_constraints_from_decl (decl, diag);
> -}
> -
>   /* Normalize an EXPR as a constraint.  */
>   
>   static tree
> @@ -2068,15 +2051,8 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
>   static tree
>   tsubst_nested_requirement (tree t, tree args, subst_info info)
>   {
> -  /* Perform satisfaction quietly first.  */
>     sat_info quiet (tf_none, info.in_decl);
> -  tree result = satisfy_constraint_expression (t, args, quiet);
> -  if (result == error_mark_node)
> -    {
> -      /* Replay the error.  */
> -      sat_info noisy (tf_warning_or_error, info.in_decl);
> -      satisfy_constraint_expression (t, args, noisy);
> -    }
> +  tree result = constraint_satisfaction_value (t, args, quiet);
>     if (result != boolean_true_node)
>       return error_mark_node;
>     return boolean_true_node;
> @@ -2459,7 +2435,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
>   /* Cache the result of satisfy_atom.  */
>   static GTY((deletable)) hash_table<sat_hasher> *sat_cache;
>   
> -/* Cache the result of constraint_satisfaction_value.  */
> +/* Cache the result of satisfy_declaration_constraints.  */
>   static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache;
>   
>   /* A tool used by satisfy_atom to help manage satisfaction caching and to
> @@ -2941,7 +2917,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info)
>   /* Check that the normalized constraint T is satisfied for ARGS.  */
>   
>   static tree
> -satisfy_constraint (tree t, tree args, sat_info info)
> +satisfy_normalized_constraints (tree t, tree args, sat_info info)
>   {
>     auto_timevar time (TV_CONSTRAINT_SAT);
>   
> @@ -2957,24 +2933,6 @@ satisfy_constraint (tree t, tree args, sat_info info)
>     return satisfy_constraint_r (t, args, info);
>   }
>   
> -/* Check the normalized constraints T against ARGS, returning a satisfaction
> -   value (either true, false, or error).  */
> -
> -static tree
> -satisfy_associated_constraints (tree t, tree args, sat_info info)
> -{
> -  /* If there are no constraints then this is trivially satisfied.  */
> -  if (!t)
> -    return boolean_true_node;
> -
> -  /* If any arguments depend on template parameters, we can't
> -     check constraints. Pretend they're satisfied for now.  */
> -  if (args && uses_template_parms (args))
> -    return boolean_true_node;
> -
> -  return satisfy_constraint (t, args, info);
> -}
> -
>   /* Return the normal form of the constraints on the placeholder 'auto'
>      type T.  */
>   
> @@ -3005,19 +2963,20 @@ normalize_placeholder_type_constraints (tree t, bool diag)
>     return normalize_constraint_expression (constr, info);
>   }
>   
> -/* Evaluate EXPR as a constraint expression using ARGS, returning a
> -   satisfaction value. */
> +/* Evaluate the constraints of T using ARGS, returning a satisfaction value.
> +   Here, T can be a concept-id, nested-requirement or placeholder 'auto'.  */
>   
>   static tree
> -satisfy_constraint_expression (tree t, tree args, sat_info info)
> +satisfy_nondeclaration_constraints (tree t, tree args, sat_info info)
>   {
>     if (t == error_mark_node)
>       return error_mark_node;
>   
>     /* Get the normalized constraints.  */
>     tree norm;
> -  if (args == NULL_TREE && concept_check_p (t))
> +  if (concept_check_p (t))
>       {
> +      gcc_assert (!args);
>         tree id = unpack_concept_check (t);
>         args = TREE_OPERAND (id, 1);
>         tree tmpl = get_concept_check_template (id);
> @@ -3032,11 +2991,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info)
>         ninfo.initial_parms = TREE_TYPE (t);
>         norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo);
>       }
> -  else if (EXPR_P (t))
> -    {
> -      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 ());
> @@ -3047,23 +3001,16 @@ satisfy_constraint_expression (tree t, tree args, sat_info info)
>       gcc_unreachable ();
>   
>     /* Perform satisfaction.  */
> -  return satisfy_constraint (norm, args, info);
> +  return satisfy_normalized_constraints (norm, args, info);
>   }
>   
> -/* Used only to evaluate requires-expressions during constant expression
> -   evaluation.  */
> -
> -tree
> -satisfy_constraint_expression (tree expr)
> -{
> -  sat_info info (tf_none, NULL_TREE);
> -  return satisfy_constraint_expression (expr, NULL_TREE, info);
> -}
> +/* Evaluate the associated constraints of the template specialization T
> +   according to INFO, returning a satisfaction value.  */
>   
>   static tree
>   satisfy_declaration_constraints (tree t, sat_info info)
>   {
> -  gcc_assert (DECL_P (t));
> +  gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL);
>     const tree saved_t = t;
>   
>     /* For inherited constructors, consider the original declaration;
> @@ -3083,26 +3030,24 @@ satisfy_declaration_constraints (tree t, sat_info info)
>       if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t))
>         return *result;
>   
> -  /* Get the normalized constraints.  */
> -  tree norm = NULL_TREE;
>     tree args = NULL_TREE;
>     if (tree ti = DECL_TEMPLATE_INFO (t))
>       {
> -      tree tmpl = TI_TEMPLATE (ti);
> -      norm = normalize_template_requirements (tmpl, info.noisy ());
> -
>         /* The initial parameter mapping is the complete set of
>   	 template arguments substituted into the declaration.  */
>         args = TI_ARGS (ti);
>         if (inh_ctor_targs)
>   	args = add_outermost_template_args (args, inh_ctor_targs);
> -    }
> -  else
> -    {
> -      /* These should be empty until we allow constraints on non-templates.  */
> -      norm = normalize_nontemplate_requirements (t, info.noisy ());
> +
> +      /* If any arguments depend on template parameters, we can't
> +	 check constraints. Pretend they're satisfied for now.  */
> +      if (uses_template_parms (args))
> +	return boolean_true_node;
>       }
>   
> +  /* Get the normalized constraints.  */
> +  tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
> +
>     unsigned ftc_count = vec_safe_length (failed_type_completions);
>   
>     tree result = boolean_true_node;
> @@ -3111,7 +3056,7 @@ satisfy_declaration_constraints (tree t, sat_info info)
>         if (!push_tinst_level (t))
>   	return result;
>         push_access_scope (t);
> -      result = satisfy_associated_constraints (norm, args, info);
> +      result = satisfy_normalized_constraints (norm, args, info);
>         pop_access_scope (t);
>         pop_tinst_level ();
>       }
> @@ -3134,6 +3079,10 @@ satisfy_declaration_constraints (tree t, sat_info info)
>     return result;
>   }
>   
> +/* Evaluate the associated constraints of the template T using ARGS as the
> +   innermost set of template arguments and according to INFO, returning a
> +   satisfaction value.  */
> +
>   static tree
>   satisfy_declaration_constraints (tree t, tree args, sat_info info)
>   {
> @@ -3144,14 +3093,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
>   
>     args = add_outermost_template_args (t, args);
>   
> +  /* If any arguments depend on template parameters, we can't
> +     check constraints. Pretend they're satisfied for now.  */
> +  if (uses_template_parms (args))
> +    return boolean_true_node;
> +
>     tree result = boolean_true_node;
> -  if (tree norm = normalize_template_requirements (t, info.noisy ()))
> +  if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
>       {
>         if (!push_tinst_level (t, args))
>   	return result;
>         tree pattern = DECL_TEMPLATE_RESULT (t);
>         push_access_scope (pattern);
> -      result = satisfy_associated_constraints (norm, args, info);
> +      result = satisfy_normalized_constraints (norm, args, info);
>         pop_access_scope (pattern);
>         pop_tinst_level ();
>       }
> @@ -3159,62 +3113,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
>     return result;
>   }
>   
> +/* A wrapper around satisfy_declaration_constraints and
> +   satisfy_nondeclaration_constraints which additionally replays
> +   quiet ill-formed satisfaction noisily, so that ill-formed
> +   satisfaction always gets diagnosed.  */
> +
>   static tree
> -constraint_satisfaction_value (tree t, sat_info info)
> +constraint_satisfaction_value (tree t, tree args, sat_info info)
>   {
>     tree r;
>     if (DECL_P (t))
> -    r = satisfy_declaration_constraints (t, info);
> +    {
> +      if (args)
> +	r = satisfy_declaration_constraints (t, args, info);
> +      else
> +	r = satisfy_declaration_constraints (t, info);
> +    }
>     else
> -    r = satisfy_constraint_expression (t, NULL_TREE, info);
> +    r = satisfy_nondeclaration_constraints (t, args, info);
>     if (r == error_mark_node && info.quiet ()
>         && !(DECL_P (t) && TREE_NO_WARNING (t)))
>       {
> -      /* Replay the error with re-normalized requirements.  */
> +      /* Replay the error noisily.  */
>         sat_info noisy (tf_warning_or_error, info.in_decl);
> -      constraint_satisfaction_value (t, noisy);
> -      if (DECL_P (t))
> +      constraint_satisfaction_value (t, args, noisy);
> +      if (DECL_P (t) && !args)
>   	/* Avoid giving these errors again.  */
>   	TREE_NO_WARNING (t) = true;
>       }
>     return r;
>   }
>   
> -static tree
> -constraint_satisfaction_value (tree t, tree args, sat_info info)
> -{
> -  tree r;
> -  if (DECL_P (t))
> -    r = satisfy_declaration_constraints (t, args, info);
> -  else
> -    r = satisfy_constraint_expression (t, args, info);
> -  if (r == error_mark_node && info.quiet ())
> -    {
> -      /* Replay the error with re-normalized requirements.  */
> -      sat_info noisy (tf_warning_or_error, info.in_decl);
> -      constraint_satisfaction_value (t, args, noisy);
> -    }
> -  return r;
> -}
> -
> -/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false
> -   otherwise, even in the case of errors.  */
> +/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE
> +   and false otherwise, even in the case of errors.
>   
> -bool
> -constraints_satisfied_p (tree t)
> -{
> -  if (!flag_concepts)
> -    return true;
> -
> -  sat_info quiet (tf_none, NULL_TREE);
> -  return constraint_satisfaction_value (t, quiet) == boolean_true_node;
> -}
> -
> -/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE
> -    and false otherwise, even in the case of errors.  */
> +   Here, T can be:
> +     - a template declaration (in which case ARGS is an innermost template
> +       argument set for T)
> +     - a template specialization (in which case ARGS must be empty)
> +     - a concept-id (in which case ARGS must be empty)
> +     - a nested-requirement (in which case ARGS is a complete argument set)
> +     - a placeholder 'auto' (in which case ARGS is a complete argument set).  */
>   
>   bool
> -constraints_satisfied_p (tree t, tree args)
> +constraints_satisfied_p (tree t, tree args/*= NULL_TREE */)
>   {
>     if (!flag_concepts)
>       return true;
> @@ -3227,7 +3169,7 @@ constraints_satisfied_p (tree t, tree args)
>      evaluation of template-ids as id-expressions.  */
>   
>   tree
> -evaluate_concept_check (tree check, tsubst_flags_t complain)
> +evaluate_concept_check (tree check)
>   {
>     if (check == error_mark_node)
>       return error_mark_node;
> @@ -3236,14 +3178,7 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
>   
>     /* Check for satisfaction without diagnostics.  */
>     sat_info quiet (tf_none, NULL_TREE);
> -  tree result = satisfy_constraint_expression (check, NULL_TREE, quiet);
> -  if (result == error_mark_node && (complain & tf_error))
> -    {
> -      /* Replay the error with re-normalized requirements.  */
> -      sat_info noisy (tf_warning_or_error, NULL_TREE);
> -      satisfy_constraint_expression (check, NULL_TREE, noisy);
> -    }
> -  return result;
> +  return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet);
>   }
>   
>   /*---------------------------------------------------------------------------
> @@ -3709,7 +3644,7 @@ diagnose_nested_requirement (tree req, tree args)
>   {
>     /* Quietly check for satisfaction first.  */
>     sat_info quiet (tf_none, NULL_TREE);
> -  tree result = satisfy_constraint_expression (req, args, quiet);
> +  tree result = satisfy_nondeclaration_constraints (req, args, quiet);
>     if (result == boolean_true_node)
>       return;
>   
> @@ -3721,7 +3656,7 @@ diagnose_nested_requirement (tree req, tree args)
>         inform (loc, "nested requirement %qE is not satisfied, because", expr);
>   
>         sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
> -      satisfy_constraint_expression (req, args, noisy);
> +      satisfy_nondeclaration_constraints (req, args, noisy);
>       }
>     else
>       inform (loc, "nested requirement %qE is not satisfied", expr);
> @@ -3854,7 +3789,9 @@ diagnosing_failed_constraint::replay_errors_p ()
>   }
>   
>   /* Emit diagnostics detailing the failure ARGS to satisfy the constraints
> -   of T. Here, T can be either a constraint or a declaration.  */
> +   of T.  Here, T and ARGS are as in constraints_satisfied_p, except that T
> +   can also be a REQUIRES_EXPR (in which case ARGS is must be empty).  The
> +   latter is used by finish_static_assert to diagnose a false REQUIRES_EXPR.  */
>   
>   void
>   diagnose_constraints (location_t loc, tree t, tree args)
> @@ -3866,8 +3803,13 @@ diagnose_constraints (location_t loc, tree t, tree args)
>   
>     /* Replay satisfaction, but diagnose unsatisfaction.  */
>     sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
> -  if (!args)
> -    constraint_satisfaction_value (t, noisy);
> +  if (TREE_CODE (t) == REQUIRES_EXPR)
> +    {
> +      gcc_assert (!args);
> +      ++current_constraint_diagnosis_depth;
> +      diagnose_requires_expr (t, /*map=*/NULL_TREE, /*in_decl=*/NULL_TREE);
> +      --current_constraint_diagnosis_depth;
> +    }
>     else
>       constraint_satisfaction_value (t, args, noisy);
>   
> diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
> index abb8a6ef078..64b1dc1b433 100644
> --- a/gcc/cp/cp-gimplify.c
> +++ b/gcc/cp/cp-gimplify.c
> @@ -1381,7 +1381,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
>   	 normal functions.  */
>         if (concept_check_p (stmt))
>   	{
> -	  *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
> +	  *stmt_p = evaluate_concept_check (stmt);
>   	  * walk_subtrees = 0;
>   	  break;
>   	}
> @@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
>   
>       case REQUIRES_EXPR:
>         /* Emit the value of the requires-expression.  */
> -      *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt),
> -				       boolean_type_node);
> +      *stmt_p = tsubst_requires_expr (stmt, NULL_TREE, tf_none, NULL_TREE);
>         *walk_subtrees = 0;
>         break;
>   
>       case TEMPLATE_ID_EXPR:
>         gcc_assert (concept_check_p (stmt));
>         /* Emit the value of the concept check.  */
> -      *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
> +      *stmt_p = evaluate_concept_check (stmt);
>         walk_subtrees = 0;
>         break;
>   
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 544e99538a4..995e13f4a6e 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -8123,10 +8123,8 @@ struct processing_constraint_expression_sentinel
>   extern bool processing_constraint_expression_p	();
>   
>   extern tree unpack_concept_check		(tree);
> -extern tree evaluate_concept_check              (tree, tsubst_flags_t);
> -extern tree satisfy_constraint_expression	(tree);
> -extern bool constraints_satisfied_p		(tree);
> -extern bool constraints_satisfied_p		(tree, tree);
> +extern tree evaluate_concept_check              (tree);
> +extern bool constraints_satisfied_p		(tree, tree = NULL_TREE);
>   extern bool* lookup_subsumption_result          (tree, tree);
>   extern bool save_subsumption_result             (tree, tree, bool);
>   extern tree find_template_parameters		(tree, tree);
> diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
> index e809f0e4068..3f5467c8283 100644
> --- a/gcc/cp/cvt.c
> +++ b/gcc/cp/cvt.c
> @@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
>     /* Explicitly evaluate void-converted concept checks since their
>        satisfaction may produce ill-formed programs.  */
>      if (concept_check_p (expr))
> -     expr = evaluate_concept_check (expr, tf_warning_or_error);
> +     expr = evaluate_concept_check (expr);
>   
>     if (VOID_TYPE_P (TREE_TYPE (expr)))
>       return expr;
>
Patrick Palka March 2, 2021, 4:25 p.m. UTC | #2
On Mon, 1 Mar 2021, Jason Merrill wrote:

> On 2/28/21 12:58 PM, Patrick Palka wrote:
> > This patch mostly performs some straightforward refactoring:
> > 
> >    - Renamed satisfy_constraint to satisfy_normalized_constraints
> >    - Renamed the three-parameter version of satisfy_constraint_expression
> >      to satisfy_nondeclaration_constraints
> >    - Removed normalize_(non)?template_requirements
> >    - Removed satisfy_associated_constraints (and made its callers
> >      check for dependent template args sooner, before normalization)
> >    - Removed the tsubst_flags_t parameter of evaluate_concept_check
> >    - Combined the two versions of constraint_satisfaction_value
> >    - Combined the two versions of constraint_satisfied_p
> > 
> > Additionally, this patch removes the handling of bare
> > constraint-expressions from satisfy_nondeclaration_constraints, and
> > hence constraints_satisfied_p and constraint_satisfaction_value now only
> > take things that carry their own template information needed for
> > normalization.  In practice, this only means it's no longer possible to
> > evaluate bare REQUIRES_EXPRs via the satisfaction routines, and so this
> > patch adjusts the affected callers to instead use tsubst_requires_expr.
> 
> It's probably better to have a different entry point than tsubst_requires_expr
> for callers that have nothing to do with templates.  Whether that's a one-line
> wrapper for the call to tsubst_requires_expr ("evaluate_requires_expr"?) or
> calling tsubst_requires_expr from satisfy_nondeclaration_constraints, I don't
> have an opinion either way.
> 
> > For convenience, the function diagnose_constraints continues to accept
> > REQUIRES_EXPRs, but it now handles them by calling diagnose_require_expr
> > directly.
> 
> This might argue for the latter choice above.

Ah, I didn't consider the option of continuing to handle REQUIRES_EXPRs
from satisfy_nondeclaration_constraints by evaluating them directly.

Here's a patch that implements this.  I opted to add a
evaluate_requires_expr wrapper as well, since

  evaluate_requires_expr (t)

is much more readable than

  constant_boolean_node (constraints_satisfied_p (t),
                         boolean_type_node)

(Note the added TODO in satisfy_nondeclaration_constraints, which'll be
fixed in a new version of the REQUIRES_EXPR evaluation/diagnostic
unification patch.)

-- >8 --

Subject: [PATCH] c++: Clean up normalization / satisfaction routines

This patch mostly performs some straightforward refactoring:

  - Renamed satisfy_constraint to satisfy_normalized_constraints
  - Renamed the three-parameter version of satisfy_constraint_expression
    to satisfy_nondeclaration_constraints
  - Removed normalize_(non)?template_requirements
  - Removed satisfy_associated_constraints (and made its callers
    check for dependent template args sooner, before normalization)
  - Removed the tsubst_flags_t parameter of evaluate_concept_check
  - Combined the two versions of constraint_satisfaction_value
  - Combined the two versions of constraint_satisfied_p

Additionally, this patch removes the handling of general
constraint-expressions from satisfy_nondeclaration_constraints, and
hence constraints_satisfied_p and constraint_satisfaction_value now take
only things that carry their own template information needed for
normalization, and, as a special case, REQUIRES_EXPRs.  But the latter
now get evaluated directly via tsubst_requires_expr rather than going
through satisfaction.

(That we used to evaluate REQUIRES_EXPR via satisfaction might even be a
correctness issue: since we cache satisfaction in special ways that don't
apply to regular evaluation, going through satisfaction could in theory
cause us to reuse a cached value for a REQUIRES_EXPR when we shouldn't
have.)

gcc/cp/ChangeLog:

	* constexpr.c (cxx_eval_call_expression): Adjust call to
	evaluate_concept_check.
	(cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use
	evaluate_requires_expression instead of
	satisfy_constraint_expression.
	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
	* constraint.cc (struct sat_info): Adjust comment about which
	satisfaction entrypoints use noisy-unsat.
	(normalize_template_requirements): Remove (and adjust callers
	appropriately).
	(normalize_nontemplate_requirements): Likewise.
	(tsubst_nested_requirement): Use constraint_satisfaction_value
	instead of satisfy_constraint_expression, which'll do the
	noisy replaying of ill-formed quiet satisfaction for us.
	(decl_satisfied_cache): Adjust comment.
	(satisfy_constraint): Rename to ...
	(satisfy_normalized_constraints): ... this.
	(satisfy_associated_constraints): Remove (and make its
	callers check for dependent arguments).
	(satisfy_constraint_expression): Rename to ...
	(satisfy_nondeclaration_constraints): ... this.  Assert that
	'args' is empty when 't' is a concept-id.  Removing handling
	bare constraint-expressions, and handle REQUIRES_EXPRs
	specially.  Adjust comment accordingly.
	(satisfy_declaration_constraints): Assert in the two-parameter
	version that 't' is not a TEMPLATE_DECL.  Adjust following
	removal of normalize_(non)?template_requirements and
	satisfy_asociated_constraints.
	(constraint_satisfaction_value): Combine the two- and
	three-parameter versions in the natural way.
	(constraints_satisfied_p): Combine the one- and two-parameter
	versions in the natural way.  Improve documentation.
	(evaluate_requires_expr): Define.
	(evaluate_concept_check): Remove 'complain' parameter.  Use
	constraint_satisfaction_value instead of
	satisfy_constraint_expression.
	(diagnose_nested_requirement): Adjust following renaming of
	satisfy_constraint_expression.
	(diagnose_constraints): Handle REQUIRES_EXPR by going through
	diagnose_requires_expr directly instead of treating it as a
	constraint-expression.  Improve documentation.
	* cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call
	to evaluate_concept_check.
	<case REQUIRES_EXPR>: Use evaluate_requires_expr instead of
	constraints_satisfied_p.
	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
	* cp-tree.h (evaluate_requires_expr): Declare.
	(evaluate_concept_check): Remove tsubst_flag_t parameter.
	(satisfy_constraint_expression): Remove declaration.
	(constraints_satisfied_p): Remove one-parameter declaration.
	Add a default argument to the two-parameter declaration.
	* cvt.c (convert_to_void): Adjust call to
	evaluate_concept_check.
---
 gcc/cp/constexpr.c   |   6 +-
 gcc/cp/constraint.cc | 234 ++++++++++++++++++-------------------------
 gcc/cp/cp-gimplify.c |   7 +-
 gcc/cp/cp-tree.h     |   7 +-
 gcc/cp/cvt.c         |   2 +-
 5 files changed, 110 insertions(+), 146 deletions(-)

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index cd0a68e9fd6..7d96d577d84 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2257,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 {
   /* Handle concept checks separately.  */
   if (concept_check_p (t))
-    return evaluate_concept_check (t, tf_warning_or_error);
+    return evaluate_concept_check (t);
 
   location_t loc = cp_expr_loc_or_input_loc (t);
   tree fun = get_function_named_in_call (t);
@@ -6905,7 +6905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          '!requires (T t) { ... }' which is not transformed into
          a constraint.  */
       if (!processing_template_decl)
-        return satisfy_constraint_expression (t);
+	return evaluate_requires_expr (t);
       else
         *non_constant_p = true;
       return t;
@@ -6941,7 +6941,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 
 	if (!processing_template_decl
 	    && !uid_sensitive_constexpr_evaluation_p ())
-	  r = evaluate_concept_check (t, tf_warning_or_error);
+	  r = evaluate_concept_check (t);
 	else
 	  *non_constant_p = true;
 
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 3332e6092a9..0949788aa29 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -107,10 +107,9 @@ struct subst_info
    a constraint is not satisfied.
 
    The entrypoints to satisfaction for which we set noisy+unsat are
-   diagnose_constraints and diagnose_nested_requirement.  The entrypoints for
-   which we set noisy-unsat are the replays inside constraint_satisfaction_value,
-   evaluate_concept_check and tsubst_nested_requirement.  In other entrypoints,
-   e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags
+   diagnose_constraints and diagnose_nested_requirement.  The entrypoint for
+   which we set noisy-unsat is the replay inside constraint_satisfaction_value.
+   From constraints_satisfied_p, we enter satisfaction quietly (both flags
    cleared).  */
 
 struct sat_info : subst_info
@@ -133,7 +132,7 @@ struct sat_info : subst_info
   bool diagnose_unsatisfaction;
 };
 
-static tree satisfy_constraint_expression (tree, tree, sat_info);
+static tree constraint_satisfaction_value (tree, tree, sat_info);
 
 /* True if T is known to be some type other than bool. Note that this
    is false for dependent types and errors.  */
@@ -958,22 +957,6 @@ normalize_concept_definition (tree tmpl, bool diag = false)
   return norm;
 }
 
-/* Returns the normal form of TMPL's requirements.  */
-
-static tree
-normalize_template_requirements (tree tmpl, bool diag = false)
-{
-  return get_normalized_constraints_from_decl (tmpl, diag);
-}
-
-/* Returns the normal form of TMPL's requirements.  */
-
-static tree
-normalize_nontemplate_requirements (tree decl, bool diag = false)
-{
-  return get_normalized_constraints_from_decl (decl, diag);
-}
-
 /* Normalize an EXPR as a constraint.  */
 
 static tree
@@ -2068,15 +2051,8 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
 static tree
 tsubst_nested_requirement (tree t, tree args, subst_info info)
 {
-  /* Perform satisfaction quietly first.  */
   sat_info quiet (tf_none, info.in_decl);
-  tree result = satisfy_constraint_expression (t, args, quiet);
-  if (result == error_mark_node)
-    {
-      /* Replay the error.  */
-      sat_info noisy (tf_warning_or_error, info.in_decl);
-      satisfy_constraint_expression (t, args, noisy);
-    }
+  tree result = constraint_satisfaction_value (t, args, quiet);
   if (result != boolean_true_node)
     return error_mark_node;
   return boolean_true_node;
@@ -2459,7 +2435,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
 /* Cache the result of satisfy_atom.  */
 static GTY((deletable)) hash_table<sat_hasher> *sat_cache;
 
-/* Cache the result of constraint_satisfaction_value.  */
+/* Cache the result of satisfy_declaration_constraints.  */
 static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache;
 
 /* A tool used by satisfy_atom to help manage satisfaction caching and to
@@ -2941,7 +2917,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info)
 /* Check that the normalized constraint T is satisfied for ARGS.  */
 
 static tree
-satisfy_constraint (tree t, tree args, sat_info info)
+satisfy_normalized_constraints (tree t, tree args, sat_info info)
 {
   auto_timevar time (TV_CONSTRAINT_SAT);
 
@@ -2957,24 +2933,6 @@ satisfy_constraint (tree t, tree args, sat_info info)
   return satisfy_constraint_r (t, args, info);
 }
 
-/* Check the normalized constraints T against ARGS, returning a satisfaction
-   value (either true, false, or error).  */
-
-static tree
-satisfy_associated_constraints (tree t, tree args, sat_info info)
-{
-  /* If there are no constraints then this is trivially satisfied.  */
-  if (!t)
-    return boolean_true_node;
-
-  /* If any arguments depend on template parameters, we can't
-     check constraints. Pretend they're satisfied for now.  */
-  if (args && uses_template_parms (args))
-    return boolean_true_node;
-
-  return satisfy_constraint (t, args, info);
-}
-
 /* Return the normal form of the constraints on the placeholder 'auto'
    type T.  */
 
@@ -3005,19 +2963,34 @@ normalize_placeholder_type_constraints (tree t, bool diag)
   return normalize_constraint_expression (constr, info);
 }
 
-/* Evaluate EXPR as a constraint expression using ARGS, returning a
-   satisfaction value. */
+/* Evaluate the constraints of T using ARGS, returning a satisfaction value.
+   Here, T can be a concept-id, nested-requirement, placeholder 'auto', or
+   requires-expression.  */
 
 static tree
-satisfy_constraint_expression (tree t, tree args, sat_info info)
+satisfy_nondeclaration_constraints (tree t, tree args, sat_info info)
 {
   if (t == error_mark_node)
     return error_mark_node;
 
+  /* Handle REQUIRES_EXPR directly, bypassing satisfaction.  */
+  if (TREE_CODE (t) == REQUIRES_EXPR)
+    {
+      /* TODO: Remove this assert and the special casing of REQUIRES_EXPRs
+	 from diagnose_constraints once we merge tsubst_requires_expr and
+	 diagnose_requires_expr.  */
+      gcc_assert (!info.diagnose_unsatisfaction_p ());
+      auto ovr = make_temp_override (current_constraint_diagnosis_depth);
+      if (info.noisy ())
+	++current_constraint_diagnosis_depth;
+      return tsubst_requires_expr (t, args, info.complain, info.in_decl);
+    }
+
   /* Get the normalized constraints.  */
   tree norm;
-  if (args == NULL_TREE && concept_check_p (t))
+  if (concept_check_p (t))
     {
+      gcc_assert (!args);
       tree id = unpack_concept_check (t);
       args = TREE_OPERAND (id, 1);
       tree tmpl = get_concept_check_template (id);
@@ -3032,11 +3005,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info)
       ninfo.initial_parms = TREE_TYPE (t);
       norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo);
     }
-  else if (EXPR_P (t))
-    {
-      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 ());
@@ -3047,23 +3015,16 @@ satisfy_constraint_expression (tree t, tree args, sat_info info)
     gcc_unreachable ();
 
   /* Perform satisfaction.  */
-  return satisfy_constraint (norm, args, info);
+  return satisfy_normalized_constraints (norm, args, info);
 }
 
-/* Used only to evaluate requires-expressions during constant expression
-   evaluation.  */
-
-tree
-satisfy_constraint_expression (tree expr)
-{
-  sat_info info (tf_none, NULL_TREE);
-  return satisfy_constraint_expression (expr, NULL_TREE, info);
-}
+/* Evaluate the associated constraints of the template specialization T
+   according to INFO, returning a satisfaction value.  */
 
 static tree
 satisfy_declaration_constraints (tree t, sat_info info)
 {
-  gcc_assert (DECL_P (t));
+  gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL);
   const tree saved_t = t;
 
   /* For inherited constructors, consider the original declaration;
@@ -3083,26 +3044,24 @@ satisfy_declaration_constraints (tree t, sat_info info)
     if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t))
       return *result;
 
-  /* Get the normalized constraints.  */
-  tree norm = NULL_TREE;
   tree args = NULL_TREE;
   if (tree ti = DECL_TEMPLATE_INFO (t))
     {
-      tree tmpl = TI_TEMPLATE (ti);
-      norm = normalize_template_requirements (tmpl, info.noisy ());
-
       /* The initial parameter mapping is the complete set of
 	 template arguments substituted into the declaration.  */
       args = TI_ARGS (ti);
       if (inh_ctor_targs)
 	args = add_outermost_template_args (args, inh_ctor_targs);
-    }
-  else
-    {
-      /* These should be empty until we allow constraints on non-templates.  */
-      norm = normalize_nontemplate_requirements (t, info.noisy ());
+
+      /* If any arguments depend on template parameters, we can't
+	 check constraints. Pretend they're satisfied for now.  */
+      if (uses_template_parms (args))
+	return boolean_true_node;
     }
 
+  /* Get the normalized constraints.  */
+  tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
+
   unsigned ftc_count = vec_safe_length (failed_type_completions);
 
   tree result = boolean_true_node;
@@ -3111,7 +3070,7 @@ satisfy_declaration_constraints (tree t, sat_info info)
       if (!push_tinst_level (t))
 	return result;
       push_access_scope (t);
-      result = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_normalized_constraints (norm, args, info);
       pop_access_scope (t);
       pop_tinst_level ();
     }
@@ -3134,6 +3093,10 @@ satisfy_declaration_constraints (tree t, sat_info info)
   return result;
 }
 
+/* Evaluate the associated constraints of the template T using ARGS as the
+   innermost set of template arguments and according to INFO, returning a
+   satisfaction value.  */
+
 static tree
 satisfy_declaration_constraints (tree t, tree args, sat_info info)
 {
@@ -3144,14 +3107,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
 
   args = add_outermost_template_args (t, args);
 
+  /* If any arguments depend on template parameters, we can't
+     check constraints. Pretend they're satisfied for now.  */
+  if (uses_template_parms (args))
+    return boolean_true_node;
+
   tree result = boolean_true_node;
-  if (tree norm = normalize_template_requirements (t, info.noisy ()))
+  if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
     {
       if (!push_tinst_level (t, args))
 	return result;
       tree pattern = DECL_TEMPLATE_RESULT (t);
       push_access_scope (pattern);
-      result = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_normalized_constraints (norm, args, info);
       pop_access_scope (pattern);
       pop_tinst_level ();
     }
@@ -3159,62 +3127,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
   return result;
 }
 
+/* A wrapper around satisfy_declaration_constraints and
+   satisfy_nondeclaration_constraints which additionally replays
+   quiet ill-formed satisfaction noisily, so that ill-formed
+   satisfaction always gets diagnosed.  */
+
 static tree
-constraint_satisfaction_value (tree t, sat_info info)
+constraint_satisfaction_value (tree t, tree args, sat_info info)
 {
   tree r;
   if (DECL_P (t))
-    r = satisfy_declaration_constraints (t, info);
+    {
+      if (args)
+	r = satisfy_declaration_constraints (t, args, info);
+      else
+	r = satisfy_declaration_constraints (t, info);
+    }
   else
-    r = satisfy_constraint_expression (t, NULL_TREE, info);
+    r = satisfy_nondeclaration_constraints (t, args, info);
   if (r == error_mark_node && info.quiet ()
       && !(DECL_P (t) && TREE_NO_WARNING (t)))
     {
-      /* Replay the error with re-normalized requirements.  */
+      /* Replay the error noisily.  */
       sat_info noisy (tf_warning_or_error, info.in_decl);
-      constraint_satisfaction_value (t, noisy);
-      if (DECL_P (t))
+      constraint_satisfaction_value (t, args, noisy);
+      if (DECL_P (t) && !args)
 	/* Avoid giving these errors again.  */
 	TREE_NO_WARNING (t) = true;
     }
   return r;
 }
 
-static tree
-constraint_satisfaction_value (tree t, tree args, sat_info info)
-{
-  tree r;
-  if (DECL_P (t))
-    r = satisfy_declaration_constraints (t, args, info);
-  else
-    r = satisfy_constraint_expression (t, args, info);
-  if (r == error_mark_node && info.quiet ())
-    {
-      /* Replay the error with re-normalized requirements.  */
-      sat_info noisy (tf_warning_or_error, info.in_decl);
-      constraint_satisfaction_value (t, args, noisy);
-    }
-  return r;
-}
-
-/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false
-   otherwise, even in the case of errors.  */
-
-bool
-constraints_satisfied_p (tree t)
-{
-  if (!flag_concepts)
-    return true;
-
-  sat_info quiet (tf_none, NULL_TREE);
-  return constraint_satisfaction_value (t, quiet) == boolean_true_node;
-}
+/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE
+   and false otherwise, even in the case of errors.
 
-/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE
-    and false otherwise, even in the case of errors.  */
+   Here, T can be:
+     - a template declaration
+     - a template specialization (in which case ARGS must be empty)
+     - a concept-id (in which case ARGS must be empty)
+     - a nested-requirement
+     - a placeholder 'auto'
+     - a requires-expression.  */
 
 bool
-constraints_satisfied_p (tree t, tree args)
+constraints_satisfied_p (tree t, tree args/*= NULL_TREE */)
 {
   if (!flag_concepts)
     return true;
@@ -3227,7 +3183,7 @@ constraints_satisfied_p (tree t, tree args)
    evaluation of template-ids as id-expressions.  */
 
 tree
-evaluate_concept_check (tree check, tsubst_flags_t complain)
+evaluate_concept_check (tree check)
 {
   if (check == error_mark_node)
     return error_mark_node;
@@ -3236,14 +3192,19 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
 
   /* Check for satisfaction without diagnostics.  */
   sat_info quiet (tf_none, NULL_TREE);
-  tree result = satisfy_constraint_expression (check, NULL_TREE, quiet);
-  if (result == error_mark_node && (complain & tf_error))
-    {
-      /* Replay the error with re-normalized requirements.  */
-      sat_info noisy (tf_warning_or_error, NULL_TREE);
-      satisfy_constraint_expression (check, NULL_TREE, noisy);
-    }
-  return result;
+  return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet);
+}
+
+/* Evaluate the requires-expression T, returning either boolean_true_node
+   or boolean_false_node.  This is used during gimplification and constexpr
+   evaluation.  */
+
+tree
+evaluate_requires_expr (tree t)
+{
+  gcc_assert (TREE_CODE (t) == REQUIRES_EXPR);
+  sat_info quiet (tf_none, NULL_TREE);
+  return constraint_satisfaction_value (t, /*args=*/NULL_TREE, quiet);
 }
 
 /*---------------------------------------------------------------------------
@@ -3709,7 +3670,7 @@ diagnose_nested_requirement (tree req, tree args)
 {
   /* Quietly check for satisfaction first.  */
   sat_info quiet (tf_none, NULL_TREE);
-  tree result = satisfy_constraint_expression (req, args, quiet);
+  tree result = satisfy_nondeclaration_constraints (req, args, quiet);
   if (result == boolean_true_node)
     return;
 
@@ -3721,7 +3682,7 @@ diagnose_nested_requirement (tree req, tree args)
       inform (loc, "nested requirement %qE is not satisfied, because", expr);
 
       sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
-      satisfy_constraint_expression (req, args, noisy);
+      satisfy_nondeclaration_constraints (req, args, noisy);
     }
   else
     inform (loc, "nested requirement %qE is not satisfied", expr);
@@ -3854,7 +3815,7 @@ diagnosing_failed_constraint::replay_errors_p ()
 }
 
 /* Emit diagnostics detailing the failure ARGS to satisfy the constraints
-   of T. Here, T can be either a constraint or a declaration.  */
+   of T.  Here, T and ARGS are as in constraints_satisfied_p.  */
 
 void
 diagnose_constraints (location_t loc, tree t, tree args)
@@ -3866,8 +3827,13 @@ diagnose_constraints (location_t loc, tree t, tree args)
 
   /* Replay satisfaction, but diagnose unsatisfaction.  */
   sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
-  if (!args)
-    constraint_satisfaction_value (t, noisy);
+  if (TREE_CODE (t) == REQUIRES_EXPR)
+    {
+      gcc_assert (!args);
+      ++current_constraint_diagnosis_depth;
+      diagnose_requires_expr (t, /*map=*/NULL_TREE, /*in_decl=*/NULL_TREE);
+      --current_constraint_diagnosis_depth;
+    }
   else
     constraint_satisfaction_value (t, args, noisy);
 
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index abb8a6ef078..df89ff3815b 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1381,7 +1381,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	 normal functions.  */
       if (concept_check_p (stmt))
 	{
-	  *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+	  *stmt_p = evaluate_concept_check (stmt);
 	  * walk_subtrees = 0;
 	  break;
 	}
@@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 
     case REQUIRES_EXPR:
       /* Emit the value of the requires-expression.  */
-      *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt),
-				       boolean_type_node);
+      *stmt_p = evaluate_requires_expr (stmt);
       *walk_subtrees = 0;
       break;
 
     case TEMPLATE_ID_EXPR:
       gcc_assert (concept_check_p (stmt));
       /* Emit the value of the concept check.  */
-      *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+      *stmt_p = evaluate_concept_check (stmt);
       walk_subtrees = 0;
       break;
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 544e99538a4..f06ac3c73bf 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8109,6 +8109,7 @@ extern tree finish_compound_requirement         (location_t, tree, tree, bool);
 extern tree finish_nested_requirement           (location_t, tree);
 extern void check_constrained_friend            (tree, tree);
 extern tree tsubst_requires_expr                (tree, tree, tsubst_flags_t, tree);
+extern tree evaluate_requires_expr		(tree);
 extern tree tsubst_constraint                   (tree, tree, tsubst_flags_t, tree);
 extern tree tsubst_constraint_info              (tree, tree, tsubst_flags_t, tree);
 extern tree tsubst_parameter_mapping		(tree, tree, tsubst_flags_t, tree);
@@ -8123,10 +8124,8 @@ struct processing_constraint_expression_sentinel
 extern bool processing_constraint_expression_p	();
 
 extern tree unpack_concept_check		(tree);
-extern tree evaluate_concept_check              (tree, tsubst_flags_t);
-extern tree satisfy_constraint_expression	(tree);
-extern bool constraints_satisfied_p		(tree);
-extern bool constraints_satisfied_p		(tree, tree);
+extern tree evaluate_concept_check              (tree);
+extern bool constraints_satisfied_p		(tree, tree = NULL_TREE);
 extern bool* lookup_subsumption_result          (tree, tree);
 extern bool save_subsumption_result             (tree, tree, bool);
 extern tree find_template_parameters		(tree, tree);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index e809f0e4068..3f5467c8283 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
   /* Explicitly evaluate void-converted concept checks since their
      satisfaction may produce ill-formed programs.  */
    if (concept_check_p (expr))
-     expr = evaluate_concept_check (expr, tf_warning_or_error);
+     expr = evaluate_concept_check (expr);
 
   if (VOID_TYPE_P (TREE_TYPE (expr)))
     return expr;
Jason Merrill March 2, 2021, 10:58 p.m. UTC | #3
On 3/2/21 11:25 AM, Patrick Palka wrote:
> On Mon, 1 Mar 2021, Jason Merrill wrote:
> 
>> On 2/28/21 12:58 PM, Patrick Palka wrote:
>>> This patch mostly performs some straightforward refactoring:
>>>
>>>     - Renamed satisfy_constraint to satisfy_normalized_constraints
>>>     - Renamed the three-parameter version of satisfy_constraint_expression
>>>       to satisfy_nondeclaration_constraints
>>>     - Removed normalize_(non)?template_requirements
>>>     - Removed satisfy_associated_constraints (and made its callers
>>>       check for dependent template args sooner, before normalization)
>>>     - Removed the tsubst_flags_t parameter of evaluate_concept_check
>>>     - Combined the two versions of constraint_satisfaction_value
>>>     - Combined the two versions of constraint_satisfied_p
>>>
>>> Additionally, this patch removes the handling of bare
>>> constraint-expressions from satisfy_nondeclaration_constraints, and
>>> hence constraints_satisfied_p and constraint_satisfaction_value now only
>>> take things that carry their own template information needed for
>>> normalization.  In practice, this only means it's no longer possible to
>>> evaluate bare REQUIRES_EXPRs via the satisfaction routines, and so this
>>> patch adjusts the affected callers to instead use tsubst_requires_expr.
>>
>> It's probably better to have a different entry point than tsubst_requires_expr
>> for callers that have nothing to do with templates.  Whether that's a one-line
>> wrapper for the call to tsubst_requires_expr ("evaluate_requires_expr"?) or
>> calling tsubst_requires_expr from satisfy_nondeclaration_constraints, I don't
>> have an opinion either way.
>>
>>> For convenience, the function diagnose_constraints continues to accept
>>> REQUIRES_EXPRs, but it now handles them by calling diagnose_require_expr
>>> directly.
>>
>> This might argue for the latter choice above.
> 
> Ah, I didn't consider the option of continuing to handle REQUIRES_EXPRs
> from satisfy_nondeclaration_constraints by evaluating them directly.
> 
> Here's a patch that implements this.  I opted to add a
> evaluate_requires_expr wrapper as well, since
> 
>    evaluate_requires_expr (t)
> 
> is much more readable than
> 
>    constant_boolean_node (constraints_satisfied_p (t),
>                           boolean_type_node)
> 
> (Note the added TODO in satisfy_nondeclaration_constraints, which'll be
> fixed in a new version of the REQUIRES_EXPR evaluation/diagnostic
> unification patch.)

OK.

> -- >8 --
> 
> Subject: [PATCH] c++: Clean up normalization / satisfaction routines
> 
> This patch mostly performs some straightforward refactoring:
> 
>    - Renamed satisfy_constraint to satisfy_normalized_constraints
>    - Renamed the three-parameter version of satisfy_constraint_expression
>      to satisfy_nondeclaration_constraints
>    - Removed normalize_(non)?template_requirements
>    - Removed satisfy_associated_constraints (and made its callers
>      check for dependent template args sooner, before normalization)
>    - Removed the tsubst_flags_t parameter of evaluate_concept_check
>    - Combined the two versions of constraint_satisfaction_value
>    - Combined the two versions of constraint_satisfied_p
> 
> Additionally, this patch removes the handling of general
> constraint-expressions from satisfy_nondeclaration_constraints, and
> hence constraints_satisfied_p and constraint_satisfaction_value now take
> only things that carry their own template information needed for
> normalization, and, as a special case, REQUIRES_EXPRs.  But the latter
> now get evaluated directly via tsubst_requires_expr rather than going
> through satisfaction.
> 
> (That we used to evaluate REQUIRES_EXPR via satisfaction might even be a
> correctness issue: since we cache satisfaction in special ways that don't
> apply to regular evaluation, going through satisfaction could in theory
> cause us to reuse a cached value for a REQUIRES_EXPR when we shouldn't
> have.)
> 
> gcc/cp/ChangeLog:
> 
> 	* constexpr.c (cxx_eval_call_expression): Adjust call to
> 	evaluate_concept_check.
> 	(cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use
> 	evaluate_requires_expression instead of
> 	satisfy_constraint_expression.
> 	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
> 	* constraint.cc (struct sat_info): Adjust comment about which
> 	satisfaction entrypoints use noisy-unsat.
> 	(normalize_template_requirements): Remove (and adjust callers
> 	appropriately).
> 	(normalize_nontemplate_requirements): Likewise.
> 	(tsubst_nested_requirement): Use constraint_satisfaction_value
> 	instead of satisfy_constraint_expression, which'll do the
> 	noisy replaying of ill-formed quiet satisfaction for us.
> 	(decl_satisfied_cache): Adjust comment.
> 	(satisfy_constraint): Rename to ...
> 	(satisfy_normalized_constraints): ... this.
> 	(satisfy_associated_constraints): Remove (and make its
> 	callers check for dependent arguments).
> 	(satisfy_constraint_expression): Rename to ...
> 	(satisfy_nondeclaration_constraints): ... this.  Assert that
> 	'args' is empty when 't' is a concept-id.  Removing handling
> 	bare constraint-expressions, and handle REQUIRES_EXPRs
> 	specially.  Adjust comment accordingly.
> 	(satisfy_declaration_constraints): Assert in the two-parameter
> 	version that 't' is not a TEMPLATE_DECL.  Adjust following
> 	removal of normalize_(non)?template_requirements and
> 	satisfy_asociated_constraints.
> 	(constraint_satisfaction_value): Combine the two- and
> 	three-parameter versions in the natural way.
> 	(constraints_satisfied_p): Combine the one- and two-parameter
> 	versions in the natural way.  Improve documentation.
> 	(evaluate_requires_expr): Define.
> 	(evaluate_concept_check): Remove 'complain' parameter.  Use
> 	constraint_satisfaction_value instead of
> 	satisfy_constraint_expression.
> 	(diagnose_nested_requirement): Adjust following renaming of
> 	satisfy_constraint_expression.
> 	(diagnose_constraints): Handle REQUIRES_EXPR by going through
> 	diagnose_requires_expr directly instead of treating it as a
> 	constraint-expression.  Improve documentation.
> 	* cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call
> 	to evaluate_concept_check.
> 	<case REQUIRES_EXPR>: Use evaluate_requires_expr instead of
> 	constraints_satisfied_p.
> 	<case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
> 	* cp-tree.h (evaluate_requires_expr): Declare.
> 	(evaluate_concept_check): Remove tsubst_flag_t parameter.
> 	(satisfy_constraint_expression): Remove declaration.
> 	(constraints_satisfied_p): Remove one-parameter declaration.
> 	Add a default argument to the two-parameter declaration.
> 	* cvt.c (convert_to_void): Adjust call to
> 	evaluate_concept_check.
> ---
>   gcc/cp/constexpr.c   |   6 +-
>   gcc/cp/constraint.cc | 234 ++++++++++++++++++-------------------------
>   gcc/cp/cp-gimplify.c |   7 +-
>   gcc/cp/cp-tree.h     |   7 +-
>   gcc/cp/cvt.c         |   2 +-
>   5 files changed, 110 insertions(+), 146 deletions(-)
> 
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index cd0a68e9fd6..7d96d577d84 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -2257,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
>   {
>     /* Handle concept checks separately.  */
>     if (concept_check_p (t))
> -    return evaluate_concept_check (t, tf_warning_or_error);
> +    return evaluate_concept_check (t);
>   
>     location_t loc = cp_expr_loc_or_input_loc (t);
>     tree fun = get_function_named_in_call (t);
> @@ -6905,7 +6905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>            '!requires (T t) { ... }' which is not transformed into
>            a constraint.  */
>         if (!processing_template_decl)
> -        return satisfy_constraint_expression (t);
> +	return evaluate_requires_expr (t);
>         else
>           *non_constant_p = true;
>         return t;
> @@ -6941,7 +6941,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>   
>   	if (!processing_template_decl
>   	    && !uid_sensitive_constexpr_evaluation_p ())
> -	  r = evaluate_concept_check (t, tf_warning_or_error);
> +	  r = evaluate_concept_check (t);
>   	else
>   	  *non_constant_p = true;
>   
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 3332e6092a9..0949788aa29 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -107,10 +107,9 @@ struct subst_info
>      a constraint is not satisfied.
>   
>      The entrypoints to satisfaction for which we set noisy+unsat are
> -   diagnose_constraints and diagnose_nested_requirement.  The entrypoints for
> -   which we set noisy-unsat are the replays inside constraint_satisfaction_value,
> -   evaluate_concept_check and tsubst_nested_requirement.  In other entrypoints,
> -   e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags
> +   diagnose_constraints and diagnose_nested_requirement.  The entrypoint for
> +   which we set noisy-unsat is the replay inside constraint_satisfaction_value.
> +   From constraints_satisfied_p, we enter satisfaction quietly (both flags
>      cleared).  */
>   
>   struct sat_info : subst_info
> @@ -133,7 +132,7 @@ struct sat_info : subst_info
>     bool diagnose_unsatisfaction;
>   };
>   
> -static tree satisfy_constraint_expression (tree, tree, sat_info);
> +static tree constraint_satisfaction_value (tree, tree, sat_info);
>   
>   /* True if T is known to be some type other than bool. Note that this
>      is false for dependent types and errors.  */
> @@ -958,22 +957,6 @@ normalize_concept_definition (tree tmpl, bool diag = false)
>     return norm;
>   }
>   
> -/* Returns the normal form of TMPL's requirements.  */
> -
> -static tree
> -normalize_template_requirements (tree tmpl, bool diag = false)
> -{
> -  return get_normalized_constraints_from_decl (tmpl, diag);
> -}
> -
> -/* Returns the normal form of TMPL's requirements.  */
> -
> -static tree
> -normalize_nontemplate_requirements (tree decl, bool diag = false)
> -{
> -  return get_normalized_constraints_from_decl (decl, diag);
> -}
> -
>   /* Normalize an EXPR as a constraint.  */
>   
>   static tree
> @@ -2068,15 +2051,8 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
>   static tree
>   tsubst_nested_requirement (tree t, tree args, subst_info info)
>   {
> -  /* Perform satisfaction quietly first.  */
>     sat_info quiet (tf_none, info.in_decl);
> -  tree result = satisfy_constraint_expression (t, args, quiet);
> -  if (result == error_mark_node)
> -    {
> -      /* Replay the error.  */
> -      sat_info noisy (tf_warning_or_error, info.in_decl);
> -      satisfy_constraint_expression (t, args, noisy);
> -    }
> +  tree result = constraint_satisfaction_value (t, args, quiet);
>     if (result != boolean_true_node)
>       return error_mark_node;
>     return boolean_true_node;
> @@ -2459,7 +2435,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
>   /* Cache the result of satisfy_atom.  */
>   static GTY((deletable)) hash_table<sat_hasher> *sat_cache;
>   
> -/* Cache the result of constraint_satisfaction_value.  */
> +/* Cache the result of satisfy_declaration_constraints.  */
>   static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache;
>   
>   /* A tool used by satisfy_atom to help manage satisfaction caching and to
> @@ -2941,7 +2917,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info)
>   /* Check that the normalized constraint T is satisfied for ARGS.  */
>   
>   static tree
> -satisfy_constraint (tree t, tree args, sat_info info)
> +satisfy_normalized_constraints (tree t, tree args, sat_info info)
>   {
>     auto_timevar time (TV_CONSTRAINT_SAT);
>   
> @@ -2957,24 +2933,6 @@ satisfy_constraint (tree t, tree args, sat_info info)
>     return satisfy_constraint_r (t, args, info);
>   }
>   
> -/* Check the normalized constraints T against ARGS, returning a satisfaction
> -   value (either true, false, or error).  */
> -
> -static tree
> -satisfy_associated_constraints (tree t, tree args, sat_info info)
> -{
> -  /* If there are no constraints then this is trivially satisfied.  */
> -  if (!t)
> -    return boolean_true_node;
> -
> -  /* If any arguments depend on template parameters, we can't
> -     check constraints. Pretend they're satisfied for now.  */
> -  if (args && uses_template_parms (args))
> -    return boolean_true_node;
> -
> -  return satisfy_constraint (t, args, info);
> -}
> -
>   /* Return the normal form of the constraints on the placeholder 'auto'
>      type T.  */
>   
> @@ -3005,19 +2963,34 @@ normalize_placeholder_type_constraints (tree t, bool diag)
>     return normalize_constraint_expression (constr, info);
>   }
>   
> -/* Evaluate EXPR as a constraint expression using ARGS, returning a
> -   satisfaction value. */
> +/* Evaluate the constraints of T using ARGS, returning a satisfaction value.
> +   Here, T can be a concept-id, nested-requirement, placeholder 'auto', or
> +   requires-expression.  */
>   
>   static tree
> -satisfy_constraint_expression (tree t, tree args, sat_info info)
> +satisfy_nondeclaration_constraints (tree t, tree args, sat_info info)
>   {
>     if (t == error_mark_node)
>       return error_mark_node;
>   
> +  /* Handle REQUIRES_EXPR directly, bypassing satisfaction.  */
> +  if (TREE_CODE (t) == REQUIRES_EXPR)
> +    {
> +      /* TODO: Remove this assert and the special casing of REQUIRES_EXPRs
> +	 from diagnose_constraints once we merge tsubst_requires_expr and
> +	 diagnose_requires_expr.  */
> +      gcc_assert (!info.diagnose_unsatisfaction_p ());
> +      auto ovr = make_temp_override (current_constraint_diagnosis_depth);
> +      if (info.noisy ())
> +	++current_constraint_diagnosis_depth;
> +      return tsubst_requires_expr (t, args, info.complain, info.in_decl);
> +    }
> +
>     /* Get the normalized constraints.  */
>     tree norm;
> -  if (args == NULL_TREE && concept_check_p (t))
> +  if (concept_check_p (t))
>       {
> +      gcc_assert (!args);
>         tree id = unpack_concept_check (t);
>         args = TREE_OPERAND (id, 1);
>         tree tmpl = get_concept_check_template (id);
> @@ -3032,11 +3005,6 @@ satisfy_constraint_expression (tree t, tree args, sat_info info)
>         ninfo.initial_parms = TREE_TYPE (t);
>         norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo);
>       }
> -  else if (EXPR_P (t))
> -    {
> -      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 ());
> @@ -3047,23 +3015,16 @@ satisfy_constraint_expression (tree t, tree args, sat_info info)
>       gcc_unreachable ();
>   
>     /* Perform satisfaction.  */
> -  return satisfy_constraint (norm, args, info);
> +  return satisfy_normalized_constraints (norm, args, info);
>   }
>   
> -/* Used only to evaluate requires-expressions during constant expression
> -   evaluation.  */
> -
> -tree
> -satisfy_constraint_expression (tree expr)
> -{
> -  sat_info info (tf_none, NULL_TREE);
> -  return satisfy_constraint_expression (expr, NULL_TREE, info);
> -}
> +/* Evaluate the associated constraints of the template specialization T
> +   according to INFO, returning a satisfaction value.  */
>   
>   static tree
>   satisfy_declaration_constraints (tree t, sat_info info)
>   {
> -  gcc_assert (DECL_P (t));
> +  gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL);
>     const tree saved_t = t;
>   
>     /* For inherited constructors, consider the original declaration;
> @@ -3083,26 +3044,24 @@ satisfy_declaration_constraints (tree t, sat_info info)
>       if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t))
>         return *result;
>   
> -  /* Get the normalized constraints.  */
> -  tree norm = NULL_TREE;
>     tree args = NULL_TREE;
>     if (tree ti = DECL_TEMPLATE_INFO (t))
>       {
> -      tree tmpl = TI_TEMPLATE (ti);
> -      norm = normalize_template_requirements (tmpl, info.noisy ());
> -
>         /* The initial parameter mapping is the complete set of
>   	 template arguments substituted into the declaration.  */
>         args = TI_ARGS (ti);
>         if (inh_ctor_targs)
>   	args = add_outermost_template_args (args, inh_ctor_targs);
> -    }
> -  else
> -    {
> -      /* These should be empty until we allow constraints on non-templates.  */
> -      norm = normalize_nontemplate_requirements (t, info.noisy ());
> +
> +      /* If any arguments depend on template parameters, we can't
> +	 check constraints. Pretend they're satisfied for now.  */
> +      if (uses_template_parms (args))
> +	return boolean_true_node;
>       }
>   
> +  /* Get the normalized constraints.  */
> +  tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
> +
>     unsigned ftc_count = vec_safe_length (failed_type_completions);
>   
>     tree result = boolean_true_node;
> @@ -3111,7 +3070,7 @@ satisfy_declaration_constraints (tree t, sat_info info)
>         if (!push_tinst_level (t))
>   	return result;
>         push_access_scope (t);
> -      result = satisfy_associated_constraints (norm, args, info);
> +      result = satisfy_normalized_constraints (norm, args, info);
>         pop_access_scope (t);
>         pop_tinst_level ();
>       }
> @@ -3134,6 +3093,10 @@ satisfy_declaration_constraints (tree t, sat_info info)
>     return result;
>   }
>   
> +/* Evaluate the associated constraints of the template T using ARGS as the
> +   innermost set of template arguments and according to INFO, returning a
> +   satisfaction value.  */
> +
>   static tree
>   satisfy_declaration_constraints (tree t, tree args, sat_info info)
>   {
> @@ -3144,14 +3107,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
>   
>     args = add_outermost_template_args (t, args);
>   
> +  /* If any arguments depend on template parameters, we can't
> +     check constraints. Pretend they're satisfied for now.  */
> +  if (uses_template_parms (args))
> +    return boolean_true_node;
> +
>     tree result = boolean_true_node;
> -  if (tree norm = normalize_template_requirements (t, info.noisy ()))
> +  if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
>       {
>         if (!push_tinst_level (t, args))
>   	return result;
>         tree pattern = DECL_TEMPLATE_RESULT (t);
>         push_access_scope (pattern);
> -      result = satisfy_associated_constraints (norm, args, info);
> +      result = satisfy_normalized_constraints (norm, args, info);
>         pop_access_scope (pattern);
>         pop_tinst_level ();
>       }
> @@ -3159,62 +3127,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
>     return result;
>   }
>   
> +/* A wrapper around satisfy_declaration_constraints and
> +   satisfy_nondeclaration_constraints which additionally replays
> +   quiet ill-formed satisfaction noisily, so that ill-formed
> +   satisfaction always gets diagnosed.  */
> +
>   static tree
> -constraint_satisfaction_value (tree t, sat_info info)
> +constraint_satisfaction_value (tree t, tree args, sat_info info)
>   {
>     tree r;
>     if (DECL_P (t))
> -    r = satisfy_declaration_constraints (t, info);
> +    {
> +      if (args)
> +	r = satisfy_declaration_constraints (t, args, info);
> +      else
> +	r = satisfy_declaration_constraints (t, info);
> +    }
>     else
> -    r = satisfy_constraint_expression (t, NULL_TREE, info);
> +    r = satisfy_nondeclaration_constraints (t, args, info);
>     if (r == error_mark_node && info.quiet ()
>         && !(DECL_P (t) && TREE_NO_WARNING (t)))
>       {
> -      /* Replay the error with re-normalized requirements.  */
> +      /* Replay the error noisily.  */
>         sat_info noisy (tf_warning_or_error, info.in_decl);
> -      constraint_satisfaction_value (t, noisy);
> -      if (DECL_P (t))
> +      constraint_satisfaction_value (t, args, noisy);
> +      if (DECL_P (t) && !args)
>   	/* Avoid giving these errors again.  */
>   	TREE_NO_WARNING (t) = true;
>       }
>     return r;
>   }
>   
> -static tree
> -constraint_satisfaction_value (tree t, tree args, sat_info info)
> -{
> -  tree r;
> -  if (DECL_P (t))
> -    r = satisfy_declaration_constraints (t, args, info);
> -  else
> -    r = satisfy_constraint_expression (t, args, info);
> -  if (r == error_mark_node && info.quiet ())
> -    {
> -      /* Replay the error with re-normalized requirements.  */
> -      sat_info noisy (tf_warning_or_error, info.in_decl);
> -      constraint_satisfaction_value (t, args, noisy);
> -    }
> -  return r;
> -}
> -
> -/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false
> -   otherwise, even in the case of errors.  */
> -
> -bool
> -constraints_satisfied_p (tree t)
> -{
> -  if (!flag_concepts)
> -    return true;
> -
> -  sat_info quiet (tf_none, NULL_TREE);
> -  return constraint_satisfaction_value (t, quiet) == boolean_true_node;
> -}
> +/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE
> +   and false otherwise, even in the case of errors.
>   
> -/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE
> -    and false otherwise, even in the case of errors.  */
> +   Here, T can be:
> +     - a template declaration
> +     - a template specialization (in which case ARGS must be empty)
> +     - a concept-id (in which case ARGS must be empty)
> +     - a nested-requirement
> +     - a placeholder 'auto'
> +     - a requires-expression.  */
>   
>   bool
> -constraints_satisfied_p (tree t, tree args)
> +constraints_satisfied_p (tree t, tree args/*= NULL_TREE */)
>   {
>     if (!flag_concepts)
>       return true;
> @@ -3227,7 +3183,7 @@ constraints_satisfied_p (tree t, tree args)
>      evaluation of template-ids as id-expressions.  */
>   
>   tree
> -evaluate_concept_check (tree check, tsubst_flags_t complain)
> +evaluate_concept_check (tree check)
>   {
>     if (check == error_mark_node)
>       return error_mark_node;
> @@ -3236,14 +3192,19 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
>   
>     /* Check for satisfaction without diagnostics.  */
>     sat_info quiet (tf_none, NULL_TREE);
> -  tree result = satisfy_constraint_expression (check, NULL_TREE, quiet);
> -  if (result == error_mark_node && (complain & tf_error))
> -    {
> -      /* Replay the error with re-normalized requirements.  */
> -      sat_info noisy (tf_warning_or_error, NULL_TREE);
> -      satisfy_constraint_expression (check, NULL_TREE, noisy);
> -    }
> -  return result;
> +  return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet);
> +}
> +
> +/* Evaluate the requires-expression T, returning either boolean_true_node
> +   or boolean_false_node.  This is used during gimplification and constexpr
> +   evaluation.  */
> +
> +tree
> +evaluate_requires_expr (tree t)
> +{
> +  gcc_assert (TREE_CODE (t) == REQUIRES_EXPR);
> +  sat_info quiet (tf_none, NULL_TREE);
> +  return constraint_satisfaction_value (t, /*args=*/NULL_TREE, quiet);
>   }
>   
>   /*---------------------------------------------------------------------------
> @@ -3709,7 +3670,7 @@ diagnose_nested_requirement (tree req, tree args)
>   {
>     /* Quietly check for satisfaction first.  */
>     sat_info quiet (tf_none, NULL_TREE);
> -  tree result = satisfy_constraint_expression (req, args, quiet);
> +  tree result = satisfy_nondeclaration_constraints (req, args, quiet);
>     if (result == boolean_true_node)
>       return;
>   
> @@ -3721,7 +3682,7 @@ diagnose_nested_requirement (tree req, tree args)
>         inform (loc, "nested requirement %qE is not satisfied, because", expr);
>   
>         sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
> -      satisfy_constraint_expression (req, args, noisy);
> +      satisfy_nondeclaration_constraints (req, args, noisy);
>       }
>     else
>       inform (loc, "nested requirement %qE is not satisfied", expr);
> @@ -3854,7 +3815,7 @@ diagnosing_failed_constraint::replay_errors_p ()
>   }
>   
>   /* Emit diagnostics detailing the failure ARGS to satisfy the constraints
> -   of T. Here, T can be either a constraint or a declaration.  */
> +   of T.  Here, T and ARGS are as in constraints_satisfied_p.  */
>   
>   void
>   diagnose_constraints (location_t loc, tree t, tree args)
> @@ -3866,8 +3827,13 @@ diagnose_constraints (location_t loc, tree t, tree args)
>   
>     /* Replay satisfaction, but diagnose unsatisfaction.  */
>     sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
> -  if (!args)
> -    constraint_satisfaction_value (t, noisy);
> +  if (TREE_CODE (t) == REQUIRES_EXPR)
> +    {
> +      gcc_assert (!args);
> +      ++current_constraint_diagnosis_depth;
> +      diagnose_requires_expr (t, /*map=*/NULL_TREE, /*in_decl=*/NULL_TREE);
> +      --current_constraint_diagnosis_depth;
> +    }
>     else
>       constraint_satisfaction_value (t, args, noisy);
>   
> diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
> index abb8a6ef078..df89ff3815b 100644
> --- a/gcc/cp/cp-gimplify.c
> +++ b/gcc/cp/cp-gimplify.c
> @@ -1381,7 +1381,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
>   	 normal functions.  */
>         if (concept_check_p (stmt))
>   	{
> -	  *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
> +	  *stmt_p = evaluate_concept_check (stmt);
>   	  * walk_subtrees = 0;
>   	  break;
>   	}
> @@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
>   
>       case REQUIRES_EXPR:
>         /* Emit the value of the requires-expression.  */
> -      *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt),
> -				       boolean_type_node);
> +      *stmt_p = evaluate_requires_expr (stmt);
>         *walk_subtrees = 0;
>         break;
>   
>       case TEMPLATE_ID_EXPR:
>         gcc_assert (concept_check_p (stmt));
>         /* Emit the value of the concept check.  */
> -      *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
> +      *stmt_p = evaluate_concept_check (stmt);
>         walk_subtrees = 0;
>         break;
>   
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 544e99538a4..f06ac3c73bf 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -8109,6 +8109,7 @@ extern tree finish_compound_requirement         (location_t, tree, tree, bool);
>   extern tree finish_nested_requirement           (location_t, tree);
>   extern void check_constrained_friend            (tree, tree);
>   extern tree tsubst_requires_expr                (tree, tree, tsubst_flags_t, tree);
> +extern tree evaluate_requires_expr		(tree);
>   extern tree tsubst_constraint                   (tree, tree, tsubst_flags_t, tree);
>   extern tree tsubst_constraint_info              (tree, tree, tsubst_flags_t, tree);
>   extern tree tsubst_parameter_mapping		(tree, tree, tsubst_flags_t, tree);
> @@ -8123,10 +8124,8 @@ struct processing_constraint_expression_sentinel
>   extern bool processing_constraint_expression_p	();
>   
>   extern tree unpack_concept_check		(tree);
> -extern tree evaluate_concept_check              (tree, tsubst_flags_t);
> -extern tree satisfy_constraint_expression	(tree);
> -extern bool constraints_satisfied_p		(tree);
> -extern bool constraints_satisfied_p		(tree, tree);
> +extern tree evaluate_concept_check              (tree);
> +extern bool constraints_satisfied_p		(tree, tree = NULL_TREE);
>   extern bool* lookup_subsumption_result          (tree, tree);
>   extern bool save_subsumption_result             (tree, tree, bool);
>   extern tree find_template_parameters		(tree, tree);
> diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
> index e809f0e4068..3f5467c8283 100644
> --- a/gcc/cp/cvt.c
> +++ b/gcc/cp/cvt.c
> @@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
>     /* Explicitly evaluate void-converted concept checks since their
>        satisfaction may produce ill-formed programs.  */
>      if (concept_check_p (expr))
> -     expr = evaluate_concept_check (expr, tf_warning_or_error);
> +     expr = evaluate_concept_check (expr);
>   
>     if (VOID_TYPE_P (TREE_TYPE (expr)))
>       return expr;
>
diff mbox series

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index cd0a68e9fd6..f940e3e5985 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2257,7 +2257,7 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 {
   /* Handle concept checks separately.  */
   if (concept_check_p (t))
-    return evaluate_concept_check (t, tf_warning_or_error);
+    return evaluate_concept_check (t);
 
   location_t loc = cp_expr_loc_or_input_loc (t);
   tree fun = get_function_named_in_call (t);
@@ -6905,7 +6905,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          '!requires (T t) { ... }' which is not transformed into
          a constraint.  */
       if (!processing_template_decl)
-        return satisfy_constraint_expression (t);
+	return tsubst_requires_expr (t, NULL_TREE, tf_none, NULL_TREE);
       else
         *non_constant_p = true;
       return t;
@@ -6941,7 +6941,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 
 	if (!processing_template_decl
 	    && !uid_sensitive_constexpr_evaluation_p ())
-	  r = evaluate_concept_check (t, tf_warning_or_error);
+	  r = evaluate_concept_check (t);
 	else
 	  *non_constant_p = true;
 
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 2b61ad8d9ea..cf319b34da0 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -107,10 +107,9 @@  struct subst_info
    a constraint is not satisfied.
 
    The entrypoints to satisfaction for which we set noisy+unsat are
-   diagnose_constraints and diagnose_nested_requirement.  The entrypoints for
-   which we set noisy-unsat are the replays inside constraint_satisfaction_value,
-   evaluate_concept_check and tsubst_nested_requirement.  In other entrypoints,
-   e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags
+   diagnose_constraints and diagnose_nested_requirement.  The entrypoint for
+   which we set noisy-unsat is the replay inside constraint_satisfaction_value.
+   From constraints_satisfied_p, we enter satisfaction quietly (both flags
    cleared).  */
 
 struct sat_info : subst_info
@@ -133,7 +132,7 @@  struct sat_info : subst_info
   bool diagnose_unsatisfaction;
 };
 
-static tree satisfy_constraint_expression (tree, tree, sat_info);
+static tree constraint_satisfaction_value (tree, tree, sat_info);
 
 /* True if T is known to be some type other than bool. Note that this
    is false for dependent types and errors.  */
@@ -958,22 +957,6 @@  normalize_concept_definition (tree tmpl, bool diag = false)
   return norm;
 }
 
-/* Returns the normal form of TMPL's requirements.  */
-
-static tree
-normalize_template_requirements (tree tmpl, bool diag = false)
-{
-  return get_normalized_constraints_from_decl (tmpl, diag);
-}
-
-/* Returns the normal form of TMPL's requirements.  */
-
-static tree
-normalize_nontemplate_requirements (tree decl, bool diag = false)
-{
-  return get_normalized_constraints_from_decl (decl, diag);
-}
-
 /* Normalize an EXPR as a constraint.  */
 
 static tree
@@ -2068,15 +2051,8 @@  tsubst_compound_requirement (tree t, tree args, subst_info info)
 static tree
 tsubst_nested_requirement (tree t, tree args, subst_info info)
 {
-  /* Perform satisfaction quietly first.  */
   sat_info quiet (tf_none, info.in_decl);
-  tree result = satisfy_constraint_expression (t, args, quiet);
-  if (result == error_mark_node)
-    {
-      /* Replay the error.  */
-      sat_info noisy (tf_warning_or_error, info.in_decl);
-      satisfy_constraint_expression (t, args, noisy);
-    }
+  tree result = constraint_satisfaction_value (t, args, quiet);
   if (result != boolean_true_node)
     return error_mark_node;
   return boolean_true_node;
@@ -2459,7 +2435,7 @@  struct sat_hasher : ggc_ptr_hash<sat_entry>
 /* Cache the result of satisfy_atom.  */
 static GTY((deletable)) hash_table<sat_hasher> *sat_cache;
 
-/* Cache the result of constraint_satisfaction_value.  */
+/* Cache the result of satisfy_declaration_constraints.  */
 static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache;
 
 /* A tool used by satisfy_atom to help manage satisfaction caching and to
@@ -2941,7 +2917,7 @@  satisfy_constraint_r (tree t, tree args, sat_info info)
 /* Check that the normalized constraint T is satisfied for ARGS.  */
 
 static tree
-satisfy_constraint (tree t, tree args, sat_info info)
+satisfy_normalized_constraints (tree t, tree args, sat_info info)
 {
   auto_timevar time (TV_CONSTRAINT_SAT);
 
@@ -2957,24 +2933,6 @@  satisfy_constraint (tree t, tree args, sat_info info)
   return satisfy_constraint_r (t, args, info);
 }
 
-/* Check the normalized constraints T against ARGS, returning a satisfaction
-   value (either true, false, or error).  */
-
-static tree
-satisfy_associated_constraints (tree t, tree args, sat_info info)
-{
-  /* If there are no constraints then this is trivially satisfied.  */
-  if (!t)
-    return boolean_true_node;
-
-  /* If any arguments depend on template parameters, we can't
-     check constraints. Pretend they're satisfied for now.  */
-  if (args && uses_template_parms (args))
-    return boolean_true_node;
-
-  return satisfy_constraint (t, args, info);
-}
-
 /* Return the normal form of the constraints on the placeholder 'auto'
    type T.  */
 
@@ -3005,19 +2963,20 @@  normalize_placeholder_type_constraints (tree t, bool diag)
   return normalize_constraint_expression (constr, info);
 }
 
-/* Evaluate EXPR as a constraint expression using ARGS, returning a
-   satisfaction value. */
+/* Evaluate the constraints of T using ARGS, returning a satisfaction value.
+   Here, T can be a concept-id, nested-requirement or placeholder 'auto'.  */
 
 static tree
-satisfy_constraint_expression (tree t, tree args, sat_info info)
+satisfy_nondeclaration_constraints (tree t, tree args, sat_info info)
 {
   if (t == error_mark_node)
     return error_mark_node;
 
   /* Get the normalized constraints.  */
   tree norm;
-  if (args == NULL_TREE && concept_check_p (t))
+  if (concept_check_p (t))
     {
+      gcc_assert (!args);
       tree id = unpack_concept_check (t);
       args = TREE_OPERAND (id, 1);
       tree tmpl = get_concept_check_template (id);
@@ -3032,11 +2991,6 @@  satisfy_constraint_expression (tree t, tree args, sat_info info)
       ninfo.initial_parms = TREE_TYPE (t);
       norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo);
     }
-  else if (EXPR_P (t))
-    {
-      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 ());
@@ -3047,23 +3001,16 @@  satisfy_constraint_expression (tree t, tree args, sat_info info)
     gcc_unreachable ();
 
   /* Perform satisfaction.  */
-  return satisfy_constraint (norm, args, info);
+  return satisfy_normalized_constraints (norm, args, info);
 }
 
-/* Used only to evaluate requires-expressions during constant expression
-   evaluation.  */
-
-tree
-satisfy_constraint_expression (tree expr)
-{
-  sat_info info (tf_none, NULL_TREE);
-  return satisfy_constraint_expression (expr, NULL_TREE, info);
-}
+/* Evaluate the associated constraints of the template specialization T
+   according to INFO, returning a satisfaction value.  */
 
 static tree
 satisfy_declaration_constraints (tree t, sat_info info)
 {
-  gcc_assert (DECL_P (t));
+  gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL);
   const tree saved_t = t;
 
   /* For inherited constructors, consider the original declaration;
@@ -3083,26 +3030,24 @@  satisfy_declaration_constraints (tree t, sat_info info)
     if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t))
       return *result;
 
-  /* Get the normalized constraints.  */
-  tree norm = NULL_TREE;
   tree args = NULL_TREE;
   if (tree ti = DECL_TEMPLATE_INFO (t))
     {
-      tree tmpl = TI_TEMPLATE (ti);
-      norm = normalize_template_requirements (tmpl, info.noisy ());
-
       /* The initial parameter mapping is the complete set of
 	 template arguments substituted into the declaration.  */
       args = TI_ARGS (ti);
       if (inh_ctor_targs)
 	args = add_outermost_template_args (args, inh_ctor_targs);
-    }
-  else
-    {
-      /* These should be empty until we allow constraints on non-templates.  */
-      norm = normalize_nontemplate_requirements (t, info.noisy ());
+
+      /* If any arguments depend on template parameters, we can't
+	 check constraints. Pretend they're satisfied for now.  */
+      if (uses_template_parms (args))
+	return boolean_true_node;
     }
 
+  /* Get the normalized constraints.  */
+  tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
+
   unsigned ftc_count = vec_safe_length (failed_type_completions);
 
   tree result = boolean_true_node;
@@ -3111,7 +3056,7 @@  satisfy_declaration_constraints (tree t, sat_info info)
       if (!push_tinst_level (t))
 	return result;
       push_access_scope (t);
-      result = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_normalized_constraints (norm, args, info);
       pop_access_scope (t);
       pop_tinst_level ();
     }
@@ -3134,6 +3079,10 @@  satisfy_declaration_constraints (tree t, sat_info info)
   return result;
 }
 
+/* Evaluate the associated constraints of the template T using ARGS as the
+   innermost set of template arguments and according to INFO, returning a
+   satisfaction value.  */
+
 static tree
 satisfy_declaration_constraints (tree t, tree args, sat_info info)
 {
@@ -3144,14 +3093,19 @@  satisfy_declaration_constraints (tree t, tree args, sat_info info)
 
   args = add_outermost_template_args (t, args);
 
+  /* If any arguments depend on template parameters, we can't
+     check constraints. Pretend they're satisfied for now.  */
+  if (uses_template_parms (args))
+    return boolean_true_node;
+
   tree result = boolean_true_node;
-  if (tree norm = normalize_template_requirements (t, info.noisy ()))
+  if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
     {
       if (!push_tinst_level (t, args))
 	return result;
       tree pattern = DECL_TEMPLATE_RESULT (t);
       push_access_scope (pattern);
-      result = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_normalized_constraints (norm, args, info);
       pop_access_scope (pattern);
       pop_tinst_level ();
     }
@@ -3159,62 +3113,50 @@  satisfy_declaration_constraints (tree t, tree args, sat_info info)
   return result;
 }
 
+/* A wrapper around satisfy_declaration_constraints and
+   satisfy_nondeclaration_constraints which additionally replays
+   quiet ill-formed satisfaction noisily, so that ill-formed
+   satisfaction always gets diagnosed.  */
+
 static tree
-constraint_satisfaction_value (tree t, sat_info info)
+constraint_satisfaction_value (tree t, tree args, sat_info info)
 {
   tree r;
   if (DECL_P (t))
-    r = satisfy_declaration_constraints (t, info);
+    {
+      if (args)
+	r = satisfy_declaration_constraints (t, args, info);
+      else
+	r = satisfy_declaration_constraints (t, info);
+    }
   else
-    r = satisfy_constraint_expression (t, NULL_TREE, info);
+    r = satisfy_nondeclaration_constraints (t, args, info);
   if (r == error_mark_node && info.quiet ()
       && !(DECL_P (t) && TREE_NO_WARNING (t)))
     {
-      /* Replay the error with re-normalized requirements.  */
+      /* Replay the error noisily.  */
       sat_info noisy (tf_warning_or_error, info.in_decl);
-      constraint_satisfaction_value (t, noisy);
-      if (DECL_P (t))
+      constraint_satisfaction_value (t, args, noisy);
+      if (DECL_P (t) && !args)
 	/* Avoid giving these errors again.  */
 	TREE_NO_WARNING (t) = true;
     }
   return r;
 }
 
-static tree
-constraint_satisfaction_value (tree t, tree args, sat_info info)
-{
-  tree r;
-  if (DECL_P (t))
-    r = satisfy_declaration_constraints (t, args, info);
-  else
-    r = satisfy_constraint_expression (t, args, info);
-  if (r == error_mark_node && info.quiet ())
-    {
-      /* Replay the error with re-normalized requirements.  */
-      sat_info noisy (tf_warning_or_error, info.in_decl);
-      constraint_satisfaction_value (t, args, noisy);
-    }
-  return r;
-}
-
-/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false
-   otherwise, even in the case of errors.  */
+/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE
+   and false otherwise, even in the case of errors.
 
-bool
-constraints_satisfied_p (tree t)
-{
-  if (!flag_concepts)
-    return true;
-
-  sat_info quiet (tf_none, NULL_TREE);
-  return constraint_satisfaction_value (t, quiet) == boolean_true_node;
-}
-
-/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE
-    and false otherwise, even in the case of errors.  */
+   Here, T can be:
+     - a template declaration (in which case ARGS is an innermost template
+       argument set for T)
+     - a template specialization (in which case ARGS must be empty)
+     - a concept-id (in which case ARGS must be empty)
+     - a nested-requirement (in which case ARGS is a complete argument set)
+     - a placeholder 'auto' (in which case ARGS is a complete argument set).  */
 
 bool
-constraints_satisfied_p (tree t, tree args)
+constraints_satisfied_p (tree t, tree args/*= NULL_TREE */)
 {
   if (!flag_concepts)
     return true;
@@ -3227,7 +3169,7 @@  constraints_satisfied_p (tree t, tree args)
    evaluation of template-ids as id-expressions.  */
 
 tree
-evaluate_concept_check (tree check, tsubst_flags_t complain)
+evaluate_concept_check (tree check)
 {
   if (check == error_mark_node)
     return error_mark_node;
@@ -3236,14 +3178,7 @@  evaluate_concept_check (tree check, tsubst_flags_t complain)
 
   /* Check for satisfaction without diagnostics.  */
   sat_info quiet (tf_none, NULL_TREE);
-  tree result = satisfy_constraint_expression (check, NULL_TREE, quiet);
-  if (result == error_mark_node && (complain & tf_error))
-    {
-      /* Replay the error with re-normalized requirements.  */
-      sat_info noisy (tf_warning_or_error, NULL_TREE);
-      satisfy_constraint_expression (check, NULL_TREE, noisy);
-    }
-  return result;
+  return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet);
 }
 
 /*---------------------------------------------------------------------------
@@ -3709,7 +3644,7 @@  diagnose_nested_requirement (tree req, tree args)
 {
   /* Quietly check for satisfaction first.  */
   sat_info quiet (tf_none, NULL_TREE);
-  tree result = satisfy_constraint_expression (req, args, quiet);
+  tree result = satisfy_nondeclaration_constraints (req, args, quiet);
   if (result == boolean_true_node)
     return;
 
@@ -3721,7 +3656,7 @@  diagnose_nested_requirement (tree req, tree args)
       inform (loc, "nested requirement %qE is not satisfied, because", expr);
 
       sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
-      satisfy_constraint_expression (req, args, noisy);
+      satisfy_nondeclaration_constraints (req, args, noisy);
     }
   else
     inform (loc, "nested requirement %qE is not satisfied", expr);
@@ -3854,7 +3789,9 @@  diagnosing_failed_constraint::replay_errors_p ()
 }
 
 /* Emit diagnostics detailing the failure ARGS to satisfy the constraints
-   of T. Here, T can be either a constraint or a declaration.  */
+   of T.  Here, T and ARGS are as in constraints_satisfied_p, except that T
+   can also be a REQUIRES_EXPR (in which case ARGS is must be empty).  The
+   latter is used by finish_static_assert to diagnose a false REQUIRES_EXPR.  */
 
 void
 diagnose_constraints (location_t loc, tree t, tree args)
@@ -3866,8 +3803,13 @@  diagnose_constraints (location_t loc, tree t, tree args)
 
   /* Replay satisfaction, but diagnose unsatisfaction.  */
   sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
-  if (!args)
-    constraint_satisfaction_value (t, noisy);
+  if (TREE_CODE (t) == REQUIRES_EXPR)
+    {
+      gcc_assert (!args);
+      ++current_constraint_diagnosis_depth;
+      diagnose_requires_expr (t, /*map=*/NULL_TREE, /*in_decl=*/NULL_TREE);
+      --current_constraint_diagnosis_depth;
+    }
   else
     constraint_satisfaction_value (t, args, noisy);
 
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index abb8a6ef078..64b1dc1b433 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1381,7 +1381,7 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	 normal functions.  */
       if (concept_check_p (stmt))
 	{
-	  *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+	  *stmt_p = evaluate_concept_check (stmt);
 	  * walk_subtrees = 0;
 	  break;
 	}
@@ -1453,15 +1453,14 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 
     case REQUIRES_EXPR:
       /* Emit the value of the requires-expression.  */
-      *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt),
-				       boolean_type_node);
+      *stmt_p = tsubst_requires_expr (stmt, NULL_TREE, tf_none, NULL_TREE);
       *walk_subtrees = 0;
       break;
 
     case TEMPLATE_ID_EXPR:
       gcc_assert (concept_check_p (stmt));
       /* Emit the value of the concept check.  */
-      *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+      *stmt_p = evaluate_concept_check (stmt);
       walk_subtrees = 0;
       break;
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 544e99538a4..995e13f4a6e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8123,10 +8123,8 @@  struct processing_constraint_expression_sentinel
 extern bool processing_constraint_expression_p	();
 
 extern tree unpack_concept_check		(tree);
-extern tree evaluate_concept_check              (tree, tsubst_flags_t);
-extern tree satisfy_constraint_expression	(tree);
-extern bool constraints_satisfied_p		(tree);
-extern bool constraints_satisfied_p		(tree, tree);
+extern tree evaluate_concept_check              (tree);
+extern bool constraints_satisfied_p		(tree, tree = NULL_TREE);
 extern bool* lookup_subsumption_result          (tree, tree);
 extern bool save_subsumption_result             (tree, tree, bool);
 extern tree find_template_parameters		(tree, tree);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index e809f0e4068..3f5467c8283 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1170,7 +1170,7 @@  convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
   /* Explicitly evaluate void-converted concept checks since their
      satisfaction may produce ill-formed programs.  */
    if (concept_check_p (expr))
-     expr = evaluate_concept_check (expr, tf_warning_or_error);
+     expr = evaluate_concept_check (expr);
 
   if (VOID_TYPE_P (TREE_TYPE (expr)))
     return expr;