As discussed in 91428 and in <https://stackoverflow.com/questions/54251530/stdis-constant-evaluated-behavior>,
if constexpr (std::is_constant_evaluated ()) // ... else // ... always evaluates the true branch. Someone in the SO post said "But hopefully compilers will just diagnose that case" so I'm adding a warning. I didn't want to invent a completely new warning so I'm tagging along -Wtautological-compare. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-08-27 Marek Polacek <pola...@redhat.com> PR c++/91428 - warn about std::is_constant_evaluated in if constexpr. * cp-tree.h (decl_in_std_namespace_p): Declare. * semantics.c (is_std_constant_evaluated_p): New. (finish_if_stmt_cond): Warn about "std::is_constant_evaluated ()" in an if-constexpr. * typeck.c (decl_in_std_namespace_p): No longer static. * g++.dg/cpp2a/is-constant-evaluated9.C: New test. diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h index 42f180d1dd3..225dbb67c63 100644 --- gcc/cp/cp-tree.h +++ gcc/cp/cp-tree.h @@ -7496,6 +7496,7 @@ extern tree finish_left_unary_fold_expr (tree, int); extern tree finish_right_unary_fold_expr (tree, int); extern tree finish_binary_fold_expr (tree, tree, int); extern bool treat_lvalue_as_rvalue_p (tree, bool); +extern bool decl_in_std_namespace_p (tree); /* in typeck2.c */ extern void require_complete_eh_spec_types (tree, tree); diff --git gcc/cp/semantics.c gcc/cp/semantics.c index 1f7745933f9..8603e57e7f7 100644 --- gcc/cp/semantics.c +++ gcc/cp/semantics.c @@ -723,6 +723,28 @@ begin_if_stmt (void) return r; } +/* Returns true if FN, a CALL_EXPR, is a call to + std::is_constant_evaluated or __builtin_is_constant_evaluated. */ + +static bool +is_std_constant_evaluated_p (tree fn) +{ + /* std::is_constant_evaluated takes no arguments. */ + if (call_expr_nargs (fn) != 0) + return false; + + tree fndecl = cp_get_callee_fndecl_nofold (fn); + if (fndecl_built_in_p (fndecl, CP_BUILT_IN_IS_CONSTANT_EVALUATED, + BUILT_IN_FRONTEND)) + return true; + + if (!decl_in_std_namespace_p (fndecl)) + return false; + + tree name = DECL_NAME (fndecl); + return name && id_equal (name, "is_constant_evaluated"); +} + /* Process the COND of an if-statement, which may be given by IF_STMT. */ @@ -738,6 +760,20 @@ finish_if_stmt_cond (tree cond, tree if_stmt) converted to bool. */ && TYPE_MAIN_VARIANT (TREE_TYPE (cond)) == boolean_type_node) { + /* if constexpr (std::is_constant_evaluated()) is always true, + so give the user a clue. */ + if (warn_tautological_compare) + { + tree t = cond; + if (TREE_CODE (t) == CLEANUP_POINT_EXPR) + t = TREE_OPERAND (t, 0); + if (TREE_CODE (t) == CALL_EXPR + && is_std_constant_evaluated_p (t)) + warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare, + "%qs always evaluates to true in %<if constexpr%>", + "std::is_constant_evaluated"); + } + cond = instantiate_non_dependent_expr (cond); cond = cxx_constant_value (cond, NULL_TREE); } diff --git gcc/cp/typeck.c gcc/cp/typeck.c index e2a4f285a72..c09bb309142 100644 --- gcc/cp/typeck.c +++ gcc/cp/typeck.c @@ -9328,7 +9328,7 @@ maybe_warn_about_returning_address_of_local (tree retval) /* Returns true if DECL is in the std namespace. */ -static bool +bool decl_in_std_namespace_p (tree decl) { return (decl != NULL_TREE diff --git gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated9.C gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated9.C new file mode 100644 index 00000000000..37833698992 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated9.C @@ -0,0 +1,49 @@ +// PR c++/91428 - warn about std::is_constant_evaluated in if constexpr. +// { dg-do compile { target c++2a } } +// { dg-options "-Wtautological-compare" } + +namespace std { + constexpr inline bool + is_constant_evaluated () noexcept + { + return __builtin_is_constant_evaluated (); + } +} + +constexpr int +foo(int i) +{ + if constexpr (std::is_constant_evaluated ()) // { dg-warning ".std::is_constant_evaluated. always evaluates to true in .if constexpr." } + return 42; + else + return i; +} + +constexpr int +foo2(int i) +{ + if constexpr (__builtin_is_constant_evaluated ()) // { dg-warning ".std::is_constant_evaluated. always evaluates to true in .if constexpr." } + return 42; + else + return i; +} + +constexpr int +foo3(int i) +{ + // I is not a constant expression but we short-circuit it. + if constexpr (__builtin_is_constant_evaluated () || i) + return 42; + else + return i; +} + +constexpr int +foo4(int i) +{ + const int j = 0; + if constexpr (j && __builtin_is_constant_evaluated ()) + return 42; + else + return i; +}