From 44f6b7fb574ff197443374dd042ca02ad91e1914 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Thu, 20 Feb 2020 15:56:20 -0500 Subject: [PATCH] c++: Some improvements to concept diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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> || __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> | ^~ Second, this patch changes diagnose_atomic_constraint to print unsatisfied atomic constraint expressions with their template arguments. So e.g. we now print cpp2a/concepts-pr67719.C:9:8: note: the expression ‘(... &&(C)()) [with Tx = {int, long int, void}]’ evaluated to ‘false’ instead of cpp2a/concepts-pr67719.C:9:8: note: the expression ‘(... &&(C)())’ evaluated to ‘false’ Tested on x86_64-pc-linux-gnu, and verified that all the diagnostics emitted in our concept tests are no worse with this patch. 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/ChangeLog | 16 ++++++++++ gcc/cp/constraint.cc | 33 ++++++++++++--------- gcc/cp/cxx-pretty-print.c | 17 +++++++++++ gcc/testsuite/ChangeLog | 5 ++++ gcc/testsuite/g++.dg/concepts/diagnostic2.C | 30 +++++++++++++++++++ gcc/testsuite/g++.dg/concepts/diagnostic3.C | 29 ++++++++++++++++++ 6 files changed, 116 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/ChangeLog b/gcc/cp/ChangeLog index d34730c6ec1..5962981d3c0 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,19 @@ +2020-02-26 Patrick Palka + + * 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. + 2020-02-26 Marek Polacek PR c++/93676 - value-init crash in template. diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 58044cd0f9d..4bb4a3f7252 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 + with 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 %", expr); + tree a = copy_node (t); + ATOMIC_CONSTR_MAP (a) = map; + inform (loc, "the expression %qE evaluated to %", a); + ggc_free (a); } } diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 9e0c5fc8891..397bdbfa234 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/ChangeLog b/gcc/testsuite/ChangeLog index 572989b2c7a..662ffbfd11e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2020-02-26 Patrick Palka + + * g++.dg/concepts/diagnostic2.C: New test. + * g++.dg/concepts/diagnostic3.C: New test. + 2020-02-26 Marek Polacek PR c++/93676 - value-init crash in template. 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 + inline constexpr bool foo_v = false; + +template + concept foo = foo_v || foo_v; // { dg-message "neither operand" } +/* { dg-begin-multiline-output "" } + concept foo = foo_v || foo_v; + ~~~~~~~~~^~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + +template + requires foo + void bar(); +/* { dg-begin-multiline-output "" } + void bar(); + { dg-end-multiline-output "" } */ +/* { dg-prune-output "~" } */ + +void +baz() +{ + bar(); // { dg-error "unsatisfied constraints" } +/* { dg-begin-multiline-output "" } + bar(); + ^ + { 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 + inline constexpr bool foo_v = false; + +template + concept foo = (bool)(foo_v | foo_v); + +template +requires (foo && ...) +void +bar() // { dg-message "with Ts = .int, char... evaluated to .false." } +{ } + +template +struct S { }; + +template +requires (foo> && ...) +void +baz() // { dg-message "with Is = .2, 3, 4... evaluated to .false." } +{ } + +void +baz() +{ + bar(); // { dg-error "unsatisfied constraints" } + baz<2,3,4>(); // { dg-error "unsatisfied constraints" } +} -- 2.30.2