On Mon, 16 Mar 2020, Patrick Palka wrote: > Hi Martin, > > On Sun, 15 Mar 2020, Martin Sebor wrote: > > > On 3/11/20 4:18 PM, Patrick Palka via Gcc-patches wrote: > > ... > > > Hmm, like this? This version introduces a new static member function > > > diagnosing_failed_constraint::replay_errors_p that checks > > > current_constraint_diagnosis_depth, and also sets max_depth_exceeded_p > > > when appropriate. > > > > > ... > > > > Just one small quoting nit: > > > > > > > @@ -3368,11 +3464,25 @@ 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 -fconcepts-diagnostics-depth= to at least %d for more > > > detail", > > > + concepts_diagnostics_max_depth + 1); > > > > > > Can you please quote the option name in the diagnostic (e.g., by using > > "set %qs to...", "-fconcepts-diagnostics-depth=" or equivalent) to avoid > > -Wformat-diag? It won't cause errors (yet) but will eventually need to > > be cleaned up. > > Yes sure, consider it done. I've amended the patch locally with this > change.
Here's a rebased version of this patch with the above diagnostic change, no other changes made (aside from a minor adjustment to diagnostic5.C): -- >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" } +} -- 2.26.0.rc1.11.g30e9940356