-- >8 --
gcc/c-family/ChangeLog:
* c.opt: Add -fconcepts-diagnostics-depth.
gcc/cp/ChangeLog:
* constraint.cc (finish_constraint_binary_op): Set the location of EXPR
as well as its range, because build_x_binary_op doesn't always do so.
(current_constraint_diagnosis_depth): New.
(concepts_diagnostics_max_depth_exceeded_p): New.
(collect_operands_of_disjunction): New.
(satisfy_disjunction): When diagnosing a satisfaction failure, maybe
replay each branch of the disjunction, subject to the current diagnosis
depth.
(diagnose_valid_expression): When diagnosing a satisfaction failure,
maybe replay the substitution error, subject to the current diagnosis
recursion.
(diagnose_valid_type): Likewise.
(diagnose_nested_requiremnet): Likewise.
(diagnosing_failed_constraint::diagnosing_failed_constraint): Increment
current_constraint_diagnosis_depth when diagnosing.
(diagnosing_failed_constraint::~diagnosing_failed_constraint): Decrement
current_constraint_diagnosis_depth when diagnosing.
(diagnosing_failed_constraint::replay_errors_p): New static member
function.
(diagnose_constraints): Don't diagnose if concepts_diagnostics_max_depth
is 0. Emit a one-off note to increase -fconcepts-diagnostics-depth if
the limit was exceeded.
* cp-tree.h (diagnosing_failed_constraint::replay_errors_p): Declare.
gcc/testsuite/ChangeLog:
* g++.dg/concepts/diagnostic2.C: Expect "no operand" instead of
"neither operand".
* g++.dg/concepts/diagnostic5.C: New test.
---
gcc/c-family/c.opt | 4 +
gcc/cp/constraint.cc | 150 +++++++++++++++++---
gcc/cp/cp-tree.h | 1 +
gcc/testsuite/g++.dg/concepts/diagnostic2.C | 2 +-
gcc/testsuite/g++.dg/concepts/diagnostic5.C | 46 ++++++
5 files changed, 182 insertions(+), 21 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic5.C
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index a1e0f4cdc3a..c49da99d395 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1453,6 +1453,10 @@ fconcepts-ts
C++ ObjC++ Var(flag_concepts_ts) Init(0)
Enable certain features present in the Concepts TS.
+fconcepts-diagnostics-depth=
+C++ ObjC++ Joined RejectNegative UInteger Var(concepts_diagnostics_max_depth)
Init(1)
+Specify maximum error replay depth during recursive diagnosis of a constraint
satisfaction failure.
+
fcond-mismatch
C ObjC C++ ObjC++
Allow the arguments of the '?' operator to have different types.
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index a86bcdf603a..76c520318c3 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -162,6 +162,7 @@ finish_constraint_binary_op (location_t loc,
/* 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;
}
@@ -2395,6 +2396,49 @@ satisfy_conjunction (tree t, tree args, subst_info info)
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
@@ -2412,11 +2456,25 @@ satisfy_disjunction (tree t, tree args, subst_info info)
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;
}
@@ -3182,10 +3240,14 @@ diagnose_valid_expression (tree expr, tree args, tree
in_decl)
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;
}
@@ -3198,10 +3260,14 @@ diagnose_valid_type (tree type, tree args, tree in_decl)
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;
}
@@ -3280,11 +3346,16 @@ diagnose_nested_requirement (tree req, tree args)
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
@@ -3385,14 +3456,38 @@ diagnosing_failed_constraint (tree t, tree args, bool
diag)
: 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
@@ -3403,11 +3498,26 @@ diagnose_constraints (location_t loc, tree t, tree args)
{
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"
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4e1d0f1d42e..af9c4516996 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7833,6 +7833,7 @@ struct diagnosing_failed_constraint
{
diagnosing_failed_constraint (tree, tree, bool);
~diagnosing_failed_constraint ();
+ static bool replay_errors_p ();
bool diagnosing_error;
};
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C
b/gcc/testsuite/g++.dg/concepts/diagnostic2.C
index ce51b71fa8b..47accb8366e 100644
--- a/gcc/testsuite/g++.dg/concepts/diagnostic2.C
+++ b/gcc/testsuite/g++.dg/concepts/diagnostic2.C
@@ -5,7 +5,7 @@ template<typename T>
inline constexpr bool foo_v = false;
template<typename T>
- concept foo = foo_v<T> || foo_v<T&>; // { dg-message "neither operand" }
+ concept foo = foo_v<T> || foo_v<T&>; // { dg-message "no operand" }
/* { dg-begin-multiline-output "" }
concept foo = foo_v<T> || foo_v<T&>;
~~~~~~~~~^~~~~~~~~~~~
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic5.C
b/gcc/testsuite/g++.dg/concepts/diagnostic5.C
new file mode 100644
index 00000000000..2641dc18423
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/diagnostic5.C
@@ -0,0 +1,46 @@
+// { 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" }
+}