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" } +} -- 2.25.1.291.ge68e29171c