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;
+}

Reply via email to