diff mbox series

[1/2,v2] c++: Distinguish unsatisfaction vs errors during satisfaction [PR97093]

Message ID 20201204213301.1836322-1-ppalka@redhat.com
State New
Headers show
Series [1/2,v2] c++: Distinguish unsatisfaction vs errors during satisfaction [PR97093] | expand

Commit Message

Patrick Palka Dec. 4, 2020, 9:33 p.m. UTC
During satisfaction, the flag info.noisy() controls three things:
whether to diagnose ill-formed satisfaction (such as the satisfaction
value of an atom being non-bool or non-constant); whether to diagnose
unsatisfaction; and whether to bypass the satisfaction cache.

The flag turns out to be too coarse however, because in some cases we
want to diagnose ill-formed satisfaction (and bypass the satisfaction
cache) but not diagnose unsatisfaction, for instance when replaying an
erroneous satisfaction result from constraint_satisfaction_value,
evaluate_concept_check and tsubst_nested_requirement.

And when noisily evaluating a disjunction, we want to first evaluate its
branches noisily (bypassing the satisfaction cache) but suppress
unsatisfaction diagnostics.  We currently work around this by instead
first evaluating each branch quietly, but that means the recursive calls
to satisfy_atom will use the satisfaction cache.

To fix this, this patch adds the info.diagnose_unsatisfaction_p() flag,
which refines the info.noisy() flag as part of a new sat_info class that
derives from subst_info.  During satisfaction, info.noisy() now controls
whether to diagnose ill-formed satisfaction, and
info.diagnose_unsatisfaction_p() controls whether to additionally
diagnose unsatisfaction.  This enables us to address the above two
issues straightforwardly.

Incidentally, the change to satisfy_disjunction suppresses the ICE in
the PR97093 testcase because we no longer insert atoms into the
satisfaction cache that have been incorrectly re-normalized in
diagnose_nested_requirement (after losing the necessary template
context).  But the underlying re-normalization issue remains, and will
be fixed in a subsequent patch.

gcc/cp/ChangeLog:

	PR c++/97093
	* constraint.cc (struct sat_info): Define.
	(tsubst_nested_requirement): Pass a sat_info object to
	satisfy_constraint.
	(satisfy_constraint_r): Take a sat_info argument instead of
	subst_info.
	(satisfy_conjunction): Likewise.
	(satisfy_disjunction): Likewise.  Instead of first evaluating
	each branch quietly, evaluate each branch only with
	unsatisfaction diagnostics disabled.  Exit early if evaluation
	of a branch returns error_mark_node.
	(satisfy_atom): Take a sat_info argument instead of subst_info.
	Fix a comment.  Check diagnose_unsatisfaction_p() instead of
	noisy() before replaying a substitution failure.
	(satisfy_constraint): Take a sat_info argument instead of
	subst_info.
	(satisfy_associated_constraints): Likewise.
	(satisfy_constraint_expression): Likewise.
	(satisfy_declaration_constraints): Likewise.
	(constraint_satisfaction_value): Likewise and adjust
	accordingly.  Fix formatting.
	(constraints_satisfied_p): Pass a sat_info object to
	constraint_satisfaction_value.
	(evaluate_concept_check): Pass a sat_info object to
	satisfy_constraint_expression.
	(diagnose_nested_requirement): Likewise.
	(diagnose_constraints): Pass an appropriate sat_info object to
	constraint_satisfaction_value.

gcc/testsuite/ChangeLog:

	PR c++/97093
	* g++.dg/concepts/pr94252.C: Verify we no longer issue a
	spurious satisfaction failure note when diagnosing ill-formed
	satisfaction.
	* g++.dg/cpp2a/concepts-requires18.C: No longer expect a
	spurious satisfaction failure diagnostic when immediately
	evaluating the nested-requirement subst<void&> of a
	requires-expression that appears outside of a template.
	* g++.dg/cpp2a/concepts-requires21.C: Verify we no longer issue
	a spurious satisfaction failure note when immediately evaluating
	a nested-requirement of a requires-expression that appears
	outside of a template.
	* g++.dg/cpp2a/concepts-nonbool3.C: New test.
	* g++.dg/cpp2a/concepts-pr97093.C: New test.
---
 gcc/cp/constraint.cc                          | 149 +++++++++++-------
 gcc/testsuite/g++.dg/concepts/pr94252.C       |   1 +
 .../g++.dg/cpp2a/concepts-nonbool3.C          |   5 +
 .../g++.dg/cpp2a/concepts-requires18.C        |   2 +-
 .../g++.dg/cpp2a/concepts-requires21.C        |   1 +
 5 files changed, 104 insertions(+), 54 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C

Comments

Jason Merrill Dec. 4, 2020, 10:41 p.m. UTC | #1
On 12/4/20 4:33 PM, Patrick Palka wrote:
> I've convinced myself to do away with the whole diagnose_requires_expr /
> tsubst_requires_expr consolidation, since that part is just a pure
> refactoring change and the added overloadedness of the flags is not
> ideal.  This simplifies the patch considerably.

