C++ PATCH for c++/91428 - warn about std::is_constant_evaluated in if constexpr

Message ID 20190827215000.GF14737@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/91428 - warn about std::is_constant_evaluated in if constexpr
Related show

Commit Message

Marek Polacek Aug. 27, 2019, 9:50 p.m.
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  <polacek@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.

Comments

Jason Merrill Aug. 28, 2019, 1:53 a.m. | #1
On Tue, Aug 27, 2019 at 5:50 PM Marek Polacek <polacek@redhat.com> wrote:
>

> 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?


OK.

Jason

Patch

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