+struct sat_info : subst_info
+{
+ sat_info (tsubst_flags_t cmp, tree in, bool diag_unsat = false)
+ : subst_info (cmp, in), diagnose_unsatisfaction (diag_unsat)
+ {
+ if (diagnose_unsatisfaction_p ())
+ gcc_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);
+static tree satisfy_constraint_expression (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. */
@@ -1911,22 +1934,44 @@ hash_placeholder_constraint (tree c)
return val;
}
-/* Substitute through the simple requirement. */
+/* Substitute through the expression of a simple requirement or
+ compound requirement. */
static tree
-tsubst_valid_expression_requirement (tree t, tree args, subst_info info)
+tsubst_valid_expression_requirement (tree t, tree args, sat_info info)
{
- tree r = tsubst_expr (t, args, info.complain, info.in_decl, false);
- if (convert_to_void (r, ICV_STATEMENT, info.complain) == error_mark_node)
- return error_mark_node;
- return r;
+ tree r = tsubst_expr (t, args, tf_none, info.in_decl, false);
+ if (convert_to_void (r, ICV_STATEMENT, tf_none) != error_mark_node)
+ return r;
+
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ location_t loc = cp_expr_loc_or_input_loc (t);
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ inform (loc, "the required expression %qE is invalid, because", t);
+ if (r == error_mark_node)
+ tsubst_expr (t, args, info.complain, info.in_decl, false);
+ else
+ convert_to_void (r, ICV_STATEMENT, info.complain);
+ }
+ else
+ inform (loc, "the required expression %qE is invalid", t);
+ }
+ else if (info.noisy ())
+ {
+ r = tsubst_expr (t, args, info.complain, info.in_decl, false);
+ convert_to_void (r, ICV_STATEMENT, info.complain);
+ }
+
+ return error_mark_node;
}
/* Substitute through the simple requirement. */
static tree
-tsubst_simple_requirement (tree t, tree args, subst_info info)
+tsubst_simple_requirement (tree t, tree args, sat_info info)
{
tree t0 = TREE_OPERAND (t, 0);
tree expr = tsubst_valid_expression_requirement (t0, args, info);
@@ -1935,13 +1980,40 @@ tsubst_simple_requirement (tree t, tree args,
subst_info info)
return finish_simple_requirement (EXPR_LOCATION (t), expr);
}
+/* Subroutine of tsubst_type_requirement that performs the actual substitution
+ and diagnosing. Also used by tsubst_compound_requirement. */
+
+static tree
+tsubst_type_requirement_1 (tree t, tree args, sat_info info, location_t loc)
+{
+ tree r = tsubst (t, args, tf_none, info.in_decl);
+ if (r != error_mark_node)
+ return r;
+
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ /* Replay the substitution error. */
+ inform (loc, "the required type %qT is invalid, because", t);
+ tsubst (t, args, info.complain, info.in_decl);
+ }
+ else
+ inform (loc, "the required type %qT is invalid", t);
+ }
+ else if (info.noisy ())
+ tsubst (t, args, info.complain, info.in_decl);
+
+ return error_mark_node;
+}
+
/* Substitute through the type requirement. */
static tree
-tsubst_type_requirement (tree t, tree args, subst_info info)
+tsubst_type_requirement (tree t, tree args, sat_info info)
{
tree t0 = TREE_OPERAND (t, 0);
- tree type = tsubst (t0, args, info.complain, info.in_decl);
+ tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t));
if (type == error_mark_node)
return error_mark_node;
return finish_type_requirement (EXPR_LOCATION (t), type);
@@ -2018,7 +2090,7 @@ expression_convertible_p (tree expr, tree type,
subst_info info)
/* Substitute through the compound requirement. */
static tree
-tsubst_compound_requirement (tree t, tree args, subst_info info)
+tsubst_compound_requirement (tree t, tree args, sat_info info)
{
tree t0 = TREE_OPERAND (t, 0);
tree t1 = TREE_OPERAND (t, 1);
@@ -2026,13 +2098,20 @@ tsubst_compound_requirement (tree t, tree args,
subst_info info)
if (expr == error_mark_node)
return error_mark_node;
+ location_t loc = cp_expr_loc_or_input_loc (expr);
+
/* Check the noexcept condition. */
bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
if (noexcept_p && !expr_noexcept_p (expr, tf_none))
- return error_mark_node;
+ {
+ if (info.diagnose_unsatisfaction_p ())
+ inform (loc, "%qE is not %<noexcept%>", expr);
+ else
+ return error_mark_node;
+ }
/* Substitute through the type expression, if any. */
- tree type = tsubst (t1, args, info.complain, info.in_decl);
+ tree type = tsubst_type_requirement_1 (t1, args, info, EXPR_LOCATION (t));
if (type == error_mark_node)
return error_mark_node;
@@ -2044,36 +2123,83 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
if (tree placeholder = type_uses_auto (type))
{
if (!type_deducible_p (expr, type, placeholder, args, quiet))
- return error_mark_node;
+ {
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ inform (loc,
+ "%qE does not satisfy return-type-requirement, "
+ "because", t0);
+ /* Further explain the reason for the error. */
+ type_deducible_p (expr, type, placeholder, args, info);
+ }
+ else
+ inform (loc,
+ "%qE does not satisfy return-type-requirement", t0);
+ }
+ return error_mark_node;
+ }
}
else if (!expression_convertible_p (expr, type, quiet))
- return error_mark_node;
+ {
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ inform (loc, "cannot convert %qE to %qT because", t0, type);
+ /* Further explain the reason for the error. */
+ expression_convertible_p (expr, type, info);
+ }
+ else
+ inform (loc, "cannot convert %qE to %qT", t0, type);
+ }
+ return error_mark_node;
+ }
}
return finish_compound_requirement (EXPR_LOCATION (t),
expr, type, noexcept_p);
}
+/* Substitute through the nested requirement. */
+
static tree
-tsubst_nested_requirement (tree t, tree args, subst_info info)
+tsubst_nested_requirement (tree t, tree args, sat_info info)
{
- /* Ensure that we're in an evaluation context prior to satisfaction. */
+ sat_info quiet (tf_none, info.in_decl);
tree norm = TREE_TYPE (t);
- tree result = satisfy_constraint (norm, args, info);
- if (result == error_mark_node && info.quiet ())
+ tree result = satisfy_constraint (norm, args, quiet);
- template<typename T>
- class X {
- template<typename U>
- static constexpr bool var = requires (U u) { T::fn(u); };
- };
+ When processing a REQUIRES_EXPR that appears outside a template in
+ cp_parser_requires_expression, we call this routine with
+ info.noisy() == true.
- In the instantiation of X<Y> (assuming Y defines fn), then the
- instantiated requires-expression would include Y::fn(u). If any
- substitution in the requires-expression fails, we can immediately
- fold the expression to false, as would be the case e.g., when
- instantiation X<int>. */
+ Finally, when diagnosing unsatisfaction from diagnose_atomic_constraint,
+ we call this routine with info.diagnose_unsatisfaction_p() == true. */
-tree
-tsubst_requires_expr (tree t, tree args,
- tsubst_flags_t complain, tree in_decl)
+static tree
+tsubst_requires_expr (tree t, tree args, sat_info info)
{
local_specialization_stack stack (lss_copy);
- subst_info info (complain, in_decl);
-
/* A requires-expression is an unevaluated context. */
cp_unevaluated u;
@@ -2216,7 +2333,7 @@ tsubst_requires_expr (tree t, tree args,
checked out of order, so instead just remember the template
arguments and wait until we can substitute them all at once. */
t = copy_node (t);
- REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, complain);
+ REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain);
return t;
}
@@ -2236,6 +2353,16 @@ tsubst_requires_expr (tree t, tree args,
return boolean_true_node;
}
+/* Wrapper for the above. */
+
+tree
+tsubst_requires_expr (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ sat_info info (complain, in_decl);
+ return tsubst_requires_expr (t, args, info);
+}
+
/* Substitute ARGS into the constraint information CI, producing a new
constraint record. */
@@ -2256,7 +2383,7 @@ tsubst_constraint_info (tree t, tree args,
if the substitution into arguments produces something ill-formed. */
static tree
-tsubst_parameter_mapping (tree map, tree args, subst_info info)
+tsubst_parameter_mapping (tree map, tree args, sat_info info)
{
if (!map)
return NULL_TREE;
@@ -2326,7 +2453,7 @@ tsubst_parameter_mapping (tree map, tree args, subst_info
info)
tree
tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree
in_decl)
{
- return tsubst_parameter_mapping (map, args, subst_info (complain, in_decl));
+ return tsubst_parameter_mapping (map, args, sat_info (complain, in_decl));
}
/*---------------------------------------------------------------------------
@@ -2508,12 +2635,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 +2694,24 @@ 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;
+
+ /* Diagnose the satisfaction failure in each branch. */
+ if (info.diagnose_unsatisfaction_p ())
{
+ diagnosing_failed_constraint failure (t, args, /*diag=*/true);
cp_expr disj_expr = CONSTR_EXPR (t);
inform (disj_expr.get_location (),
"no operand of the disjunction is satisfied");
@@ -2601,7 +2732,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
@@ -2677,19 +2809,19 @@ get_mapped_args (tree map)
return args;
}
-static void diagnose_atomic_constraint (tree, tree, tree, subst_info);
+static void diagnose_atomic_constraint (tree, tree, tree, sat_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 ())
return r;
/* Perform substitution quietly. */
- subst_info quiet (tf_none, NULL_TREE);
+ sat_info quiet (tf_none, NULL_TREE);
/* In case there is a diagnostic, we want to establish the context
prior to printing errors. If no errors occur, this context is
@@ -2700,9 +2832,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 +2861,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));
}
@@ -2747,17 +2879,18 @@ satisfy_atom (tree t, tree args, subst_info info)
}
/* Compute the value of the constraint. */
- if (info.noisy ())
- result = cxx_constant_value (result);
- else
+ result = maybe_constant_value (result, NULL_TREE,
+ /*manifestly_const_eval=*/true);
+ if (!TREE_CONSTANT (result))
{
- result = maybe_constant_value (result, NULL_TREE,
- /*manifestly_const_eval=*/true);
- if (!TREE_CONSTANT (result))
- result = error_mark_node;
+ if (info.noisy ())
+ cxx_constant_value (result);
+ return cache.save (inst_cache.save (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));
@@ -2772,10 +2905,17 @@ satisfy_atom (tree t, tree args, subst_info info)
set of template arguments that will be substituted into
the expression, regardless of template parameters appearing
withing. Whether a template argument is used in the atomic
- constraint only matters for subsumption. */
+ constraint only matters for subsumption.
+
+ When info.noisy() is true, diagnose errors that make the
+ program ill-formed.
+
+ When info.diagnose_unsatisfaction_p() is true and the overall
+ constraint is unsatisfied, additionally diagnose the cause of
+ satisfaction failure. */
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 +2936,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 +2954,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 +2972,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 +3001,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 +3066,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 +3091,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 +3100,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 +3137,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 +3150,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 +3166,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;
}
@@ -3300,11 +3446,10 @@ get_constraint_error_location (tree t)
/* Emit a diagnostic for a failed trait. */
-void
-diagnose_trait_expr (tree expr, tree map)
+static void
+diagnose_trait_expr (tree expr, tree args)
{
location_t loc = cp_expr_location (expr);
- tree args = get_mapped_args (map);
/* Build a "fake" version of the instantiated trait, so we can
get the instantiated types from result. */
@@ -3384,193 +3529,11 @@ diagnose_trait_expr (tree expr, tree map)
}
}
-static tree
-diagnose_valid_expression (tree expr, tree args, tree in_decl)
-{
- tree result = tsubst_expr (expr, args, tf_none, in_decl, false);
- if (result != error_mark_node
- && convert_to_void (result, ICV_STATEMENT, tf_none) != error_mark_node)
- return result;
-
- location_t loc = cp_expr_loc_or_input_loc (expr);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- /* Replay the substitution error. */
- inform (loc, "the required expression %qE is invalid, because", expr);
- if (result == error_mark_node)
- tsubst_expr (expr, args, tf_error, in_decl, false);
- else
- convert_to_void (result, ICV_STATEMENT, tf_error);
- }
- else
- inform (loc, "the required expression %qE is invalid", expr);
-
- return error_mark_node;
-}
-
-static tree
-diagnose_valid_type (tree type, tree args, tree in_decl)
-{
- tree result = tsubst (type, args, tf_none, in_decl);
- if (result != error_mark_node)
- return result;
-
- location_t loc = cp_expr_loc_or_input_loc (type);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- /* Replay the substitution error. */
- inform (loc, "the required type %qT is invalid, because", type);
- tsubst (type, args, tf_error, in_decl);
- }
- else
- inform (loc, "the required type %qT is invalid", type);
-
- return error_mark_node;
-}
-
-static void
-diagnose_simple_requirement (tree req, tree args, tree in_decl)
-{
- diagnose_valid_expression (TREE_OPERAND (req, 0), args, in_decl);
-}
-
-static void
-diagnose_compound_requirement (tree req, tree args, tree in_decl)
-{
- tree expr = TREE_OPERAND (req, 0);
- expr = diagnose_valid_expression (expr, args, in_decl);
- if (expr == error_mark_node)
- return;
-
- location_t loc = cp_expr_loc_or_input_loc (expr);
-
- /* Check the noexcept condition. */
- if (COMPOUND_REQ_NOEXCEPT_P (req) && !expr_noexcept_p (expr, tf_none))
- inform (loc, "%qE is not %<noexcept%>", expr);
-
- tree type = TREE_OPERAND (req, 1);
- type = diagnose_valid_type (type, args, in_decl);
- if (type == error_mark_node)
- return;
-
- if (type)
- {
- subst_info quiet (tf_none, in_decl);
- subst_info noisy (tf_error, in_decl);
-
- /* Check the expression against the result type. */
- if (tree placeholder = type_uses_auto (type))
- {
- if (!type_deducible_p (expr, type, placeholder, args, quiet))
- {
- tree orig_expr = TREE_OPERAND (req, 0);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- inform (loc,
- "%qE does not satisfy return-type-requirement, "
- "because", orig_expr);
- /* Further explain the reason for the error. */
- type_deducible_p (expr, type, placeholder, args, noisy);
- }
- else
- inform (loc, "%qE does not satisfy return-type-requirement",
- orig_expr);
- }
- }
- else if (!expression_convertible_p (expr, type, quiet))
- {
- tree orig_expr = TREE_OPERAND (req, 0);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- inform (loc, "cannot convert %qE to %qT because", orig_expr,
type);
- /* Further explain the reason for the error. */
- expression_convertible_p (expr, type, noisy);
- }
- else
- inform (loc, "cannot convert %qE to %qT", orig_expr, type);
- }
- }
-}
-
-static void
-diagnose_type_requirement (tree req, tree args, tree in_decl)
-{
- tree type = TREE_OPERAND (req, 0);
- diagnose_valid_type (type, args, in_decl);
-}
-
-static void
-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);
- tree result = satisfy_constraint (norm, args, info);
- if (result == boolean_true_node)
- return;
-
- tree expr = TREE_OPERAND (req, 0);
- location_t loc = cp_expr_location (expr);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- /* Replay the substitution error. */
- inform (loc, "nested requirement %qE is not satisfied, because", expr);
- subst_info noisy (tf_warning_or_error, NULL_TREE);
- satisfy_constraint_expression (expr, args, noisy);
- }
- else
- inform (loc, "nested requirement %qE is not satisfied", expr);
-
-}
-
-static void
-diagnose_requirement (tree req, tree args, tree in_decl)
-{
- iloc_sentinel loc_s (cp_expr_location (req));
- switch (TREE_CODE (req))
- {
- case SIMPLE_REQ:
- return diagnose_simple_requirement (req, args, in_decl);
- case COMPOUND_REQ:
- return diagnose_compound_requirement (req, args, in_decl);
- case TYPE_REQ:
- return diagnose_type_requirement (req, args, in_decl);
- case NESTED_REQ:
- return diagnose_nested_requirement (req, args);
- default:
- gcc_unreachable ();
- }
-}
-
-static void
-diagnose_requires_expr (tree expr, tree map, tree in_decl)
-{
- local_specialization_stack stack (lss_copy);
- tree parms = TREE_OPERAND (expr, 0);
- tree body = TREE_OPERAND (expr, 1);
- tree args = get_mapped_args (map);
-
- cp_unevaluated u;
- subst_info info (tf_warning_or_error, NULL_TREE);
- tree vars = tsubst_constraint_variables (parms, args, info);
- if (vars == error_mark_node)
- return;
-
- tree p = body;
- while (p)
- {
- tree req = TREE_VALUE (p);
- diagnose_requirement (req, args, in_decl);
- p = TREE_CHAIN (p);
- }
-}
-
/* Diagnose a substitution failure in the atomic constraint T when applied
with the instantiated parameter mapping MAP. */
static void
-diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info)
+diagnose_atomic_constraint (tree t, tree map, tree result, sat_info info)
{
/* If the constraint is already ill-formed, we've previously diagnosed
the reason. We should still say why the constraints aren't satisfied. */
@@ -3591,13 +3554,15 @@ diagnose_atomic_constraint (tree t, tree map, tree
result, subst_info info)
/* Generate better diagnostics for certain kinds of expressions. */
tree expr = ATOMIC_CONSTR_EXPR (t);
STRIP_ANY_LOCATION_WRAPPER (expr);
+ tree args = get_mapped_args (map);
switch (TREE_CODE (expr))
{
case TRAIT_EXPR:
- diagnose_trait_expr (expr, map);
+ diagnose_trait_expr (expr, args);
break;
case REQUIRES_EXPR:
- diagnose_requires_expr (expr, map, info.in_decl);
+ gcc_checking_assert (info.diagnose_unsatisfaction_p ());
+ tsubst_requires_expr (expr, args, info);
break;
default:
if (!same_type_p (TREE_TYPE (result), boolean_type_node))
@@ -3660,11 +3625,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, /*diag_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/diagnostic1.C
b/gcc/testsuite/g++.dg/concepts/diagnostic1.C
index 29c78c4c730..23bd592411e 100644
--- a/gcc/testsuite/g++.dg/concepts/diagnostic1.C
+++ b/gcc/testsuite/g++.dg/concepts/diagnostic1.C
@@ -8,7 +8,7 @@ concept bool SameAs = __is_same_as(T, U);
template <class T>
concept bool R1 = requires (T& t) { // { dg-message "in requirements" }
{ t.begin() } -> T; // { dg-error "no match" }
- { t.end() } -> SameAs<T*>; // { dg-message "does not satisfy" }
+ { t.end() } -> SameAs<T*>;
};
template <class T>
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-pr66844.C
b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C
index afeee5927e5..efd9f035b20 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr66844.C
@@ -11,6 +11,6 @@ concept C = requires (T t) { // { dg-error "invalid parameter|in
requirements" }
template <typename T>
requires C<T>
-constexpr bool is_c() { return true; }
+constexpr bool is_c() { return true; } // { dg-error "in declaration" }
static_assert(is_c<void>(), ""); // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
new file mode 100644
index 00000000000..d9800159d70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr97093.C
@@ -0,0 +1,31 @@
+// { dg-do compile { target c++20 } }
+// { dg-additional-options "-fconcepts-diagnostics-depth=3
--param=hash-table-verification-limit=10000" }
+
+template <typename T>
+concept C = requires (T t)
+{
+ requires t.some_const < 2 || requires { t.some_fn (); };
+};
+
+template <unsigned, unsigned>
+struct c
+{};
+
+template <typename T>
+concept P = requires (T t, c <0, 1> v) { { t (v) }; }; // { dg-error "no
match" }
+
+template <P auto, P auto ...>
+struct m
+{
+ constexpr auto operator () (C auto) const
+ {};
+};
+
+struct pc
+{
+ constexpr auto operator () (C auto) const
+ {};
+};
+
+constexpr auto cc = pc {};
+constexpr auto mmcc = m <cc> {}; // { dg-error "not satisfied" }
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 }