aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite/g++.dg/concepts
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2020-03-28 08:43:20 -0400
committerPatrick Palka <ppalka@redhat.com>2020-03-28 08:43:20 -0400
commit75defde9fb56157d5f0279720d48866925b71b19 (patch)
tree9aacf4a114baa2bc27601c688bd699e3d5ffd9b5 /gcc/testsuite/g++.dg/concepts
parentc6a562de88c44a555e1688c212869b20b02151bc (diff)
downloadgcc-75defde9fb56157d5f0279720d48866925b71b19.zip
gcc-75defde9fb56157d5f0279720d48866925b71b19.tar.gz
gcc-75defde9fb56157d5f0279720d48866925b71b19.tar.bz2
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.
Diffstat (limited to 'gcc/testsuite/g++.dg/concepts')
-rw-r--r--gcc/testsuite/g++.dg/concepts/diagnostic2.C2
-rw-r--r--gcc/testsuite/g++.dg/concepts/diagnostic5.C46
2 files changed, 47 insertions, 1 deletions
diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C b/gcc/testsuite/g++.dg/concepts/diagnostic2.C
index ce51b71..47accb8 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 0000000..2641dc1
--- /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" }
+}