Does dropping that reduce the overloadedness?  I'm always in favor of 
reducing code duplication.  But it certainly makes sense for that to 
happen in a separate patch.

> During satisfaction, the flag info.noisy() controls three things:
> whether to diagnose ill-formed satisfaction (such as the satisfaction
> value of an atom being non-bool or non-constant); whether to diagnose
> unsatisfaction; and whether to bypass the satisfaction cache.
> 
> The flag turns out to be too coarse however, because in some cases we
> want to diagnose ill-formed satisfaction (and bypass the satisfaction
> cache) but not diagnose unsatisfaction, for instance when replaying an
> erroneous satisfaction result from constraint_satisfaction_value,
> evaluate_concept_check and tsubst_nested_requirement.
> 
> And when noisily evaluating a disjunction, we want to first evaluate its
> branches noisily (bypassing the satisfaction cache) but suppress
> unsatisfaction diagnostics.  We currently work around this by instead
> first evaluating each branch quietly, but that means the recursive calls
> to satisfy_atom will use the satisfaction cache.
> 
> To fix this, this patch adds the info.diagnose_unsatisfaction_p() flag,
> which refines the info.noisy() flag as part of a new sat_info class that
> derives from subst_info.  During satisfaction, info.noisy() now controls
> whether to diagnose ill-formed satisfaction, and
> info.diagnose_unsatisfaction_p() controls whether to additionally
> diagnose unsatisfaction.  This enables us to address the above two
> issues straightforwardly.
> 
> Incidentally, the change to satisfy_disjunction suppresses the ICE in
> the PR97093 testcase because we no longer insert atoms into the
> satisfaction cache that have been incorrectly re-normalized in
> diagnose_nested_requirement (after losing the necessary template
> context).  But the underlying re-normalization issue remains, and will
> be fixed in a subsequent patch.
> 
> gcc/cp/ChangeLog:
> 
> 	PR c++/97093
> 	* constraint.cc (struct sat_info): Define.
> 	(tsubst_nested_requirement): Pass a sat_info object to
> 	satisfy_constraint.
> 	(satisfy_constraint_r): Take a sat_info argument instead of
> 	subst_info.
> 	(satisfy_conjunction): Likewise.
> 	(satisfy_disjunction): Likewise.  Instead of first evaluating
> 	each branch quietly, evaluate each branch only with
> 	unsatisfaction diagnostics disabled.  Exit early if evaluation
> 	of a branch returns error_mark_node.
> 	(satisfy_atom): Take a sat_info argument instead of subst_info.
> 	Fix a comment.  Check diagnose_unsatisfaction_p() instead of
> 	noisy() before replaying a substitution failure.
> 	(satisfy_constraint): Take a sat_info argument instead of
> 	subst_info.
> 	(satisfy_associated_constraints): Likewise.
> 	(satisfy_constraint_expression): Likewise.
> 	(satisfy_declaration_constraints): Likewise.
> 	(constraint_satisfaction_value): Likewise and adjust
> 	accordingly.  Fix formatting.
> 	(constraints_satisfied_p): Pass a sat_info object to
> 	constraint_satisfaction_value.
> 	(evaluate_concept_check): Pass a sat_info object to
> 	satisfy_constraint_expression.
> 	(diagnose_nested_requirement): Likewise.
> 	(diagnose_constraints): Pass an appropriate sat_info object to
> 	constraint_satisfaction_value.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/97093
> 	* g++.dg/concepts/pr94252.C: Verify we no longer issue a
> 	spurious satisfaction failure note when diagnosing ill-formed
> 	satisfaction.
> 	* g++.dg/cpp2a/concepts-requires18.C: No longer expect a
> 	spurious satisfaction failure diagnostic when immediately
> 	evaluating the nested-requirement subst<void&> of a
> 	requires-expression that appears outside of a template.
> 	* g++.dg/cpp2a/concepts-requires21.C: Verify we no longer issue
> 	a spurious satisfaction failure note when immediately evaluating
> 	a nested-requirement of a requires-expression that appears
> 	outside of a template.
> 	* g++.dg/cpp2a/concepts-nonbool3.C: New test.
> 	* g++.dg/cpp2a/concepts-pr97093.C: New test.
> ---
>   gcc/cp/constraint.cc                          | 149 +++++++++++-------
>   gcc/testsuite/g++.dg/concepts/pr94252.C       |   1 +
>   .../g++.dg/cpp2a/concepts-nonbool3.C          |   5 +
>   .../g++.dg/cpp2a/concepts-requires18.C        |   2 +-
>   .../g++.dg/cpp2a/concepts-requires21.C        |   1 +
>   5 files changed, 104 insertions(+), 54 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 7f02aa0a215..2be1a841535 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -98,7 +98,35 @@ struct subst_info
>     tree in_decl;
>   };
>   
> -static tree satisfy_constraint (tree, tree, subst_info);
> +/* Provides additional context for satisfaction.
> +
> +   The flag noisy() controls whether to diagnose ill-formed satisfaction,
> +   such as the satisfaction value of an atom being non-bool or non-constant.
> +
> +   The flag diagnose_unsatisfaction_p(), which implies noisy(), controls
> +   whether to explain why a constraint is not satisfied.  */

