From ac8f92c18886057261d61d45b8d339629464c77e Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 24 Oct 2022 17:17:24 -0400 Subject: [PATCH] c++: improve failed constexpr assume diagnostic I noticed that we were printing "the comparison reduces to (x == 42)" when we should be able to give the value of x. Fixed by doing the same evaluation in diagnose_failing_condition that we already do in find_failing_clause. gcc/cp/ChangeLog: * constexpr.cc (fold_operand): New function. (find_failing_clause_r): Add const. (find_failing_clause): Add const. (diagnose_failing_condition): Add ctx parameter. (cxx_eval_internal_function): Pass it. * semantics.cc (diagnose_failing_condition): Move to constexpr.cc. * cp-tree.h: Adjust. gcc/testsuite/ChangeLog: * g++.dg/cpp23/attr-assume2.C: Expect constant values. --- gcc/cp/constexpr.cc | 63 ++++++++++++++++++++++++------- gcc/cp/cp-tree.h | 5 ++- gcc/cp/semantics.cc | 27 ------------- gcc/testsuite/g++.dg/cpp23/attr-assume2.C | 4 +- 4 files changed, 55 insertions(+), 44 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 94b54fc..fc1bc53 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1799,6 +1799,24 @@ cx_error_context (void) return r; } +/* E is an operand of a failed assertion, fold it either with or without + constexpr context. */ + +static tree +fold_operand (tree e, const constexpr_ctx *ctx) +{ + if (ctx) + { + bool new_non_constant_p = false, new_overflow_p = false; + e = cxx_eval_constant_expression (ctx, e, vc_prvalue, + &new_non_constant_p, + &new_overflow_p); + } + else + e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true); + return e; +} + /* If we have a condition in conjunctive normal form (CNF), find the first failing clause. In other words, given an expression like @@ -1807,7 +1825,7 @@ cx_error_context (void) return the first 'false'. EXPR is the expression. */ static tree -find_failing_clause_r (constexpr_ctx *ctx, tree expr) +find_failing_clause_r (const constexpr_ctx *ctx, tree expr) { if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR) { @@ -1818,16 +1836,7 @@ find_failing_clause_r (constexpr_ctx *ctx, tree expr) e = find_failing_clause_r (ctx, TREE_OPERAND (expr, 1)); return e; } - tree e = contextual_conv_bool (expr, tf_none); - if (ctx) - { - bool new_non_constant_p = false, new_overflow_p = false; - e = cxx_eval_constant_expression (ctx, e, vc_prvalue, - &new_non_constant_p, - &new_overflow_p); - } - else - e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true); + tree e = fold_operand (expr, ctx); if (integer_zerop (e)) /* This is the failing clause. */ return expr; @@ -1837,7 +1846,7 @@ find_failing_clause_r (constexpr_ctx *ctx, tree expr) /* Wrapper for find_failing_clause_r. */ tree -find_failing_clause (constexpr_ctx *ctx, tree expr) +find_failing_clause (const constexpr_ctx *ctx, tree expr) { if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR) if (tree e = find_failing_clause_r (ctx, expr)) @@ -1845,6 +1854,34 @@ find_failing_clause (constexpr_ctx *ctx, tree expr) return expr; } +/* Emit additional diagnostics for failing condition BAD. + Used by finish_static_assert and IFN_ASSUME constexpr diagnostics. + If SHOW_EXPR_P is true, print the condition (because it was + instantiation-dependent). */ + +void +diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p, + const constexpr_ctx *ctx /* = nullptr */) +{ + /* Nobody wants to see the artificial (bool) cast. */ + bad = tree_strip_nop_conversions (bad); + + /* Actually explain the failure if this is a concept check or a + requires-expression. */ + if (concept_check_p (bad) || TREE_CODE (bad) == REQUIRES_EXPR) + diagnose_constraints (cloc, bad, NULL_TREE); + else if (COMPARISON_CLASS_P (bad) + && ARITHMETIC_TYPE_P (TREE_TYPE (TREE_OPERAND (bad, 0)))) + { + tree op0 = fold_operand (TREE_OPERAND (bad, 0), ctx); + tree op1 = fold_operand (TREE_OPERAND (bad, 1), ctx); + tree cond = build2 (TREE_CODE (bad), boolean_type_node, op0, op1); + inform (cloc, "the comparison reduces to %qE", cond); + } + else if (show_expr_p) + inform (cloc, "%qE evaluates to false", bad); +} + /* Evaluate a call T to a GCC internal function when possible and return the evaluated result or, under the control of CTX, give an error, set NON_CONSTANT_P, and return the unevaluated call T otherwise. */ @@ -1897,7 +1934,7 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, /* Report the error. */ error_at (cloc, "failed % attribute assumption"); - diagnose_failing_condition (bad, cloc, false); + diagnose_failing_condition (bad, cloc, false, &new_ctx); } *non_constant_p = true; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 2cca20b..cec376d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7758,7 +7758,6 @@ extern tree build_transaction_expr (location_t, tree, int, tree); extern bool cxx_omp_create_clause_info (tree, tree, bool, bool, bool, bool); extern tree baselink_for_fns (tree); -extern void diagnose_failing_condition (tree, location_t, bool); extern void finish_static_assert (tree, tree, location_t, bool, bool); extern tree finish_decltype_type (tree, bool, tsubst_flags_t); @@ -8497,7 +8496,9 @@ extern void clear_cv_and_fold_caches (void); extern tree unshare_constructor (tree CXX_MEM_STAT_INFO); extern bool decl_implicit_constexpr_p (tree); struct constexpr_ctx; -extern tree find_failing_clause (constexpr_ctx *ctx, tree); +extern tree find_failing_clause (const constexpr_ctx *ctx, tree); +extern void diagnose_failing_condition (tree, location_t, bool, + const constexpr_ctx * = nullptr); extern bool replace_decl (tree *, tree, tree); /* An RAII sentinel used to restrict constexpr evaluation so that it diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 82f9dd8..36aa9c4 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -11183,33 +11183,6 @@ init_cp_semantics (void) } -/* Emit additional diagnostics for failing condition BAD. - Used by finish_static_assert and IFN_ASSUME constexpr diagnostics. - If SHOW_EXPR_P is true, print the condition (because it was - instantiation-dependent). */ - -void -diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p) -{ - /* Nobody wants to see the artificial (bool) cast. */ - bad = tree_strip_nop_conversions (bad); - - /* Actually explain the failure if this is a concept check or a - requires-expression. */ - if (concept_check_p (bad) || TREE_CODE (bad) == REQUIRES_EXPR) - diagnose_constraints (cloc, bad, NULL_TREE); - else if (COMPARISON_CLASS_P (bad) - && ARITHMETIC_TYPE_P (TREE_TYPE (TREE_OPERAND (bad, 0)))) - { - tree op0 = fold_non_dependent_expr (TREE_OPERAND (bad, 0)); - tree op1 = fold_non_dependent_expr (TREE_OPERAND (bad, 1)); - tree cond = build2 (TREE_CODE (bad), boolean_type_node, op0, op1); - inform (cloc, "the comparison reduces to %qE", cond); - } - else if (show_expr_p) - inform (cloc, "%qE evaluates to false", bad); -} - /* Build a STATIC_ASSERT for a static assertion with the condition CONDITION and the message text MESSAGE. LOCATION is the location of the static assertion in the source code. When MEMBER_P, this diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume2.C b/gcc/testsuite/g++.dg/cpp23/attr-assume2.C index 9e54c14..4dd1221 100644 --- a/gcc/testsuite/g++.dg/cpp23/attr-assume2.C +++ b/gcc/testsuite/g++.dg/cpp23/attr-assume2.C @@ -26,7 +26,7 @@ f2 (int x) { #if __cpp_constexpr >= 201304L [[assume (x == 42)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } } -#endif // { dg-message "the comparison reduces to '\\\(x == 42\\\)'" "" { target c++14 } .-1 } +#endif // { dg-message "the comparison reduces to '\\\(44 == 42\\\)'" "" { target c++14 } .-1 } return x; } @@ -76,7 +76,7 @@ f7 (int x, int y, int z, int w) { #if __cpp_constexpr >= 201304L [[assume (x == 42 && y == 43 && z == 44 && w == 45)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } } -#endif // { dg-message "the comparison reduces to '\\\(z == 44\\\)'" "" { target c++14 } .-1 } +#endif // { dg-message "the comparison reduces to '\\\(45 == 44\\\)'" "" { target c++14 } .-1 } return x; } -- 2.7.4