On Mon, Jul 18, 2016 at 01:16:26PM -0400, Jason Merrill wrote:
> On Fri, Jul 15, 2016 at 2:42 PM, Jakub Jelinek <ja...@redhat.com> wrote:
> > While in C++11, builtins returning two results, one of them by dereferencing
> > a pointer argument can't be constexpr, in my understanding in C++14
> > generalized constexprs they can.
> 
> Yes.
> 
> > So, this patch tweaks cxx_eval_builtin_function_call so that it handles how
> > builtins.c folds these builtins (i.e. COMPOUND_EXPR with first operand
> > being *arg = const1 and second operand const2, optionally all wrapped into a
> > NON_LVALUE_EXPR.
> 
> Why so specific?  Can't we just pass the return value from fold into
> cxx_eval_constant_expression, if it isn't still a CALL_EXPR?

I'll test it.

> Incidentally, I think we should be using fold_builtin_call_array
> rather than fold_build_call_array_loc.

That is reasonable, but not 100% sure what to do if it returns NULL
- it should return t, but if I do VERIFY_CONSTANT (t); there or
manually
    if (!*non_constant_p)
      {
        if (!allow_non_constant)
          error ("%q+E is not a constant expression", t);
        *non_constant_p = true;
      }
    return t;
it will print the original expression (without folded arguments).
Another option would be to build_call_array_loc if we want to emit
the error.  Preferences?

> > In addition, I've noticed that the lval argument is passed down to
> > evaluation of arguments, that doesn't make sense to me, IMHO arguments
> > should be always evaluated as rvalues (and for non-builtins they are).
> 
> I'm a bit nervous about this, since some builtins take arguments by
> magic rather than by value, but I'm willing to accept this and see
> what breaks.

If some builtin is special, wouldn't it be special regardless of whether
we want to take address of the builtin's return value or not?
I'm really not aware of any though, after all, gimplification will turn
all their arguments into rvalues anyway.

The following patch uses VERIFY_CONSTANT (t) for the above mentioned issue,
and passes make check-g++ RUNTESTFLAGS=dg.exp=*constexpr*, is that what you
want or something different?

2016-07-18  Jakub Jelinek  <ja...@redhat.com>

        PR c++/50060
        * constexpr.c (cxx_eval_builtin_function_call): Pass false as lval
        when evaluating call arguments.  Use fold_builtin_call_array instead
        of fold_build_call_array_loc, return t if it returns NULL.
        For C++14 and later, pass new_call to if new_call
        cxx_eval_constant_expression.

        * g++.dg/cpp1y/constexpr-50060.C: New test.

--- gcc/cp/constexpr.c.jj       2016-07-16 10:41:04.525652516 +0200
+++ gcc/cp/constexpr.c  2016-07-18 19:53:45.059291912 +0200
@@ -1105,7 +1105,7 @@ cxx_eval_builtin_function_call (const co
   for (i = 0; i < nargs; ++i)
     {
       args[i] = cxx_eval_constant_expression (&new_ctx, CALL_EXPR_ARG (t, i),
-                                             lval, &dummy1, &dummy2);
+                                             false, &dummy1, &dummy2);
       if (bi_const_p)
        /* For __built_in_constant_p, fold all expressions with constant values
           even if they aren't C++ constant-expressions.  */
@@ -1114,11 +1114,23 @@ cxx_eval_builtin_function_call (const co
 
   bool save_ffbcp = force_folding_builtin_constant_p;
   force_folding_builtin_constant_p = true;
-  new_call = fold_build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t),
-                                       CALL_EXPR_FN (t), nargs, args);
+  new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
+                                     CALL_EXPR_FN (t), nargs, args);
+  if (new_call == NULL)
+    {
+      VERIFY_CONSTANT (t);
+      return t;
+    }
+
   /* Fold away the NOP_EXPR from fold_builtin_n.  */
   new_call = fold (new_call);
   force_folding_builtin_constant_p = save_ffbcp;
+
+  /* Folding some math builtins produces e.g. COMPOUND_EXPR etc.  */
+  if (cxx_dialect >= cxx14)
+    return cxx_eval_constant_expression (&new_ctx, new_call, lval,
+                                        non_constant_p, overflow_p);
+
   VERIFY_CONSTANT (new_call);
   return new_call;
 }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-50060.C.jj     2016-07-18 
19:45:30.528496123 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-50060.C        2016-07-18 
19:45:30.528496123 +0200
@@ -0,0 +1,100 @@
+// PR c++/50060
+// { dg-do compile { target c++14 } }
+
+// sincos and lgamma_r aren't available in -std=c++14,
+// only in -std=gnu++14.  Use __builtin_* in that case.
+extern "C" void sincos (double, double *, double *);
+extern "C" double frexp (double, int *);
+extern "C" double modf (double, double *);
+extern "C" double remquo (double, double, int *);
+extern "C" double lgamma_r (double, int *);
+
+constexpr double
+f0 (double x)
+{
+  double y {};
+  double z {};
+  __builtin_sincos (x, &y, &z);
+  return y;
+}
+
+constexpr double
+f1 (double x)
+{
+  double y {};
+  double z {};
+  __builtin_sincos (x, &y, &z);
+  return z;
+}
+
+constexpr double
+f2 (double x)
+{
+  int y {};
+  return frexp (x, &y);
+}
+
+constexpr int
+f3 (double x)
+{
+  int y {};
+  frexp (x, &y);
+  return y;
+}
+
+constexpr double
+f4 (double x)
+{
+  double y {};
+  return modf (x, &y);
+}
+
+constexpr double
+f5 (double x)
+{
+  double y {};
+  modf (x, &y);
+  return y;
+}
+
+constexpr double
+f6 (double x, double y)
+{
+  int z {};
+  return remquo (x, y, &z);
+}
+
+constexpr int
+f7 (double x, double y)
+{
+  int z {};
+  remquo (x, y, &z);
+  return z;
+}
+
+constexpr double
+f8 (double x)
+{
+  int y {};
+  return __builtin_lgamma_r (x, &y);
+}
+
+constexpr int
+f9 (double x)
+{
+  int y {};
+  __builtin_lgamma_r (x, &y);
+  return y;
+}
+
+static_assert (f0 (0.0) == 0.0, "");
+static_assert (f1 (0.0) == 1.0, "");
+static_assert (f2 (6.5) == 0.8125, "");
+static_assert (f3 (6.5) == 3, "");
+static_assert (f4 (-7.25) == -0.25, "");
+static_assert (f5 (-7.25) == -7.0, "");
+static_assert (f6 (3.0, 2.0) == -1.0, "");
+static_assert (f7 (3.0, 2.0) == 2, "");
+static_assert (f8 (0.75) >= 0.20 && f8 (0.75) <= 0.21, "");
+static_assert (f8 (0.75) >= 0.20 && f8 (0.75) <= 0.21, "");
+static_assert (f9 (0.75) == 1, "");


        Jakub

Reply via email to