PR c++/80265 - constexpr __builtin_mem*.
authorJason Merrill <jason@redhat.com>
Fri, 10 Jan 2020 20:38:34 +0000 (15:38 -0500)
committerJason Merrill <jason@redhat.com>
Mon, 13 Jan 2020 17:50:12 +0000 (12:50 -0500)
The library has already worked around this issue, but I was curious about
why it wasn't working.  The answer: because we were passing &var to fold,
which doesn't know about the constexpr values hash table.  Fixed by passing
&"str" instead.

* constexpr.c (cxx_eval_builtin_function_call): Expose STRING_CST
to str/mem builtins.

gcc/cp/constexpr.c
gcc/testsuite/g++.dg/ext/constexpr-builtin1.C [new file with mode: 0644]

index 3ca3b9ecd653d684e37219f5e1860923a6a7898c..3a9fb566a5282d8e275f669839c95a94364e7211 100644 (file)
@@ -1260,28 +1260,76 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
   if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND))
     return fold_builtin_source_location (EXPR_LOCATION (t));
 
+  int strops = 0;
+  int strret = 0;
+  if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
+    switch (DECL_FUNCTION_CODE (fun))
+      {
+      case BUILT_IN_STRLEN:
+      case BUILT_IN_STRNLEN:
+       strops = 1;
+       break;
+      case BUILT_IN_MEMCHR:
+      case BUILT_IN_STRCHR:
+      case BUILT_IN_STRRCHR:
+       strops = 1;
+       strret = 1;
+       break;
+      case BUILT_IN_MEMCMP:
+      case BUILT_IN_STRCMP:
+       strops = 2;
+       break;
+      case BUILT_IN_STRSTR:
+       strops = 2;
+       strret = 1;
+      default:
+       break;
+      }
+
   /* Be permissive for arguments to built-ins; __builtin_constant_p should
      return constant false for a non-constant argument.  */
   constexpr_ctx new_ctx = *ctx;
   new_ctx.quiet = true;
   for (i = 0; i < nargs; ++i)
     {
-      args[i] = CALL_EXPR_ARG (t, i);
+      tree arg = CALL_EXPR_ARG (t, i);
+
+      /* To handle string built-ins we need to pass ADDR_EXPR<STRING_CST> since
+        expand_builtin doesn't know how to look in the values table.  */
+      bool strop = i < strops;
+      if (strop)
+       {
+         STRIP_NOPS (arg);
+         if (TREE_CODE (arg) == ADDR_EXPR)
+           arg = TREE_OPERAND (arg, 0);
+         else
+           strop = false;
+       }
+
       /* If builtin_valid_in_constant_expr_p is true,
         potential_constant_expression_1 has not recursed into the arguments
         of the builtin, verify it here.  */
       if (!builtin_valid_in_constant_expr_p (fun)
-         || potential_constant_expression (args[i]))
+         || potential_constant_expression (arg))
        {
          bool dummy1 = false, dummy2 = false;
-         args[i] = cxx_eval_constant_expression (&new_ctx, args[i], false,
-                                                 &dummy1, &dummy2);
+         arg = cxx_eval_constant_expression (&new_ctx, arg, false,
+                                             &dummy1, &dummy2);
        }
 
       if (bi_const_p)
        /* For __builtin_constant_p, fold all expressions with constant values
           even if they aren't C++ constant-expressions.  */
-       args[i] = cp_fold_rvalue (args[i]);
+       arg = cp_fold_rvalue (arg);
+      else if (strop)
+       {
+         if (TREE_CODE (arg) == CONSTRUCTOR)
+           arg = braced_lists_to_strings (TREE_TYPE (arg), arg);
+         if (TREE_CODE (arg) == STRING_CST)
+           arg = build_address (arg);
+       }
+
+      args[i] = arg;
     }
 
   bool save_ffbcp = force_folding_builtin_constant_p;
@@ -1325,6 +1373,18 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
       return t;
     }
 
+  if (strret)
+    {
+      /* memchr returns a pointer into the first argument, but we replaced the
+        argument above with a STRING_CST; put it back it now.  */
+      tree op = CALL_EXPR_ARG (t, strret-1);
+      STRIP_NOPS (new_call);
+      if (TREE_CODE (new_call) == POINTER_PLUS_EXPR)
+       TREE_OPERAND (new_call, 0) = op;
+      else if (TREE_CODE (new_call) == ADDR_EXPR)
+       new_call = op;
+    }
+
   return cxx_eval_constant_expression (&new_ctx, new_call, lval,
                                       non_constant_p, overflow_p);
 }
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-builtin1.C b/gcc/testsuite/g++.dg/ext/constexpr-builtin1.C
new file mode 100644 (file)
index 0000000..2d0904b
--- /dev/null
@@ -0,0 +1,37 @@
+// PR c++/80265
+// { dg-do compile { target c++14 } }
+
+constexpr bool compare() {
+  char s1[] = "foo";
+  char s2[] = "fxo";
+  if (!__builtin_memcmp(s1, s2, 3))
+    return false;
+  s2[1] = 'o';
+  if (__builtin_memcmp(s1, s2, 3))
+    return false;
+  if (__builtin_strcmp(s1, s2))
+    return false;
+  return true;
+}
+
+constexpr bool length() {
+  char s[] = "foo";
+  if (__builtin_strlen(s) != 3)
+    return false;
+  return true;
+}
+
+constexpr bool find() {
+  char s[] = "foo";
+  if (__builtin_memchr(s, 'f', 3) != s)
+    return false;
+  if (__builtin_strchr(s, 'o') != s+1)
+    return false;
+  if (__builtin_strstr(s, "oo") != s+1)
+    return false;
+  return true;
+}
+
+static_assert( compare(), "" );
+static_assert( length(), "" );
+static_assert( find(), "" );