c++: Replay errors during diagnosis of constraint satisfaction failures
authorPatrick Palka <ppalka@redhat.com>
Sat, 28 Mar 2020 12:43:20 +0000 (08:43 -0400)
committerPatrick Palka <ppalka@redhat.com>
Sat, 28 Mar 2020 12:43:20 +0000 (08:43 -0400)
commit75defde9fb56157d5f0279720d48866925b71b19
tree9aacf4a114baa2bc27601c688bd699e3d5ffd9b5
parentc6a562de88c44a555e1688c212869b20b02151bc
c++: Replay errors during diagnosis of constraint satisfaction failures

This patch adds a new flag -fconcepts-diagnostics-depth to the C++ frontend
which controls how deeply we replay errors when diagnosing a constraint
satisfaction failure.  The default is -fconcepts-diagnostics-depth=1 which
diagnoses only the topmost constraint satisfaction failure and is consistent
with our behavior before this patch.  By increasing this flag's value, the user
can control how deeply they want the compiler to explain a constraint
satisfaction error.

For example, if the unsatisfied constraint is a disjunction, then the default
behavior is to just say "no branch in the disjunction is satisfied", but with
-fconcepts-diagnostics-depth=2 we will additionally replay and diagnose the
error in each branch of the disjunction.  And if the unsatisfied constraint is a
requires expression, then we will replay the error in the requires expression,
etc.  This proceeds recursively until there is nothing more to replay or we
exceeded the maximum depth specified by the flag.

Implementation wise, this patch essentially just uncomments the existing
commented-out code that performs the error-replaying, and along the way adds
logic to keep track of and limit the current replay depth.  Besides that, there
is a new routine collect_operands_of_disjunction which flattens a disjunction
and collects all of its operands into a vector.

The extra diagnostics enabled by this flag are at times longer than they need to
be (e.g.  "the operand is_array_v<...> is unsatisfied because \n the expression
is_array_v<...> [with ...] evaluated to false") and not immediately easy to
follow (especially when there are nested disjunctions), but the transparency
provided by these optional diagnostics seems to be pretty helpful in practice.

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/ChangeLog
gcc/c-family/c.opt
gcc/cp/ChangeLog
gcc/cp/constraint.cc
gcc/cp/cp-tree.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/concepts/diagnostic2.C
gcc/testsuite/g++.dg/concepts/diagnostic5.C [new file with mode: 0644]