Hi! While working on the libstdc++ patch for P0595R2, I've noticed that while __builtin_is_constant_evaluated () directly works, when wrapped into an constexpr inline noexcept function, it in some cases doesn't. The problem is that the constexpr call cache didn't take ctx->pretend_const_required into account.
The following patch fixes it by just treating it like another magic parameter. Another option would be (but much more involved) to remember during the constexpr.c processing whether we've seen during the evaluation any calls to __builtin_is_constant_evaluated (that would require propagating in all the spots that use a new context back to the old context). Then we could in the constexpr hash remember either that during the evaluation of that constexpr call there was no __builtin_is_constant_evaluated seen, or, if it has been seen, whether it was in ctx->pretend_const_required mode or in !ctx->pretend_const_required mode. I've bootstrapped/regtested on x86_64-linux and i686-linux the following simpler version, ok for trunk? 2018-12-11 Jakub Jelinek <ja...@redhat.com> PR c++/88449 * constexpr.c (struct constexpr_call): Add pretend_const_required member. (constexpr_call_hasher::equal): Return false if pretend_const_required members differ. (cxx_eval_call_expression): Adjust new_call initialization. Hash in ctx->pretend_const_required. * g++.dg/cpp2a/is-constant-evaluated1.C: Change from dg-do compile to dg-do run. (e): Adjust comment with correct expected value. (main): Expect e == 1. * g++.dg/cpp2a/is-constant-evaluated2.C: New test. --- gcc/cp/constexpr.c.jj 2018-12-07 16:18:42.481847741 +0100 +++ gcc/cp/constexpr.c 2018-12-11 12:01:27.968941683 +0100 @@ -973,6 +973,8 @@ struct GTY((for_user)) constexpr_call { /* The hash of this call; we remember it here to avoid having to recalculate it when expanding the hash table. */ hashval_t hash; + /* Whether __builtin_is_constant_evaluated() should evaluate to true. */ + bool pretend_const_required; }; struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call> @@ -1052,6 +1054,8 @@ constexpr_call_hasher::equal (constexpr_ return true; if (lhs->hash != rhs->hash) return false; + if (lhs->pretend_const_required != rhs->pretend_const_required) + return false; if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef)) return false; lhs_bindings = lhs->bindings; @@ -1500,7 +1504,8 @@ cxx_eval_call_expression (const constexp { location_t loc = cp_expr_loc_or_loc (t, input_location); tree fun = get_function_named_in_call (t); - constexpr_call new_call = { NULL, NULL, NULL, 0 }; + constexpr_call new_call + = { NULL, NULL, NULL, 0, ctx->pretend_const_required }; bool depth_ok; if (fun == NULL_TREE) @@ -1642,8 +1647,11 @@ cxx_eval_call_expression (const constexp constexpr_call *entry = NULL; if (depth_ok && !non_constant_args && ctx->strict) { - new_call.hash = iterative_hash_template_arg - (new_call.bindings, constexpr_fundef_hasher::hash (new_call.fundef)); + new_call.hash = constexpr_fundef_hasher::hash (new_call.fundef); + new_call.hash + = iterative_hash_template_arg (new_call.bindings, new_call.hash); + new_call.hash + = iterative_hash_object (ctx->pretend_const_required, new_call.hash); /* If we have seen this call before, we are done. */ maybe_initialize_constexpr_call_table (); --- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C.jj 2018-08-26 22:41:13.778935483 +0200 +++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C 2018-12-11 11:57:55.027418581 +0100 @@ -1,5 +1,5 @@ // P0595R1 -// { dg-do compile { target c++14 } } +// { dg-do run { target c++14 } } template<int N> struct X { int v = N; }; X<__builtin_is_constant_evaluated ()> x; // type X<true> @@ -8,7 +8,7 @@ int a = __builtin_is_constant_evaluated int b = __builtin_is_constant_evaluated () ? 2 : y; // initializes b to 2 int c = y + (__builtin_is_constant_evaluated () ? 2 : y); // initializes c to 2*y int d = __builtin_is_constant_evaluated (); // initializes d to 1 -int e = d + __builtin_is_constant_evaluated (); // initializes e to 0 +int e = d + __builtin_is_constant_evaluated (); // initializes e to 1 + 0 struct false_type { static constexpr bool value = false; }; struct true_type { static constexpr bool value = true; }; @@ -50,7 +50,7 @@ static_assert (is_same<decltype (x), X<t int main () { - if (a != 1 || b != 2 || c != 8 || d != 1 || e != 0 || p != 26 || q != 56) + if (a != 1 || b != 2 || c != 8 || d != 1 || e != 1 || p != 26 || q != 56) __builtin_abort (); if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4) __builtin_abort (); --- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated2.C.jj 2018-12-11 11:37:03.415897434 +0100 +++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated2.C 2018-12-11 11:58:14.296103289 +0100 @@ -0,0 +1,72 @@ +// P0595R1 +// { dg-do run { target c++14 } } + +constexpr inline bool +is_constant_evaluated () noexcept +{ + return __builtin_is_constant_evaluated (); +} + +template<int N> struct X { int v = N; }; +X<is_constant_evaluated ()> x; // type X<true> +int y = 4; +int a = is_constant_evaluated () ? y : 1; // initializes a to 1 +int b = is_constant_evaluated () ? 2 : y; // initializes b to 2 +int c = y + (is_constant_evaluated () ? 2 : y); // initializes c to 2*y +int d = is_constant_evaluated (); // initializes d to 1 +int e = d + is_constant_evaluated (); // initializes e to 1 + 0 + +struct false_type { static constexpr bool value = false; }; +struct true_type { static constexpr bool value = true; }; +template<class T, class U> +struct is_same : false_type {}; +template<class T> +struct is_same<T, T> : true_type {}; + +constexpr int +foo (int x) +{ + const int n = is_constant_evaluated () ? 13 : 17; // n == 13 + int m = is_constant_evaluated () ? 13 : 17; // m might be 13 or 17 (see below) + char arr[n] = {}; // char[13] + return m + sizeof (arr) + x; +} + +constexpr int +bar () +{ + const int n = is_constant_evaluated() ? 13 : 17; + X<n> x1; + X<is_constant_evaluated() ? 13 : 17> x2; + static_assert (is_same<decltype (x1), decltype (x2)>::value, "x1/x2's type"); + return x1.v + x2.v; +} + +int p = foo (0); // m == 13; initialized to 26 +int q = p + foo (0); // m == 17 for this call; initialized to 56 +static_assert (bar () == 26, "bar"); + +struct S { int a, b; }; + +S s = { is_constant_evaluated () ? 2 : 3, y }; +S t = { is_constant_evaluated () ? 2 : 3, 4 }; + +static_assert (is_same<decltype (x), X<true> >::value, "x's type"); + +int +main () +{ + if (a != 1 || b != 2 || c != 8 || d != 1 || e != 1 || p != 26 || q != 56) + __builtin_abort (); + if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4) + __builtin_abort (); + if (foo (y) != 34) + __builtin_abort (); +#if __cplusplus >= 201703L + if constexpr (foo (0) != 26) + __builtin_abort (); +#endif + constexpr int w = foo (0); + if (w != 26) + __builtin_abort (); +} Jakub