/* When either operand is dependent, the overload set may be non-empty. */
if (expr == error_mark_node)
return error_mark_node;
+ expr.set_location (loc);
expr.set_range (lhs.get_start (), rhs.get_finish ());
return expr;
}
return satisfy_constraint_r (TREE_OPERAND (t, 1), args, info);
}
+/* The current depth at which we're replaying an error during recursive
+ diagnosis of a constraint satisfaction failure. */
+
+static int current_constraint_diagnosis_depth;
+
+/* Whether CURRENT_CONSTRAINT_DIAGNOSIS_DEPTH has ever exceeded
+ CONCEPTS_DIAGNOSTICS_MAX_DEPTH during recursive diagnosis of a constraint
+ satisfaction error. */
+
+static bool concepts_diagnostics_max_depth_exceeded_p;
+
+/* Recursive subroutine of collect_operands_of_disjunction. T is a normalized
+ subexpression of a constraint (composed of CONJ_CONSTRs and DISJ_CONSTRs)
+ and E is the corresponding unnormalized subexpression (composed of
+ TRUTH_ANDIF_EXPRs and TRUTH_ORIF_EXPRs). */
+
+static void
+collect_operands_of_disjunction_r (tree t, tree e,
+ auto_vec<tree_pair> *operands)
+{
+ if (TREE_CODE (e) == TRUTH_ORIF_EXPR)
+ {
+ collect_operands_of_disjunction_r (TREE_OPERAND (t, 0),
+ TREE_OPERAND (e, 0), operands);
+ collect_operands_of_disjunction_r (TREE_OPERAND (t, 1),
+ TREE_OPERAND (e, 1), operands);
+ }
+ else
+ {
+ tree_pair p = std::make_pair (t, e);
+ operands->safe_push (p);
+ }
+}
+
+/* Recursively collect the normalized and unnormalized operands of the
+ disjunction T and append them to OPERANDS in order. */
+
+static void
+collect_operands_of_disjunction (tree t, auto_vec<tree_pair> *operands)
+{
+ collect_operands_of_disjunction_r (t, CONSTR_EXPR (t), operands);
+}
+
/* Compute the satisfaction of a disjunction. */
static tree
tree rhs = satisfy_constraint_r (TREE_OPERAND (t, 1), args, quiet);
if (rhs != boolean_true_node && info.noisy ())
{
- location_t loc = cp_expr_location (CONSTR_EXPR (t));
- inform (loc, "neither operand of the disjunction is satisfied");
- /* TODO: Replay the LHS and RHS to find failures in both branches. */
- // satisfy_constraint_r (TREE_OPERAND (t, 0), args, info);
- // satisfy_constraint_r (TREE_OPERAND (t, 1), args, info);
+ cp_expr disj_expr = CONSTR_EXPR (t);
+ inform (disj_expr.get_location (),
+ "no operand of the disjunction is satisfied");
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ /* Replay the error in each branch of the disjunction. */
+ auto_vec<tree_pair> operands;
+ collect_operands_of_disjunction (t, &operands);
+ for (unsigned i = 0; i < operands.length (); i++)
+ {
+ tree norm_op = operands[i].first;
+ tree op = operands[i].second;
+ location_t loc = make_location (cp_expr_location (op),
+ disj_expr.get_start (),
+ disj_expr.get_finish ());
+ inform (loc, "the operand %qE is unsatisfied because", op);
+ satisfy_constraint_r (norm_op, args, info);
+ }
+ }
}
return rhs;
}
return result;
location_t loc = cp_expr_loc_or_input_loc (expr);
- inform (loc, "the required expression %qE is invalid", expr);
-
- /* TODO: Replay the substitution to diagnose the error? */
- // tsubst_expr (expr, args, tf_error, in_decl, false);
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ /* Replay the substitution error. */
+ inform (loc, "the required expression %qE is invalid, because", expr);
+ tsubst_expr (expr, args, tf_error, in_decl, false);
+ }
+ else
+ inform (loc, "the required expression %qE is invalid", expr);
return error_mark_node;
}
return result;
location_t loc = cp_expr_loc_or_input_loc (type);
- inform (loc, "the required type %qT is invalid", type);
-
- /* TODO: Replay the substitution to diagnose the error? */
- // tsubst (type, args, tf_error, in_decl);
+ 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;
}
tree expr = TREE_OPERAND (req, 0);
location_t loc = cp_expr_location (expr);
- inform (loc, "nested requirement %qE is not satisfied", 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);
- /* TODO: Replay the substitution to diagnose the error? */
- // subst_info noisy (tf_warning_or_error, NULL_TREE);
- // satisfy_constraint (norm, args, info);
}
static void
: diagnosing_error (diag)
{
if (diagnosing_error)
- current_failed_constraint = tree_cons (args, t, current_failed_constraint);
+ {
+ current_failed_constraint
+ = tree_cons (args, t, current_failed_constraint);
+ ++current_constraint_diagnosis_depth;
+ }
}
diagnosing_failed_constraint::
~diagnosing_failed_constraint ()
{
- if (diagnosing_error && current_failed_constraint)
- current_failed_constraint = TREE_CHAIN (current_failed_constraint);
+ if (diagnosing_error)
+ {
+ --current_constraint_diagnosis_depth;
+ if (current_failed_constraint)
+ current_failed_constraint = TREE_CHAIN (current_failed_constraint);
+ }
+
+}
+
+/* Whether we are allowed to replay an error that underlies a constraint failure
+ at the current diagnosis depth. */
+
+bool
+diagnosing_failed_constraint::replay_errors_p ()
+{
+ if (current_constraint_diagnosis_depth >= concepts_diagnostics_max_depth)
+ {
+ concepts_diagnostics_max_depth_exceeded_p = true;
+ return false;
+ }
+ else
+ return true;
}
/* Emit diagnostics detailing the failure ARGS to satisfy the constraints
{
inform (loc, "constraints not satisfied");
+ if (concepts_diagnostics_max_depth == 0)
+ return;
+
/* Replay satisfaction, but diagnose errors. */
if (!args)
constraint_satisfaction_value (t, tf_warning_or_error);
else
constraint_satisfaction_value (t, args, tf_warning_or_error);
+
+ static bool suggested_p;
+ if (concepts_diagnostics_max_depth_exceeded_p
+ && current_constraint_diagnosis_depth == 0
+ && !suggested_p)
+ {
+ inform (UNKNOWN_LOCATION,
+ "set %qs to at least %d for more detail",
+ "-fconcepts-diagnostics-depth=",
+ concepts_diagnostics_max_depth + 1);
+ suggested_p = true;
+ }
}
#include "gt-cp-constraint.h"
--- /dev/null
+// { dg-do compile { target c++2a } }
+// { dg-additional-options "-fconcepts-diagnostics-depth=2" }
+
+template<typename T>
+ concept c1 = requires { typename T::blah; };
+// { dg-message "satisfaction of .c1<T>. .with T = char." "" { target *-*-* } .-1 }
+// { dg-message "satisfaction of .c1<char\\*>." "" { target *-*-* } .-2 }
+// { dg-message ".typename T::blah. is invalid" "" { target *-*-* } .-3 }
+
+template<typename T>
+ concept c2 = requires (T x) { *x; };
+// { dg-message "satisfaction of .c2<T>. .with T = char." "" { target *-*-* } .-1 }
+// { dg-message "in requirements with .char x." "" { target *-*-* } .-2 }
+// { dg-message "required expression .* is invalid" "" { target *-*-* } .-3 }
+
+template<typename T>
+ concept c3 = __is_same(T, const T) || __is_same(T, int);
+// { dg-message "satisfaction of .c3<T>. .with T = char." "" { target *-*-* } .-1 }
+// { dg-message "no operand of the disjunction is satisfied" "" { target *-*-* } .-2 }
+
+template<typename T>
+ concept c4 = requires (T x) { requires c2<const T> || c2<volatile T>; };
+// { dg-message "satisfaction of .c4<T>. .with T = char." "" { target *-*-* } .-1 }
+// { dg-message "nested requirement" "" { target *-*-* } .-2 }
+
+template<typename T>
+ concept c5 = requires (T x) { { &x } -> c1; };
+// { dg-message "satisfaction of .c5<T>. .with T = char." "" { target *-*-* } .-1 }
+// { dg-message "in requirements with .char x." "" { target *-*-* } .-2 }
+// { dg-message "does not satisfy return-type-requirement" "" { target *-*-* } .-3 }
+// { dg-error "deduced expression type does not satisfy" "" { target *-*-* } .-4 }
+
+template<typename T>
+ requires (c1<T> || c2<T>) || (c3<T> || c4<T>) || c5<T> // { dg-message "49: no operand" }
+ // { dg-message ".c1<T>. is unsatisfied because" "" { target *-*-* } .-1 }
+ // { dg-message ".c2<T>. is unsatisfied because" "" { target *-*-* } .-2 }
+ // { dg-message ".c3<T>. is unsatisfied because" "" { target *-*-* } .-3 }
+ // { dg-message ".c4<T>. is unsatisfied because" "" { target *-*-* } .-4 }
+ // { dg-message ".c5<T>. is unsatisfied because" "" { target *-*-* } .-5 }
+ void foo() { }
+
+void
+bar()
+{
+ foo<char>(); // { dg-error "use of" }
+}