Please also mention the circumstances for these flags being on.

> +struct sat_info : subst_info
> +{
> +  sat_info (tsubst_flags_t cmp, tree in, bool diagnose_unsat = false)
> +    : subst_info (cmp, in), diagnose_unsatisfaction (diagnose_unsat)
> +  {
> +    if (diagnose_unsatisfaction_p ())
> +      gcc_checking_assert (noisy ());
> +  }
> +
> +  /* True if we should diagnose the cause of satisfaction failure.
> +     Implies noisy().  */
> +  bool
> +  diagnose_unsatisfaction_p () const
> +  {
> +    return diagnose_unsatisfaction;
> +  }
> +
> +  bool diagnose_unsatisfaction;
> +};
> +
> +static tree satisfy_constraint (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.  */
> @@ -2059,10 +2087,11 @@ tsubst_nested_requirement (tree t, tree args, subst_info info)
>   {
>     /* Ensure that we're in an evaluation context prior to satisfaction.  */
>     tree norm = TREE_TYPE (t);
> -  tree result = satisfy_constraint (norm, args, info);
> +  tree result = satisfy_constraint (norm, args,
> +				    sat_info (info.complain, info.in_decl));

Maybe add a constructor sat_info(const subst_info &)?  OK either way 
(with the above comment change).

>     if (result == error_mark_node && info.quiet ())
>       {
> -      subst_info noisy (tf_warning_or_error, info.in_decl);
> +      sat_info noisy (tf_warning_or_error, info.in_decl);
>         satisfy_constraint (norm, args, noisy);
>       }
>     if (result != boolean_true_node)
> @@ -2508,12 +2537,12 @@ tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>     return expr;
>   }
>   
> -static tree satisfy_constraint_r (tree, tree, subst_info info);
> +static tree satisfy_constraint_r (tree, tree, sat_info info);
>   
>   /* Compute the satisfaction of a conjunction.  */
>   
>   static tree
> -satisfy_conjunction (tree t, tree args, subst_info info)
> +satisfy_conjunction (tree t, tree args, sat_info info)
>   {
>     tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, info);
>     if (lhs == error_mark_node || lhs == boolean_false_node)
> @@ -2567,20 +2596,25 @@ collect_operands_of_disjunction (tree t, auto_vec<tree_pair> *operands)
>   /* Compute the satisfaction of a disjunction.  */
>   
>   static tree
> -satisfy_disjunction (tree t, tree args, subst_info info)
> +satisfy_disjunction (tree t, tree args, sat_info info)
>   {
> -  /* Evaluate the operands quietly.  */
> -  subst_info quiet (tf_none, NULL_TREE);
> +  /* Evaluate each operand with unsatisfaction diagnostics disabled.  */
> +  sat_info sub = info;
> +  sub.diagnose_unsatisfaction = false;
>   
> -  /* Register the constraint for diagnostics, if needed.  */
> -  diagnosing_failed_constraint failure (t, args, info.noisy ());
> +  tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, sub);
> +  if (lhs == boolean_true_node || lhs == error_mark_node)
> +    return lhs;
>   
> -  tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, quiet);
> -  if (lhs == boolean_true_node)
> -    return boolean_true_node;
> -  tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, quiet);
> -  if (rhs != boolean_true_node && info.noisy ())
> +  tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, sub);
> +  if (rhs == boolean_true_node || rhs == error_mark_node)
> +    return rhs;
> +
> +  /* Both branches evaluated to false.  Explain the satisfaction failure in
> +     each branch.  */
> +  if (info.diagnose_unsatisfaction_p ())
>       {
> +      diagnosing_failed_constraint failure (t, args, info.noisy ());
>         cp_expr disj_expr = CONSTR_EXPR (t);
>         inform (disj_expr.get_location (),
>   	      "no operand of the disjunction is satisfied");
> @@ -2601,7 +2635,8 @@ satisfy_disjunction (tree t, tree args, subst_info info)
>   	    }
>   	}
>       }
> -  return rhs;
> +
> +  return boolean_false_node;
>   }
>   
>   /* Ensures that T is a truth value and not (accidentally, as sometimes
> @@ -2682,7 +2717,7 @@ static void diagnose_atomic_constraint (tree, tree, tree, subst_info);
>   /* Compute the satisfaction of an atomic constraint.  */
>   
>   static tree
> -satisfy_atom (tree t, tree args, subst_info info)
> +satisfy_atom (tree t, tree args, sat_info info)
>   {
>     satisfaction_cache cache (t, args, info.complain);
>     if (tree r = cache.get ())
> @@ -2700,9 +2735,9 @@ satisfy_atom (tree t, tree args, subst_info info)
>     tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet);
>     if (map == error_mark_node)
>       {
> -      /* If instantiation of the parameter mapping fails, the program
> -         is ill-formed.  */
> -      if (info.noisy())
> +      /* If instantiation of the parameter mapping fails, the constraint is
> +	 not satisfied.  Replay the substitution.  */
> +      if (info.diagnose_unsatisfaction_p ())
>   	tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, info);
>         return cache.save (boolean_false_node);
>       }
> @@ -2729,7 +2764,7 @@ satisfy_atom (tree t, tree args, subst_info info)
>       {
>         /* If substitution results in an invalid type or expression, the constraint
>   	 is not satisfied. Replay the substitution.  */
> -      if (info.noisy ())
> +      if (info.diagnose_unsatisfaction_p ())
>   	tsubst_expr (expr, args, info.complain, info.in_decl, false);
>         return cache.save (inst_cache.save (boolean_false_node));
>       }
> @@ -2757,7 +2792,7 @@ satisfy_atom (tree t, tree args, subst_info info)
>   	result = error_mark_node;
>       }
>     result = satisfaction_value (result);
> -  if (result == boolean_false_node && info.noisy ())
> +  if (result == boolean_false_node && info.diagnose_unsatisfaction_p ())
>       diagnose_atomic_constraint (t, map, result, info);
>   
>     return cache.save (inst_cache.save (result));
> @@ -2775,7 +2810,7 @@ satisfy_atom (tree t, tree args, subst_info info)
>      constraint only matters for subsumption.  */
>   
>   static tree
> -satisfy_constraint_r (tree t, tree args, subst_info info)
> +satisfy_constraint_r (tree t, tree args, sat_info info)
>   {
>     if (t == error_mark_node)
>       return error_mark_node;
> @@ -2796,7 +2831,7 @@ satisfy_constraint_r (tree t, tree args, subst_info info)
>   /* Check that the normalized constraint T is satisfied for ARGS.  */
>   
>   static tree
> -satisfy_constraint (tree t, tree args, subst_info info)
> +satisfy_constraint (tree t, tree args, sat_info info)
>   {
>     auto_timevar time (TV_CONSTRAINT_SAT);
>   
> @@ -2814,7 +2849,7 @@ satisfy_constraint (tree t, tree args, subst_info info)
>      value (either true, false, or error).  */
>   
>   static tree
> -satisfy_associated_constraints (tree t, tree args, subst_info info)
> +satisfy_associated_constraints (tree t, tree args, sat_info info)
>   {
>     /* If there are no constraints then this is trivially satisfied.  */
>     if (!t)
> @@ -2832,7 +2867,7 @@ satisfy_associated_constraints (tree t, tree args, subst_info info)
>      satisfaction value. */
>   
>   static tree
> -satisfy_constraint_expression (tree t, tree args, subst_info info)
> +satisfy_constraint_expression (tree t, tree args, sat_info info)
>   {
>     if (t == error_mark_node)
>       return error_mark_node;
> @@ -2861,12 +2896,12 @@ satisfy_constraint_expression (tree t, tree args, subst_info info)
>   tree
>   satisfy_constraint_expression (tree expr)
>   {
> -  subst_info info (tf_none, NULL_TREE);
> +  sat_info info (tf_none, NULL_TREE);
>     return satisfy_constraint_expression (expr, NULL_TREE, info);
>   }
>   
>   static tree
> -satisfy_declaration_constraints (tree t, subst_info info)
> +satisfy_declaration_constraints (tree t, sat_info info)
>   {
>     gcc_assert (DECL_P (t));
>     const tree saved_t = t;
> @@ -2926,7 +2961,7 @@ satisfy_declaration_constraints (tree t, subst_info info)
>   }
>   
>   static tree
> -satisfy_declaration_constraints (tree t, tree args, subst_info info)
> +satisfy_declaration_constraints (tree t, tree args, sat_info info)
>   {
>     /* Update the declaration for diagnostics.  */
>     info.in_decl = t;
> @@ -2951,9 +2986,8 @@ satisfy_declaration_constraints (tree t, tree args, subst_info info)
>   }
>   
>   static tree
> -constraint_satisfaction_value (tree t, tsubst_flags_t complain)
> +constraint_satisfaction_value (tree t, sat_info info)
>   {
> -  subst_info info (complain, NULL_TREE);
>     tree r;
>     if (DECL_P (t))
>       r = satisfy_declaration_constraints (t, info);
> @@ -2961,26 +2995,31 @@ constraint_satisfaction_value (tree t, tsubst_flags_t complain)
>       r = satisfy_constraint_expression (t, NULL_TREE, info);
>     if (r == error_mark_node && info.quiet ()
>         && !(DECL_P (t) && TREE_NO_WARNING (t)))
> -      {
> -	constraint_satisfaction_value (t, tf_warning_or_error);
> -	if (DECL_P (t))
> -	  /* Avoid giving these errors again.  */
> -	  TREE_NO_WARNING (t) = true;
> -      }
> +    {
> +      /* Replay the error with re-normalized requirements.  */
> +      sat_info noisy (tf_warning_or_error, info.in_decl);
> +      constraint_satisfaction_value (t, noisy);
> +      if (DECL_P (t))
> +	/* Avoid giving these errors again.  */
> +	TREE_NO_WARNING (t) = true;
> +    }
>     return r;
>   }
>   
>   static tree
> -constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain)
> +constraint_satisfaction_value (tree t, tree args, sat_info info)
>   {
> -  subst_info info (complain, NULL_TREE);
>     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 ())
> -    constraint_satisfaction_value (t, args, tf_warning_or_error);
> +    {
> +      /* 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;
>   }
>   
> @@ -2993,7 +3032,8 @@ constraints_satisfied_p (tree t)
>     if (!flag_concepts)
>       return true;
>   
> -  return constraint_satisfaction_value (t, tf_none) == boolean_true_node;
> +  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
> @@ -3005,7 +3045,8 @@ constraints_satisfied_p (tree t, tree args)
>     if (!flag_concepts)
>       return true;
>   
> -  return constraint_satisfaction_value (t, args, tf_none) == boolean_true_node;
> +  sat_info quiet (tf_none, NULL_TREE);
> +  return constraint_satisfaction_value (t, args, quiet) == boolean_true_node;
>   }
>   
>   /* Evaluate a concept check of the form C<ARGS>. This is only used for the
> @@ -3020,14 +3061,14 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
>     gcc_assert (concept_check_p (check));
>   
>     /* Check for satisfaction without diagnostics.  */
> -  subst_info quiet (tf_none, NULL_TREE);
> +  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.  */
> -    subst_info noisy (tf_warning_or_error, NULL_TREE);
> -    satisfy_constraint_expression (check, NULL_TREE, noisy);
> -  }
> +    {
> +      /* 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;
>   }
>   
> @@ -3505,7 +3546,7 @@ diagnose_nested_requirement (tree req, tree args)
>     /* Quietly check for satisfaction first. We can elaborate details
>        later if needed.  */
>     tree norm = TREE_TYPE (req);
> -  subst_info info (tf_none, NULL_TREE);
> +  sat_info info (tf_none, NULL_TREE);
>     tree result = satisfy_constraint (norm, args, info);
>     if (result == boolean_true_node)
>       return;
> @@ -3516,7 +3557,8 @@ diagnose_nested_requirement (tree req, tree args)
>       {
>         /* Replay the substitution error.  */
>         inform (loc, "nested requirement %qE is not satisfied, because", expr);
> -      subst_info noisy (tf_warning_or_error, NULL_TREE);
> +      sat_info noisy (tf_warning_or_error, NULL_TREE);
> +      noisy.diagnose_unsatisfaction = true;
>         satisfy_constraint_expression (expr, args, noisy);
>       }
>     else
> @@ -3660,11 +3702,12 @@ diagnose_constraints (location_t loc, tree t, tree args)
>     if (concepts_diagnostics_max_depth == 0)
>       return;
>   
> -  /* Replay satisfaction, but diagnose errors.  */
> +  /* Replay satisfaction, but diagnose unsatisfaction.  */
> +  sat_info noisy (tf_warning_or_error, NULL_TREE, /*diagnose_unsat=*/true);
>     if (!args)
> -    constraint_satisfaction_value (t, tf_warning_or_error);
> +    constraint_satisfaction_value (t, noisy);
>     else
> -    constraint_satisfaction_value (t, args, tf_warning_or_error);
> +    constraint_satisfaction_value (t, args, noisy);
>   
>     static bool suggested_p;
>     if (concepts_diagnostics_max_depth_exceeded_p
> diff --git a/gcc/testsuite/g++.dg/concepts/pr94252.C b/gcc/testsuite/g++.dg/concepts/pr94252.C
> index 56ce5f88bad..b0457037ede 100644
> --- a/gcc/testsuite/g++.dg/concepts/pr94252.C
> +++ b/gcc/testsuite/g++.dg/concepts/pr94252.C
> @@ -16,6 +16,7 @@ static_assert(requires(S o, int i) {
>   
>   template<typename T>
>     concept c = requires (T t) { requires (T)5; }; // { dg-error "has type .int." }
> +// { dg-bogus "not satisfied" "" { target *-*-* } .-1 }
>   
>   int
>   foo()
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
> new file mode 100644
> index 00000000000..2a2af54847b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
> @@ -0,0 +1,5 @@
> +// { dg-do compile { target c++20 } }
> +
> +template <auto V> concept C = false || V || false; // { dg-error "has type 'int'" }
> +template <auto V> int f() requires C<V>;
> +int a = f<0>(); // { dg-error "no match" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
> index a9b7720cc6c..9e45c586917 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
> @@ -4,7 +4,7 @@ template<typename T>
>   concept integer = __is_same_as(T, int);
>   
>   template<typename T>
> -concept subst = requires (T x) { requires true; }; // { dg-error "parameter type .void." }
> +concept subst = requires (T x) { requires true; };
>   
>   template<typename T>
>   concept c1 = requires { requires integer<T> || subst<T&>; }; // { dg-message "in requirements" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
> index bc38b893c68..8aead2fe2c5 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
> @@ -5,3 +5,4 @@ template<typename T, typename U>
>   constexpr bool is_same_v = __is_same (T, U);
>   
>   static_assert(is_same_v<bool, decltype(requires { requires false; })>);
> +// { dg-bogus "evaluated to 'false" "" { target *-*-* } .-1 }
>
diff mbox series

Patch

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 7f02aa0a215..2be1a841535 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -98,7 +98,35 @@  struct subst_info
   tree in_decl;
 };
 
-static tree satisfy_constraint (tree, tree, subst_info);
+/* Provides additional context for satisfaction.
+
+   The flag noisy() controls whether to diagnose ill-formed satisfaction,
+   such as the satisfaction value of an atom being non-bool or non-constant.
+
+   The flag diagnose_unsatisfaction_p(), which implies noisy(), controls
+   whether to explain why a constraint is not satisfied.  */
+
+struct sat_info : subst_info
+{
+  sat_info (tsubst_flags_t cmp, tree in, bool diagnose_unsat = false)
+    : subst_info (cmp, in), diagnose_unsatisfaction (diagnose_unsat)
+  {
+    if (diagnose_unsatisfaction_p ())
+      gcc_checking_assert (noisy ());
+  }
+
+  /* True if we should diagnose the cause of satisfaction failure.
+     Implies noisy().  */
+  bool
+  diagnose_unsatisfaction_p () const
+  {
+    return diagnose_unsatisfaction;
+  }
+
+  bool diagnose_unsatisfaction;
+};
+
+static tree satisfy_constraint (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.  */
@@ -2059,10 +2087,11 @@  tsubst_nested_requirement (tree t, tree args, subst_info info)
 {
   /* Ensure that we're in an evaluation context prior to satisfaction.  */
   tree norm = TREE_TYPE (t);
-  tree result = satisfy_constraint (norm, args, info);
+  tree result = satisfy_constraint (norm, args,
+				    sat_info (info.complain, info.in_decl));
   if (result == error_mark_node && info.quiet ())
     {
-      subst_info noisy (tf_warning_or_error, info.in_decl);
+      sat_info noisy (tf_warning_or_error, info.in_decl);
       satisfy_constraint (norm, args, noisy);
     }
   if (result != boolean_true_node)
@@ -2508,12 +2537,12 @@  tsubst_constraint (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   return expr;
 }
 
-static tree satisfy_constraint_r (tree, tree, subst_info info);
+static tree satisfy_constraint_r (tree, tree, sat_info info);
 
 /* Compute the satisfaction of a conjunction.  */
 
 static tree
-satisfy_conjunction (tree t, tree args, subst_info info)
+satisfy_conjunction (tree t, tree args, sat_info info)
 {
   tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, info);
   if (lhs == error_mark_node || lhs == boolean_false_node)
@@ -2567,20 +2596,25 @@  collect_operands_of_disjunction (tree t, auto_vec<tree_pair> *operands)
 /* Compute the satisfaction of a disjunction.  */
 
 static tree
-satisfy_disjunction (tree t, tree args, subst_info info)
+satisfy_disjunction (tree t, tree args, sat_info info)
 {
-  /* Evaluate the operands quietly.  */
-  subst_info quiet (tf_none, NULL_TREE);
+  /* Evaluate each operand with unsatisfaction diagnostics disabled.  */
+  sat_info sub = info;
+  sub.diagnose_unsatisfaction = false;
 
-  /* Register the constraint for diagnostics, if needed.  */
-  diagnosing_failed_constraint failure (t, args, info.noisy ());
+  tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, sub);
+  if (lhs == boolean_true_node || lhs == error_mark_node)
+    return lhs;
 
-  tree lhs = satisfy_constraint_r (TREE_OPERAND (t, 0), args, quiet);
-  if (lhs == boolean_true_node)
-    return boolean_true_node;
-  tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, quiet);
-  if (rhs != boolean_true_node && info.noisy ())
+  tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, sub);
+  if (rhs == boolean_true_node || rhs == error_mark_node)
+    return rhs;
+
+  /* Both branches evaluated to false.  Explain the satisfaction failure in
+     each branch.  */
+  if (info.diagnose_unsatisfaction_p ())
     {
+      diagnosing_failed_constraint failure (t, args, info.noisy ());
       cp_expr disj_expr = CONSTR_EXPR (t);
       inform (disj_expr.get_location (),
 	      "no operand of the disjunction is satisfied");
@@ -2601,7 +2635,8 @@  satisfy_disjunction (tree t, tree args, subst_info info)
 	    }
 	}
     }
-  return rhs;
+
+  return boolean_false_node;
 }
 
 /* Ensures that T is a truth value and not (accidentally, as sometimes
@@ -2682,7 +2717,7 @@  static void diagnose_atomic_constraint (tree, tree, tree, subst_info);
 /* Compute the satisfaction of an atomic constraint.  */
 
 static tree
-satisfy_atom (tree t, tree args, subst_info info)
+satisfy_atom (tree t, tree args, sat_info info)
 {
   satisfaction_cache cache (t, args, info.complain);
   if (tree r = cache.get ())
@@ -2700,9 +2735,9 @@  satisfy_atom (tree t, tree args, subst_info info)
   tree map = tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, quiet);
   if (map == error_mark_node)
     {
-      /* If instantiation of the parameter mapping fails, the program
-         is ill-formed.  */
-      if (info.noisy())
+      /* If instantiation of the parameter mapping fails, the constraint is
+	 not satisfied.  Replay the substitution.  */
+      if (info.diagnose_unsatisfaction_p ())
 	tsubst_parameter_mapping (ATOMIC_CONSTR_MAP (t), args, info);
       return cache.save (boolean_false_node);
     }
@@ -2729,7 +2764,7 @@  satisfy_atom (tree t, tree args, subst_info info)
     {
       /* If substitution results in an invalid type or expression, the constraint
 	 is not satisfied. Replay the substitution.  */
-      if (info.noisy ())
+      if (info.diagnose_unsatisfaction_p ())
 	tsubst_expr (expr, args, info.complain, info.in_decl, false);
       return cache.save (inst_cache.save (boolean_false_node));
     }
@@ -2757,7 +2792,7 @@  satisfy_atom (tree t, tree args, subst_info info)
 	result = error_mark_node;
     }
   result = satisfaction_value (result);
-  if (result == boolean_false_node && info.noisy ())
+  if (result == boolean_false_node && info.diagnose_unsatisfaction_p ())
     diagnose_atomic_constraint (t, map, result, info);
 
   return cache.save (inst_cache.save (result));
@@ -2775,7 +2810,7 @@  satisfy_atom (tree t, tree args, subst_info info)
    constraint only matters for subsumption.  */
 
 static tree
-satisfy_constraint_r (tree t, tree args, subst_info info)
+satisfy_constraint_r (tree t, tree args, sat_info info)
 {
   if (t == error_mark_node)
     return error_mark_node;
@@ -2796,7 +2831,7 @@  satisfy_constraint_r (tree t, tree args, subst_info info)
 /* Check that the normalized constraint T is satisfied for ARGS.  */
 
 static tree
-satisfy_constraint (tree t, tree args, subst_info info)
+satisfy_constraint (tree t, tree args, sat_info info)
 {
   auto_timevar time (TV_CONSTRAINT_SAT);
 
@@ -2814,7 +2849,7 @@  satisfy_constraint (tree t, tree args, subst_info info)
    value (either true, false, or error).  */
 
 static tree
-satisfy_associated_constraints (tree t, tree args, subst_info info)
+satisfy_associated_constraints (tree t, tree args, sat_info info)
 {
   /* If there are no constraints then this is trivially satisfied.  */
   if (!t)
@@ -2832,7 +2867,7 @@  satisfy_associated_constraints (tree t, tree args, subst_info info)
    satisfaction value. */
 
 static tree
-satisfy_constraint_expression (tree t, tree args, subst_info info)
+satisfy_constraint_expression (tree t, tree args, sat_info info)
 {
   if (t == error_mark_node)
     return error_mark_node;
@@ -2861,12 +2896,12 @@  satisfy_constraint_expression (tree t, tree args, subst_info info)
 tree
 satisfy_constraint_expression (tree expr)
 {
-  subst_info info (tf_none, NULL_TREE);
+  sat_info info (tf_none, NULL_TREE);
   return satisfy_constraint_expression (expr, NULL_TREE, info);
 }
 
 static tree
-satisfy_declaration_constraints (tree t, subst_info info)
+satisfy_declaration_constraints (tree t, sat_info info)
 {
   gcc_assert (DECL_P (t));
   const tree saved_t = t;
@@ -2926,7 +2961,7 @@  satisfy_declaration_constraints (tree t, subst_info info)
 }
 
 static tree
-satisfy_declaration_constraints (tree t, tree args, subst_info info)
+satisfy_declaration_constraints (tree t, tree args, sat_info info)
 {
   /* Update the declaration for diagnostics.  */
   info.in_decl = t;
@@ -2951,9 +2986,8 @@  satisfy_declaration_constraints (tree t, tree args, subst_info info)
 }
 
 static tree
-constraint_satisfaction_value (tree t, tsubst_flags_t complain)
+constraint_satisfaction_value (tree t, sat_info info)
 {
-  subst_info info (complain, NULL_TREE);
   tree r;
   if (DECL_P (t))
     r = satisfy_declaration_constraints (t, info);
@@ -2961,26 +2995,31 @@  constraint_satisfaction_value (tree t, tsubst_flags_t complain)
     r = satisfy_constraint_expression (t, NULL_TREE, info);
   if (r == error_mark_node && info.quiet ()
       && !(DECL_P (t) && TREE_NO_WARNING (t)))
-      {
-	constraint_satisfaction_value (t, tf_warning_or_error);
-	if (DECL_P (t))
-	  /* Avoid giving these errors again.  */
-	  TREE_NO_WARNING (t) = true;
-      }
+    {
+      /* Replay the error with re-normalized requirements.  */
+      sat_info noisy (tf_warning_or_error, info.in_decl);
+      constraint_satisfaction_value (t, noisy);
+      if (DECL_P (t))
+	/* Avoid giving these errors again.  */
+	TREE_NO_WARNING (t) = true;
+    }
   return r;
 }
 
 static tree
-constraint_satisfaction_value (tree t, tree args, tsubst_flags_t complain)
+constraint_satisfaction_value (tree t, tree args, sat_info info)
 {
-  subst_info info (complain, NULL_TREE);
   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 ())
-    constraint_satisfaction_value (t, args, tf_warning_or_error);
+    {
+      /* 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;
 }
 
@@ -2993,7 +3032,8 @@  constraints_satisfied_p (tree t)
   if (!flag_concepts)
     return true;
 
-  return constraint_satisfaction_value (t, tf_none) == boolean_true_node;
+  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
@@ -3005,7 +3045,8 @@  constraints_satisfied_p (tree t, tree args)
   if (!flag_concepts)
     return true;
 
-  return constraint_satisfaction_value (t, args, tf_none) == boolean_true_node;
+  sat_info quiet (tf_none, NULL_TREE);
+  return constraint_satisfaction_value (t, args, quiet) == boolean_true_node;
 }
 
 /* Evaluate a concept check of the form C<ARGS>. This is only used for the
@@ -3020,14 +3061,14 @@  evaluate_concept_check (tree check, tsubst_flags_t complain)
   gcc_assert (concept_check_p (check));
 
   /* Check for satisfaction without diagnostics.  */
-  subst_info quiet (tf_none, NULL_TREE);
+  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.  */
-    subst_info noisy (tf_warning_or_error, NULL_TREE);
-    satisfy_constraint_expression (check, NULL_TREE, noisy);
-  }
+    {
+      /* 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;
 }
 
@@ -3505,7 +3546,7 @@  diagnose_nested_requirement (tree req, tree args)
   /* Quietly check for satisfaction first. We can elaborate details
      later if needed.  */
   tree norm = TREE_TYPE (req);
-  subst_info info (tf_none, NULL_TREE);
+  sat_info info (tf_none, NULL_TREE);
   tree result = satisfy_constraint (norm, args, info);
   if (result == boolean_true_node)
     return;
@@ -3516,7 +3557,8 @@  diagnose_nested_requirement (tree req, tree args)
     {
       /* Replay the substitution error.  */
       inform (loc, "nested requirement %qE is not satisfied, because", expr);
-      subst_info noisy (tf_warning_or_error, NULL_TREE);
+      sat_info noisy (tf_warning_or_error, NULL_TREE);
+      noisy.diagnose_unsatisfaction = true;
       satisfy_constraint_expression (expr, args, noisy);
     }
   else
@@ -3660,11 +3702,12 @@  diagnose_constraints (location_t loc, tree t, tree args)
   if (concepts_diagnostics_max_depth == 0)
     return;
 
-  /* Replay satisfaction, but diagnose errors.  */
+  /* Replay satisfaction, but diagnose unsatisfaction.  */
+  sat_info noisy (tf_warning_or_error, NULL_TREE, /*diagnose_unsat=*/true);
   if (!args)
-    constraint_satisfaction_value (t, tf_warning_or_error);
+    constraint_satisfaction_value (t, noisy);
   else
-    constraint_satisfaction_value (t, args, tf_warning_or_error);
+    constraint_satisfaction_value (t, args, noisy);
 
   static bool suggested_p;
   if (concepts_diagnostics_max_depth_exceeded_p
diff --git a/gcc/testsuite/g++.dg/concepts/pr94252.C b/gcc/testsuite/g++.dg/concepts/pr94252.C
index 56ce5f88bad..b0457037ede 100644
--- a/gcc/testsuite/g++.dg/concepts/pr94252.C
+++ b/gcc/testsuite/g++.dg/concepts/pr94252.C
@@ -16,6 +16,7 @@  static_assert(requires(S o, int i) {
 
 template<typename T>
   concept c = requires (T t) { requires (T)5; }; // { dg-error "has type .int." }
+// { dg-bogus "not satisfied" "" { target *-*-* } .-1 }
 
 int
 foo()
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
new file mode 100644
index 00000000000..2a2af54847b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nonbool3.C
@@ -0,0 +1,5 @@ 
+// { dg-do compile { target c++20 } }
+
+template <auto V> concept C = false || V || false; // { dg-error "has type 'int'" }
+template <auto V> int f() requires C<V>;
+int a = f<0>(); // { dg-error "no match" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
index a9b7720cc6c..9e45c586917 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
@@ -4,7 +4,7 @@  template<typename T>
 concept integer = __is_same_as(T, int);
 
 template<typename T>
-concept subst = requires (T x) { requires true; }; // { dg-error "parameter type .void." }
+concept subst = requires (T x) { requires true; };
 
 template<typename T>
 concept c1 = requires { requires integer<T> || subst<T&>; }; // { dg-message "in requirements" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
index bc38b893c68..8aead2fe2c5 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires21.C
@@ -5,3 +5,4 @@  template<typename T, typename U>
 constexpr bool is_same_v = __is_same (T, U);
 
 static_assert(is_same_v<bool, decltype(requires { requires false; })>);
+// { dg-bogus "evaluated to 'false" "" { target *-*-* } .-1 }