Message ID | 20200221002703.2389863-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: Some improvements to concept diagnostics | expand |
On 2/20/20 7:27 PM, Patrick Palka wrote: > This patch improves our concept diagnostics in two ways. First, it sets a more > precise location for the constraint expressions built in > finish_constraint_binary_op. As a result, when a disjunction is unsatisfied we > now print e.g. > > .../include/bits/range_access.h:467:2: note: neither operand of the disjunction is satisfied > 466 | requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp> > | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > 467 | || __adl_end<_Tp> > | ^~~~~~~~~~~~~~~~~ > > instead of > > .../include/bits/range_access.h:467:2: note: neither operand of the disjunction is satisfied > 467 | || __adl_end<_Tp> > | ^~ This hunk is OK. > Second, this patch changes diagnose_atomic_constraint to pretty-print > unsatisfied atomic constraint expressions with their template arguments > substituted in (if doing so does not result in a trivial expression). So for > example we now emit > > cpp2a/concepts-pr67719.C:9:8: note: the expression ‘((C<int>() && C<long int>()) && C<void>())’ evaluated to ‘false’ > > instead of > > cpp2a/concepts-pr67719.C:9:8: note: the expression ‘(... &&(C<Tx>)())’ evaluated to ‘false’ Generally with templates we try to print the dependent form and then the arguments; pp_cxx_atomic_constraint already wants to do that, but we don't get there from here. Perhaps pass 'map' instead of 'args' into diagnose_atomic_constraint, and then build a new ATOMIC_CONSTR for printing with %E? > Tested on x86_64-pc-linux-gnu, and verified that all the diagnostics emitted in > our concept tests are no worse with this patch. Does this look OK to commit? > > gcc/cp/ChangeLog: > > * constraint.cc (finish_constraint_binary_op): Set expr's location range > to the range of its operands. > (diagnose_atomic_constraint): Prefer to pretty-print the atomic > constraint with template arguments substituted in. > > gcc/testsuite/ChangeLog: > > * g++.dg/concepts/diagnostic2.C: New test. > * g++.dg/concepts/diagnostic3.C: New test. > --- > gcc/cp/constraint.cc | 15 +++++++---- > gcc/testsuite/g++.dg/concepts/diagnostic2.C | 30 +++++++++++++++++++++ > gcc/testsuite/g++.dg/concepts/diagnostic3.C | 19 +++++++++++++ > 3 files changed, 59 insertions(+), 5 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic2.C > create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic3.C > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 58044cd0f9d..4f6bc11e7e8 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -155,14 +155,14 @@ finish_constraint_binary_op (location_t loc, > if (!check_constraint_operands (loc, lhs, rhs)) > return error_mark_node; > tree overload; > - tree expr = build_x_binary_op (loc, code, > - lhs, TREE_CODE (lhs), > - rhs, TREE_CODE (rhs), > - &overload, tf_none); > + cp_expr expr = build_x_binary_op (loc, code, > + lhs, TREE_CODE (lhs), > + rhs, TREE_CODE (rhs), > + &overload, tf_none); > /* When either operand is dependent, the overload set may be non-empty. */ > if (expr == error_mark_node) > return error_mark_node; > - SET_EXPR_LOCATION (expr, loc); > + expr.set_range (lhs.get_start (), rhs.get_finish ()); > return expr; > } > > @@ -3330,6 +3330,11 @@ diagnose_atomic_constraint (tree t, tree args, subst_info info) > inform (loc, "%qE is never satisfied", expr); > break; > default: > + tree orig_expr = expr; > + expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false); > + if (CONSTANT_CLASS_P (expr)) > + /* We are better off printing the original expression. */ > + expr = orig_expr; > inform (loc, "the expression %qE evaluated to %<false%>", expr); > } > } > diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C b/gcc/testsuite/g++.dg/concepts/diagnostic2.C > new file mode 100644 > index 00000000000..ce51b71fa8b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/concepts/diagnostic2.C > @@ -0,0 +1,30 @@ > +// { dg-do compile { target c++2a } } > +// { dg-options "-fdiagnostics-show-caret" } > + > +template<typename T> > + inline constexpr bool foo_v = false; > + > +template<typename T> > + concept foo = foo_v<T> || foo_v<T&>; // { dg-message "neither operand" } > +/* { dg-begin-multiline-output "" } > + concept foo = foo_v<T> || foo_v<T&>; > + ~~~~~~~~~^~~~~~~~~~~~ > + { dg-end-multiline-output "" } */ > + > +template<typename T> > + requires foo<T> > + void bar(); > +/* { dg-begin-multiline-output "" } > + void bar(); > + { dg-end-multiline-output "" } */ > +/* { dg-prune-output "~" } */ > + > +void > +baz() > +{ > + bar<int>(); // { dg-error "unsatisfied constraints" } > +/* { dg-begin-multiline-output "" } > + bar<int>(); > + ^ > + { dg-end-multiline-output "" } */ > +} > diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic3.C b/gcc/testsuite/g++.dg/concepts/diagnostic3.C > new file mode 100644 > index 00000000000..e0649dac51a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/concepts/diagnostic3.C > @@ -0,0 +1,19 @@ > +// { dg-do compile { target c++2a } } > + > +template<typename T> > + inline constexpr bool foo_v = false; > + > +template<typename T> > + concept foo = (bool)(foo_v<T> | foo_v<T&>); // { dg-message "foo_v<int>.*foo_v<int&>" } > + > +template<typename T> > +requires foo<T> > +void > +bar() > +{ } > + > +void > +baz() > +{ > + bar<int>(); // { dg-error "unsatisfied constraints" } > +} >
On Mon, 24 Feb 2020, Jason Merrill wrote: > On 2/20/20 7:27 PM, Patrick Palka wrote: > > This patch improves our concept diagnostics in two ways. First, it sets a > > more > > precise location for the constraint expressions built in > > finish_constraint_binary_op. As a result, when a disjunction is unsatisfied > > we > > now print e.g. > > > > .../include/bits/range_access.h:467:2: note: neither operand of the > > disjunction is satisfied > > 466 | requires is_bounded_array_v<remove_reference_t<_Tp>> || > > __member_end<_Tp> > > | > > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > 467 | || __adl_end<_Tp> > > | ^~~~~~~~~~~~~~~~~ > > > > instead of > > > > .../include/bits/range_access.h:467:2: note: neither operand of the > > disjunction is satisfied > > 467 | || __adl_end<_Tp> > > | ^~ > > This hunk is OK. > > > Second, this patch changes diagnose_atomic_constraint to pretty-print > > unsatisfied atomic constraint expressions with their template arguments > > substituted in (if doing so does not result in a trivial expression). So > > for > > example we now emit > > > > cpp2a/concepts-pr67719.C:9:8: note: the expression ‘((C<int>() && C<long > > int>()) && C<void>())’ evaluated to ‘false’ > > > > instead of > > > > cpp2a/concepts-pr67719.C:9:8: note: the expression ‘(... &&(C<Tx>)())’ > > evaluated to ‘false’ > > Generally with templates we try to print the dependent form and then the > arguments; pp_cxx_atomic_constraint already wants to do that, but we don't get > there from here. Perhaps pass 'map' instead of 'args' into > diagnose_atomic_constraint, and then build a new ATOMIC_CONSTR for printing > with %E? That works really well. I had to add handling of TYPE_ARGUMENT_PACK to cxx_pretty_printer::type_id because otherwise an "unsupported type_argument_pack" placeholder would get printed in these diagnostics instead. For consistency with the way we print TYPE_ARGUMENT_PACKs and NONTYPE_ARGUMENT_PACKs elsewhere I also made cxx_pretty_printer::expression print curly braces around a NONTYPE_ARGUMENT_PACK. Tested on x86_64-pc-linux-gnu, does this look OK? -- >8 -- gcc/cp/ChangeLog: * constraint.cc (finish_constraint_binary_op): Set expr's location range to the range of its operands. (satisfy_atom): Pass MAP instead of ARGS to diagnose_atomic_constraint. (diagnose_trait_expr): Take the instantiated parameter mapping MAP instead of the corresponding template arguments ARGS and adjust body accordingly. (diagnose_requires_expr): Likewise. (diagnose_atomic_constraint): Likewise. When printing an atomic constraint expression, print the instantiated parameter mapping alongside it. * cxx-pretty-print.cc (cxx_pretty_printer::expression) [NONTYPE_ARGUMENT_PACK]: Print braces around a NONTYPE_ARGUMENT_PACK. (cxx_pretty_printer::type_id): Handle TYPE_ARGUMENT_PACK. gcc/testsuite/ChangeLog: * g++.dg/concepts/diagnostic2.C: New test. * g++.dg/concepts/diagnostic3.C: New test. --- gcc/cp/constraint.cc | 33 ++++++++++++--------- gcc/cp/cxx-pretty-print.c | 17 +++++++++++ gcc/testsuite/g++.dg/concepts/diagnostic2.C | 30 +++++++++++++++++++ gcc/testsuite/g++.dg/concepts/diagnostic3.C | 29 ++++++++++++++++++ 4 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic2.C create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic3.C diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 58044cd0f9d..4e6ed440211 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -155,14 +155,14 @@ finish_constraint_binary_op (location_t loc, if (!check_constraint_operands (loc, lhs, rhs)) return error_mark_node; tree overload; - tree expr = build_x_binary_op (loc, code, - lhs, TREE_CODE (lhs), - rhs, TREE_CODE (rhs), - &overload, tf_none); + cp_expr expr = build_x_binary_op (loc, code, + lhs, TREE_CODE (lhs), + rhs, TREE_CODE (rhs), + &overload, tf_none); /* When either operand is dependent, the overload set may be non-empty. */ if (expr == error_mark_node) return error_mark_node; - SET_EXPR_LOCATION (expr, loc); + expr.set_range (lhs.get_start (), rhs.get_finish ()); return expr; } @@ -2547,7 +2547,7 @@ satisfy_atom (tree t, tree args, subst_info info) /* Compute the value of the constraint. */ result = satisfaction_value (cxx_constant_value (result)); if (result == boolean_false_node && info.noisy ()) - diagnose_atomic_constraint (t, args, info); + diagnose_atomic_constraint (t, map, info); return cache.save (result); } @@ -3056,9 +3056,10 @@ get_constraint_error_location (tree t) /* Emit a diagnostic for a failed trait. */ void -diagnose_trait_expr (tree expr, tree args) +diagnose_trait_expr (tree expr, tree map) { location_t loc = cp_expr_location (expr); + tree args = get_mapped_args (map); /* Build a "fake" version of the instantiated trait, so we can get the instantiated types from result. */ @@ -3271,11 +3272,12 @@ diagnose_requirement (tree req, tree args, tree in_decl) } static void -diagnose_requires_expr (tree expr, tree args, tree in_decl) +diagnose_requires_expr (tree expr, tree map, tree in_decl) { local_specialization_stack stack (lss_copy); tree parms = TREE_OPERAND (expr, 0); tree body = TREE_OPERAND (expr, 1); + tree args = get_mapped_args (map); cp_unevaluated u; subst_info info (tf_warning_or_error, NULL_TREE); @@ -3292,11 +3294,11 @@ diagnose_requires_expr (tree expr, tree args, tree in_decl) } } -/* Diagnose a substitution failure in the atomic constraint T. Note that - ARGS have been previously instantiated through the parameter map. */ +/* Diagnose a substitution failure in the atomic constraint T when applied + to the instantiated parameter mapping MAP. */ static void -diagnose_atomic_constraint (tree t, tree args, subst_info info) +diagnose_atomic_constraint (tree t, tree map, subst_info info) { /* If the constraint is already ill-formed, we've previously diagnosed the reason. We should still say why the constraints aren't satisfied. */ @@ -3320,17 +3322,20 @@ diagnose_atomic_constraint (tree t, tree args, subst_info info) switch (TREE_CODE (expr)) { case TRAIT_EXPR: - diagnose_trait_expr (expr, args); + diagnose_trait_expr (expr, map); break; case REQUIRES_EXPR: - diagnose_requires_expr (expr, args, info.in_decl); + diagnose_requires_expr (expr, map, info.in_decl); break; case INTEGER_CST: /* This must be either 0 or false. */ inform (loc, "%qE is never satisfied", expr); break; default: - inform (loc, "the expression %qE evaluated to %<false%>", expr); + tree a = copy_node (t); + ATOMIC_CONSTR_MAP (a) = map; + inform (loc, "the expression %qE evaluated to %<false%>", a); + ggc_free (a); } } diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 9e0c5fc8891..119432ebb32 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -1214,12 +1214,14 @@ cxx_pretty_printer::expression (tree t) { tree args = ARGUMENT_PACK_ARGS (t); int i, len = TREE_VEC_LENGTH (args); + pp_cxx_left_brace (this); for (i = 0; i < len; ++i) { if (i > 0) pp_cxx_separate_with (this, ','); expression (TREE_VEC_ELT (args, i)); } + pp_cxx_right_brace (this); } break; @@ -1839,6 +1841,21 @@ cxx_pretty_printer::type_id (tree t) pp_cxx_ws_string (this, "..."); break; + case TYPE_ARGUMENT_PACK: + { + tree args = ARGUMENT_PACK_ARGS (t); + int len = TREE_VEC_LENGTH (args); + pp_cxx_left_brace (this); + for (int i = 0; i < len; ++i) + { + if (i > 0) + pp_cxx_separate_with (this, ','); + type_id (TREE_VEC_ELT (args, i)); + } + pp_cxx_right_brace (this); + break; + } + default: c_pretty_printer::type_id (t); break; diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C b/gcc/testsuite/g++.dg/concepts/diagnostic2.C new file mode 100644 index 00000000000..ce51b71fa8b --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/diagnostic2.C @@ -0,0 +1,30 @@ +// { dg-do compile { target c++2a } } +// { dg-options "-fdiagnostics-show-caret" } + +template<typename T> + inline constexpr bool foo_v = false; + +template<typename T> + concept foo = foo_v<T> || foo_v<T&>; // { dg-message "neither operand" } +/* { dg-begin-multiline-output "" } + concept foo = foo_v<T> || foo_v<T&>; + ~~~~~~~~~^~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + +template<typename T> + requires foo<T> + void bar(); +/* { dg-begin-multiline-output "" } + void bar(); + { dg-end-multiline-output "" } */ +/* { dg-prune-output "~" } */ + +void +baz() +{ + bar<int>(); // { dg-error "unsatisfied constraints" } +/* { dg-begin-multiline-output "" } + bar<int>(); + ^ + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic3.C b/gcc/testsuite/g++.dg/concepts/diagnostic3.C new file mode 100644 index 00000000000..b4c75409b94 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/diagnostic3.C @@ -0,0 +1,29 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + inline constexpr bool foo_v = false; + +template<typename T> + concept foo = (bool)(foo_v<T> | foo_v<T&>); + +template<typename... Ts> +requires (foo<Ts> && ...) +void +bar() // { dg-message "with Ts = .int, char... evaluated to .false." } +{ } + +template<int> +struct S { }; + +template<int... Is> +requires (foo<S<Is>> && ...) +void +baz() // { dg-message "with Is = .2, 3, 4... evaluated to .false." } +{ } + +void +baz() +{ + bar<int, char>(); // { dg-error "unsatisfied constraints" } + baz<2,3,4>(); // { dg-error "unsatisfied constraints" } +}
On 2/24/20 12:30 PM, Patrick Palka wrote: > On Mon, 24 Feb 2020, Jason Merrill wrote: > >> On 2/20/20 7:27 PM, Patrick Palka wrote: >>> This patch improves our concept diagnostics in two ways. First, it sets a >>> more >>> precise location for the constraint expressions built in >>> finish_constraint_binary_op. As a result, when a disjunction is unsatisfied >>> we >>> now print e.g. >>> >>> .../include/bits/range_access.h:467:2: note: neither operand of the >>> disjunction is satisfied >>> 466 | requires is_bounded_array_v<remove_reference_t<_Tp>> || >>> __member_end<_Tp> >>> | >>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >>> 467 | || __adl_end<_Tp> >>> | ^~~~~~~~~~~~~~~~~ >>> >>> instead of >>> >>> .../include/bits/range_access.h:467:2: note: neither operand of the >>> disjunction is satisfied >>> 467 | || __adl_end<_Tp> >>> | ^~ >> >> This hunk is OK. >> >>> Second, this patch changes diagnose_atomic_constraint to pretty-print >>> unsatisfied atomic constraint expressions with their template arguments >>> substituted in (if doing so does not result in a trivial expression). So >>> for >>> example we now emit >>> >>> cpp2a/concepts-pr67719.C:9:8: note: the expression ‘((C<int>() && C<long >>> int>()) && C<void>())’ evaluated to ‘false’ >>> >>> instead of >>> >>> cpp2a/concepts-pr67719.C:9:8: note: the expression ‘(... &&(C<Tx>)())’ >>> evaluated to ‘false’ >> >> Generally with templates we try to print the dependent form and then the >> arguments; pp_cxx_atomic_constraint already wants to do that, but we don't get >> there from here. Perhaps pass 'map' instead of 'args' into >> diagnose_atomic_constraint, and then build a new ATOMIC_CONSTR for printing >> with %E? > > That works really well. I had to add handling of TYPE_ARGUMENT_PACK to > cxx_pretty_printer::type_id because otherwise an "unsupported > type_argument_pack" placeholder would get printed in these diagnostics > instead. For consistency with the way we print TYPE_ARGUMENT_PACKs and > NONTYPE_ARGUMENT_PACKs elsewhere I also made > cxx_pretty_printer::expression print curly braces around a > NONTYPE_ARGUMENT_PACK. Tested on x86_64-pc-linux-gnu, does this look > OK? OK. > -- >8 -- > > gcc/cp/ChangeLog: > > * constraint.cc (finish_constraint_binary_op): Set expr's location range > to the range of its operands. > (satisfy_atom): Pass MAP instead of ARGS to diagnose_atomic_constraint. > (diagnose_trait_expr): Take the instantiated parameter mapping MAP > instead of the corresponding template arguments ARGS and adjust body > accordingly. > (diagnose_requires_expr): Likewise. > (diagnose_atomic_constraint): Likewise. When printing an atomic > constraint expression, print the instantiated parameter mapping > alongside it. > * cxx-pretty-print.cc (cxx_pretty_printer::expression) > [NONTYPE_ARGUMENT_PACK]: Print braces around a NONTYPE_ARGUMENT_PACK. > (cxx_pretty_printer::type_id): Handle TYPE_ARGUMENT_PACK. > > gcc/testsuite/ChangeLog: > > * g++.dg/concepts/diagnostic2.C: New test. > * g++.dg/concepts/diagnostic3.C: New test. > --- > gcc/cp/constraint.cc | 33 ++++++++++++--------- > gcc/cp/cxx-pretty-print.c | 17 +++++++++++ > gcc/testsuite/g++.dg/concepts/diagnostic2.C | 30 +++++++++++++++++++ > gcc/testsuite/g++.dg/concepts/diagnostic3.C | 29 ++++++++++++++++++ > 4 files changed, 95 insertions(+), 14 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic2.C > create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic3.C > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 58044cd0f9d..4e6ed440211 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -155,14 +155,14 @@ finish_constraint_binary_op (location_t loc, > if (!check_constraint_operands (loc, lhs, rhs)) > return error_mark_node; > tree overload; > - tree expr = build_x_binary_op (loc, code, > - lhs, TREE_CODE (lhs), > - rhs, TREE_CODE (rhs), > - &overload, tf_none); > + cp_expr expr = build_x_binary_op (loc, code, > + lhs, TREE_CODE (lhs), > + rhs, TREE_CODE (rhs), > + &overload, tf_none); > /* When either operand is dependent, the overload set may be non-empty. */ > if (expr == error_mark_node) > return error_mark_node; > - SET_EXPR_LOCATION (expr, loc); > + expr.set_range (lhs.get_start (), rhs.get_finish ()); > return expr; > } > > @@ -2547,7 +2547,7 @@ satisfy_atom (tree t, tree args, subst_info info) > /* Compute the value of the constraint. */ > result = satisfaction_value (cxx_constant_value (result)); > if (result == boolean_false_node && info.noisy ()) > - diagnose_atomic_constraint (t, args, info); > + diagnose_atomic_constraint (t, map, info); > > return cache.save (result); > } > @@ -3056,9 +3056,10 @@ get_constraint_error_location (tree t) > /* Emit a diagnostic for a failed trait. */ > > void > -diagnose_trait_expr (tree expr, tree args) > +diagnose_trait_expr (tree expr, tree map) > { > location_t loc = cp_expr_location (expr); > + tree args = get_mapped_args (map); > > /* Build a "fake" version of the instantiated trait, so we can > get the instantiated types from result. */ > @@ -3271,11 +3272,12 @@ diagnose_requirement (tree req, tree args, tree in_decl) > } > > static void > -diagnose_requires_expr (tree expr, tree args, tree in_decl) > +diagnose_requires_expr (tree expr, tree map, tree in_decl) > { > local_specialization_stack stack (lss_copy); > tree parms = TREE_OPERAND (expr, 0); > tree body = TREE_OPERAND (expr, 1); > + tree args = get_mapped_args (map); > > cp_unevaluated u; > subst_info info (tf_warning_or_error, NULL_TREE); > @@ -3292,11 +3294,11 @@ diagnose_requires_expr (tree expr, tree args, tree in_decl) > } > } > > -/* Diagnose a substitution failure in the atomic constraint T. Note that > - ARGS have been previously instantiated through the parameter map. */ > +/* Diagnose a substitution failure in the atomic constraint T when applied > + to the instantiated parameter mapping MAP. */ > > static void > -diagnose_atomic_constraint (tree t, tree args, subst_info info) > +diagnose_atomic_constraint (tree t, tree map, subst_info info) > { > /* If the constraint is already ill-formed, we've previously diagnosed > the reason. We should still say why the constraints aren't satisfied. */ > @@ -3320,17 +3322,20 @@ diagnose_atomic_constraint (tree t, tree args, subst_info info) > switch (TREE_CODE (expr)) > { > case TRAIT_EXPR: > - diagnose_trait_expr (expr, args); > + diagnose_trait_expr (expr, map); > break; > case REQUIRES_EXPR: > - diagnose_requires_expr (expr, args, info.in_decl); > + diagnose_requires_expr (expr, map, info.in_decl); > break; > case INTEGER_CST: > /* This must be either 0 or false. */ > inform (loc, "%qE is never satisfied", expr); > break; > default: > - inform (loc, "the expression %qE evaluated to %<false%>", expr); > + tree a = copy_node (t); > + ATOMIC_CONSTR_MAP (a) = map; > + inform (loc, "the expression %qE evaluated to %<false%>", a); > + ggc_free (a); > } > } > > diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c > index 9e0c5fc8891..119432ebb32 100644 > --- a/gcc/cp/cxx-pretty-print.c > +++ b/gcc/cp/cxx-pretty-print.c > @@ -1214,12 +1214,14 @@ cxx_pretty_printer::expression (tree t) > { > tree args = ARGUMENT_PACK_ARGS (t); > int i, len = TREE_VEC_LENGTH (args); > + pp_cxx_left_brace (this); > for (i = 0; i < len; ++i) > { > if (i > 0) > pp_cxx_separate_with (this, ','); > expression (TREE_VEC_ELT (args, i)); > } > + pp_cxx_right_brace (this); > } > break; > > @@ -1839,6 +1841,21 @@ cxx_pretty_printer::type_id (tree t) > pp_cxx_ws_string (this, "..."); > break; > > + case TYPE_ARGUMENT_PACK: > + { > + tree args = ARGUMENT_PACK_ARGS (t); > + int len = TREE_VEC_LENGTH (args); > + pp_cxx_left_brace (this); > + for (int i = 0; i < len; ++i) > + { > + if (i > 0) > + pp_cxx_separate_with (this, ','); > + type_id (TREE_VEC_ELT (args, i)); > + } > + pp_cxx_right_brace (this); > + break; > + } > + > default: > c_pretty_printer::type_id (t); > break; > diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C b/gcc/testsuite/g++.dg/concepts/diagnostic2.C > new file mode 100644 > index 00000000000..ce51b71fa8b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/concepts/diagnostic2.C > @@ -0,0 +1,30 @@ > +// { dg-do compile { target c++2a } } > +// { dg-options "-fdiagnostics-show-caret" } > + > +template<typename T> > + inline constexpr bool foo_v = false; > + > +template<typename T> > + concept foo = foo_v<T> || foo_v<T&>; // { dg-message "neither operand" } > +/* { dg-begin-multiline-output "" } > + concept foo = foo_v<T> || foo_v<T&>; > + ~~~~~~~~~^~~~~~~~~~~~ > + { dg-end-multiline-output "" } */ > + > +template<typename T> > + requires foo<T> > + void bar(); > +/* { dg-begin-multiline-output "" } > + void bar(); > + { dg-end-multiline-output "" } */ > +/* { dg-prune-output "~" } */ > + > +void > +baz() > +{ > + bar<int>(); // { dg-error "unsatisfied constraints" } > +/* { dg-begin-multiline-output "" } > + bar<int>(); > + ^ > + { dg-end-multiline-output "" } */ > +} > diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic3.C b/gcc/testsuite/g++.dg/concepts/diagnostic3.C > new file mode 100644 > index 00000000000..b4c75409b94 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/concepts/diagnostic3.C > @@ -0,0 +1,29 @@ > +// { dg-do compile { target c++2a } } > + > +template<typename T> > + inline constexpr bool foo_v = false; > + > +template<typename T> > + concept foo = (bool)(foo_v<T> | foo_v<T&>); > + > +template<typename... Ts> > +requires (foo<Ts> && ...) > +void > +bar() // { dg-message "with Ts = .int, char... evaluated to .false." } > +{ } > + > +template<int> > +struct S { }; > + > +template<int... Is> > +requires (foo<S<Is>> && ...) > +void > +baz() // { dg-message "with Is = .2, 3, 4... evaluated to .false." } > +{ } > + > +void > +baz() > +{ > + bar<int, char>(); // { dg-error "unsatisfied constraints" } > + baz<2,3,4>(); // { dg-error "unsatisfied constraints" } > +} >
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 58044cd0f9d..4f6bc11e7e8 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -155,14 +155,14 @@ finish_constraint_binary_op (location_t loc, if (!check_constraint_operands (loc, lhs, rhs)) return error_mark_node; tree overload; - tree expr = build_x_binary_op (loc, code, - lhs, TREE_CODE (lhs), - rhs, TREE_CODE (rhs), - &overload, tf_none); + cp_expr expr = build_x_binary_op (loc, code, + lhs, TREE_CODE (lhs), + rhs, TREE_CODE (rhs), + &overload, tf_none); /* When either operand is dependent, the overload set may be non-empty. */ if (expr == error_mark_node) return error_mark_node; - SET_EXPR_LOCATION (expr, loc); + expr.set_range (lhs.get_start (), rhs.get_finish ()); return expr; } @@ -3330,6 +3330,11 @@ diagnose_atomic_constraint (tree t, tree args, subst_info info) inform (loc, "%qE is never satisfied", expr); break; default: + tree orig_expr = expr; + expr = tsubst_expr (expr, args, tf_none, NULL_TREE, false); + if (CONSTANT_CLASS_P (expr)) + /* We are better off printing the original expression. */ + expr = orig_expr; inform (loc, "the expression %qE evaluated to %<false%>", expr); } } diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic2.C b/gcc/testsuite/g++.dg/concepts/diagnostic2.C new file mode 100644 index 00000000000..ce51b71fa8b --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/diagnostic2.C @@ -0,0 +1,30 @@ +// { dg-do compile { target c++2a } } +// { dg-options "-fdiagnostics-show-caret" } + +template<typename T> + inline constexpr bool foo_v = false; + +template<typename T> + concept foo = foo_v<T> || foo_v<T&>; // { dg-message "neither operand" } +/* { dg-begin-multiline-output "" } + concept foo = foo_v<T> || foo_v<T&>; + ~~~~~~~~~^~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + +template<typename T> + requires foo<T> + void bar(); +/* { dg-begin-multiline-output "" } + void bar(); + { dg-end-multiline-output "" } */ +/* { dg-prune-output "~" } */ + +void +baz() +{ + bar<int>(); // { dg-error "unsatisfied constraints" } +/* { dg-begin-multiline-output "" } + bar<int>(); + ^ + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic3.C b/gcc/testsuite/g++.dg/concepts/diagnostic3.C new file mode 100644 index 00000000000..e0649dac51a --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/diagnostic3.C @@ -0,0 +1,19 @@ +// { dg-do compile { target c++2a } } + +template<typename T> + inline constexpr bool foo_v = false; + +template<typename T> + concept foo = (bool)(foo_v<T> | foo_v<T&>); // { dg-message "foo_v<int>.*foo_v<int&>" } + +template<typename T> +requires foo<T> +void +bar() +{ } + +void +baz() +{ + bar<int>(); // { dg-error "unsatisfied constraints" } +}