Hi! The following patch fixes __builtin_is_constant_evaluated and __builtin_constant_p handling during static_assert evaluation. finish_static_assert calls fold_non_dependent_expr and complains if the result is not a constant expression, instead of requiring a constant expression, which causes __builtin_is_constant_evaluated () during the evaluation to be not considered as constant expression and __builtin_constant_p calls too if they appear in constexpr functions.
The patch makes sure that manifestly_const_eval is true while evaluating the expression and also makes sure to fold __builtin_constant_p when that is true even when in constexpr functions. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2018-12-12 Jakub Jelinek <ja...@redhat.com> PR c++/86524 PR c++/88446 * cp-tree.h (fold_non_dependent_expr): Add manifestly_const_eval argument. * constexpr.c (cxx_eval_builtin_function_call): Evaluate __builtin_constant_p if ctx->manifestly_const_eval even in constexpr functions. For arguments to builtins, if ctx->manifestly_const_eval try to first evaluate arguments with it, but if that doesn't result in a constant expression, retry without it. Fix comment typo. (fold_non_dependent_expr): Add manifestly_const_eval argument, pass it through to cxx_eval_outermost_constant_expr and maybe_constant_value. * semantics.c (finish_static_assert): Call fold_non_dependent_expr with true as manifestly_const_eval. * g++.dg/cpp1y/constexpr-86524.C: New test. * g++.dg/cpp2a/is-constant-evaluated4.C: New test. * g++.dg/cpp2a/is-constant-evaluated5.C: New test. * g++.dg/cpp2a/is-constant-evaluated6.C: New test. --- gcc/cp/cp-tree.h.jj 2018-12-12 09:32:27.408535853 +0100 +++ gcc/cp/cp-tree.h 2018-12-12 11:07:24.250459779 +0100 @@ -7665,7 +7665,9 @@ extern tree cxx_constant_value (tree, extern tree cxx_constant_init (tree, tree = NULL_TREE); extern tree maybe_constant_value (tree, tree = NULL_TREE, bool = false); extern tree maybe_constant_init (tree, tree = NULL_TREE, bool = false); -extern tree fold_non_dependent_expr (tree, tsubst_flags_t = tf_warning_or_error); +extern tree fold_non_dependent_expr (tree, + tsubst_flags_t = tf_warning_or_error, + bool = false); extern tree fold_simple (tree); extern bool is_sub_constant_expr (tree); extern bool reduced_constant_expression_p (tree); --- gcc/cp/constexpr.c.jj 2018-12-12 09:34:17.531736075 +0100 +++ gcc/cp/constexpr.c 2018-12-12 11:30:33.986756914 +0100 @@ -1198,6 +1198,7 @@ cxx_eval_builtin_function_call (const co in a constexpr function until we have values for the parameters. */ if (bi_const_p && ctx->quiet + && !ctx->manifestly_const_eval && current_function_decl && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) { @@ -1222,7 +1223,6 @@ cxx_eval_builtin_function_call (const co return constant false for a non-constant argument. */ constexpr_ctx new_ctx = *ctx; new_ctx.quiet = true; - bool dummy1 = false, dummy2 = false; for (i = 0; i < nargs; ++i) { args[i] = CALL_EXPR_ARG (t, i); @@ -1231,10 +1231,23 @@ cxx_eval_builtin_function_call (const co of the builtin, verify it here. */ if (!builtin_valid_in_constant_expr_p (fun) || potential_constant_expression (args[i])) - args[i] = cxx_eval_constant_expression (&new_ctx, args[i], false, - &dummy1, &dummy2); + { + bool non_cst_p = false, ovf_p = false; + tree a = cxx_eval_constant_expression (&new_ctx, args[i], false, + &non_cst_p, &ovf_p); + if ((non_cst_p || ovf_p) && ctx->manifestly_const_eval) + { + new_ctx.manifestly_const_eval = false; + non_cst_p = false; + ovf_p = false; + a = cxx_eval_constant_expression (&new_ctx, args[i], false, + &non_cst_p, &ovf_p); + new_ctx.manifestly_const_eval = true; + } + args[i] = a; + } if (bi_const_p) - /* For __built_in_constant_p, fold all expressions with constant values + /* For __builtin_constant_p, fold all expressions with constant values even if they aren't C++ constant-expressions. */ args[i] = cp_fully_fold (args[i]); } @@ -5340,6 +5353,7 @@ clear_cv_and_fold_caches (void) (t, complain) followed by maybe_constant_value but is more efficient, because it calls instantiation_dependent_expression_p and potential_constant_expression at most once. + The manifestly_const_eval argument is passed to maybe_constant_value. Callers should generally pass their active complain, or if they are in a non-template, diagnosing context, they can use the default of @@ -5350,7 +5364,8 @@ clear_cv_and_fold_caches (void) tree fold_non_dependent_expr (tree t, - tsubst_flags_t complain /* = tf_warning_or_error */) + tsubst_flags_t complain /* = tf_warning_or_error */, + bool manifestly_const_eval /* = false */) { if (t == NULL_TREE) return NULL_TREE; @@ -5380,7 +5395,8 @@ fold_non_dependent_expr (tree t, return t; } - tree r = cxx_eval_outermost_constant_expr (t, true, true, false, + tree r = cxx_eval_outermost_constant_expr (t, true, true, + manifestly_const_eval, NULL_TREE); /* cp_tree_equal looks through NOPs, so allow them. */ gcc_checking_assert (r == t @@ -5398,7 +5414,7 @@ fold_non_dependent_expr (tree t, return t; } - return maybe_constant_value (t); + return maybe_constant_value (t, NULL_TREE, manifestly_const_eval); } /* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather --- gcc/cp/semantics.c.jj 2018-12-07 00:23:15.010998822 +0100 +++ gcc/cp/semantics.c 2018-12-12 11:12:14.418720123 +0100 @@ -9189,7 +9189,8 @@ finish_static_assert (tree condition, tr /* Fold the expression and convert it to a boolean value. */ condition = perform_implicit_conversion_flags (boolean_type_node, condition, complain, LOOKUP_NORMAL); - condition = fold_non_dependent_expr (condition, complain); + condition = fold_non_dependent_expr (condition, complain, + /*manifestly_const_eval=*/true); if (TREE_CODE (condition) == INTEGER_CST && !integer_zerop (condition)) /* Do nothing; the condition is satisfied. */ --- gcc/testsuite/g++.dg/cpp1y/constexpr-86524.C.jj 2018-12-12 12:21:43.854621504 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-86524.C 2018-12-12 12:21:59.228370406 +0100 @@ -0,0 +1,41 @@ +// PR c++/86524 +// { dg-do run { target c++14 } } +// { dg-options "-O2" } + +extern "C" void abort (); +typedef __UINTPTR_TYPE__ uintptr_t; + +constexpr bool +foo (const int *x, const int *y) +{ + if (__builtin_constant_p (x < y)) + return x < y; + return (uintptr_t) x < (uintptr_t) y; +} + +void +bar () +{ + constexpr int x = 0; + static_assert (!(&x < &x)); + static_assert (!foo (&x, &x)); +} + +constexpr void +baz () +{ + constexpr int x = 0; + static_assert (!(&x < &x)); + static_assert (!foo (&x, &x)); +} + +int i, j; + +int +main () +{ + bar (); + baz (); + if (!(foo (&i, &j) ^ foo (&j, &i))) + abort (); +} --- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated4.C.jj 2018-12-12 11:58:45.673134560 +0100 +++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated4.C 2018-12-12 11:58:20.154551558 +0100 @@ -0,0 +1,19 @@ +// P0595R2 +// { dg-do compile { target c++14 } } + +namespace std { + constexpr inline bool + is_constant_evaluated () noexcept + { + return __builtin_is_constant_evaluated (); + } +} + +constexpr int +foo () noexcept +{ + return std::is_constant_evaluated () ? 5 : 12; +} + +static_assert (std::is_constant_evaluated (), ""); +static_assert (foo () == 5, ""); --- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated5.C.jj 2018-12-12 12:23:01.147359102 +0100 +++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated5.C 2018-12-12 12:23:21.444027606 +0100 @@ -0,0 +1,41 @@ +// PR c++/86524 +// { dg-do run { target c++14 } } +// { dg-options "-O2" } + +extern "C" void abort (); +typedef __UINTPTR_TYPE__ uintptr_t; + +constexpr bool +foo (const int *x, const int *y) +{ + if (__builtin_is_constant_evaluated ()) + return x < y; + return (uintptr_t) x < (uintptr_t) y; +} + +void +bar () +{ + constexpr int x = 0; + static_assert (!(&x < &x)); + static_assert (!foo (&x, &x)); +} + +constexpr void +baz () +{ + constexpr int x = 0; + static_assert (!(&x < &x)); + static_assert (!foo (&x, &x)); +} + +int i, j; + +int +main () +{ + bar (); + baz (); + if (!(foo (&i, &j) ^ foo (&j, &i))) + abort (); +} --- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated6.C.jj 2018-12-12 12:36:48.381866271 +0100 +++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated6.C 2018-12-12 12:35:04.140566444 +0100 @@ -0,0 +1,29 @@ +// P0595R2 +// { dg-do compile { target c++14 } } + +namespace std { + constexpr inline bool + is_constant_evaluated () noexcept + { + return __builtin_is_constant_evaluated (); + } +} + +int a; + +constexpr bool +foo (int x) +{ + return __builtin_constant_p (x); +} + +constexpr bool +bar (int x) +{ + return __builtin_constant_p (x + a); +} + +static_assert (__builtin_constant_p (0) + 2 * std::is_constant_evaluated () == 3, ""); +static_assert (__builtin_constant_p (a) + 2 * std::is_constant_evaluated () == 2, ""); +static_assert (foo (0) + 2 * std::is_constant_evaluated () == 3, ""); +static_assert (bar (0) + 2 * std::is_constant_evaluated () == 2, ""); Jakub