diff mbox series

[committed] c: Update checks on constexpr floating-point initializers

Message ID 5d3cd8d-8b2b-b01a-c7ce-8da7e1dd5938@codesourcery.com
State New
Headers show
Series [committed] c: Update checks on constexpr floating-point initializers | expand

Commit Message

Joseph Myers Feb. 2, 2023, 8:08 p.m. UTC
WG14 has agreed some changes (detailed at the end of N3082) to the
rules on constexpr initializers for floating types.  Update GCC's
implementation to match: binary initializers are now allowed for
decimal types, and real initializers for complex types, but signaling
NaN initializers can't be used for a different type with the same
mode.

There are also changes to the constexpr rules for pointer types
(allowing null pointer address constants that aren't null pointer
constants), which I'll deal with separately.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.

gcc/c/
	* c-typeck.cc: Include "realmpfr.h".
	(constexpr_init_fits_real_type): Do not allow signaling NaN
	conversions to different types with the same mode.  Handle
	conversions from binary to decimal types.
	(check_constexpr_init): Do not disallow real initializers for
	complex types.  Do not disallow binary initializers for decimal
	floating types.

gcc/testsuite/
	* gcc.dg/c2x-constexpr-1.c: Test constexpr initializers of complex
	types with real initializers are allowed.
	* gcc.dg/c2x-constexpr-3.c: Do not test for constexpr initializers
	of complex types with real initializers being disallowed.
	* gcc.dg/c2x-constexpr-8.c: Add tests of signaling NaN complex
	initializers.
	* gcc.dg/c2x-constexpr-9.c: Add more tests.
	* gcc.dg/dfp/c2x-constexpr-dfp-1.c: Add tests of binary floating
	initializers for decimal types.
	* gcc.dg/dfp/c2x-constexpr-dfp-2.c: Change tests of binary
	initializers for decimal types.  Add more tests of decimal
	initializers for binary types.
diff mbox series

Patch

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2737b14ea18..9d65130154d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -52,6 +52,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "realmpfr.h"
 
 /* Possible cases of implicit conversions.  Used to select diagnostic messages
    and control folding initializers in convert_for_assignment.  */
@@ -8121,8 +8122,9 @@  print_spelling (char *buffer)
 }
 
 /* Check whether INIT, a floating or integer constant, is
-   representable in TYPE, a real floating type with the same radix.
-   Return true if OK, false if not.  */
+   representable in TYPE, a real floating type with the same radix or
+   a decimal floating type initialized with a binary floating
+   constant.  Return true if OK, false if not.  */
 static bool
 constexpr_init_fits_real_type (tree type, tree init)
 {
@@ -8130,8 +8132,16 @@  constexpr_init_fits_real_type (tree type, tree init)
   gcc_assert (TREE_CODE (init) == INTEGER_CST || TREE_CODE (init) == REAL_CST);
   if (TREE_CODE (init) == REAL_CST
       && TYPE_MODE (TREE_TYPE (init)) == TYPE_MODE (type))
-    /* Same mode, no conversion required.  */
-    return true;
+    {
+      /* Same mode, no conversion required except for the case of
+	 signaling NaNs if the types are incompatible (e.g. double and
+	 long double with the same mode).  */
+      if (REAL_VALUE_ISSIGNALING_NAN (TREE_REAL_CST (init))
+	  && !comptypes (TYPE_MAIN_VARIANT (type),
+			 TYPE_MAIN_VARIANT (TREE_TYPE (init))))
+	return false;
+      return true;
+    }
   if (TREE_CODE (init) == INTEGER_CST)
     {
       tree converted = build_real_from_int_cst (type, init);
@@ -8140,6 +8150,33 @@  constexpr_init_fits_real_type (tree type, tree init)
 				    TYPE_PRECISION (TREE_TYPE (init)));
       return !fail && wi::eq_p (w, wi::to_wide (init));
     }
+  if (REAL_VALUE_ISSIGNALING_NAN (TREE_REAL_CST (init)))
+    return false;
+  if ((REAL_VALUE_ISINF (TREE_REAL_CST (init))
+       && MODE_HAS_INFINITIES (TYPE_MODE (type)))
+      || (REAL_VALUE_ISNAN (TREE_REAL_CST (init))
+	  && MODE_HAS_NANS (TYPE_MODE (type))))
+    return true;
+  if (DECIMAL_FLOAT_TYPE_P (type)
+      && !DECIMAL_FLOAT_TYPE_P (TREE_TYPE (init)))
+    {
+      /* This is valid if the real number represented by the
+	 initializer can be exactly represented in the decimal
+	 type.  Compare the values using MPFR.  */
+      REAL_VALUE_TYPE t;
+      real_convert (&t, TYPE_MODE (type), &TREE_REAL_CST (init));
+      mpfr_t bin_val, dec_val;
+      mpfr_init2 (bin_val, REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (init)))->p);
+      mpfr_init2 (dec_val, REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (init)))->p);
+      mpfr_from_real (bin_val, &TREE_REAL_CST (init), MPFR_RNDN);
+      char string[256];
+      real_to_decimal (string, &t, sizeof string, 0, 1);
+      bool res = (mpfr_strtofr (dec_val, string, NULL, 10, MPFR_RNDN) == 0
+		  && mpfr_equal_p (bin_val, dec_val));
+      mpfr_clear (bin_val);
+      mpfr_clear (dec_val);
+      return res;
+    }
   /* exact_real_truncate is not quite right here, since it doesn't
      allow even an exact conversion to subnormal values.  */
   REAL_VALUE_TYPE t;
@@ -8194,18 +8231,12 @@  check_constexpr_init (location_t loc, tree type, tree init,
   if (TREE_CODE (type) == COMPLEX_TYPE
       && TREE_CODE (TREE_TYPE (type)) != REAL_TYPE)
     return;
-  /* Both the normative text and the relevant footnote are unclear, as
-     of the C2x CD, about what exactly counts as a change of value in
-     floating-point cases.  Here, we consider all conversions between
-     binary and decimal types (even of infinities and NaNs, where
-     quantum exponents are not involved) as involving a change of
-     value, and likewise for conversions between real and complex
-     types (even when the complex constant has imaginary part positive
-     zero), and conversions of signaling NaN to a different machine
-     mode.  But we allow exact conversions of integers to binary or
-     decimal floating types, and exact conversions between different
-     binary types or different decimal types, where "exact" in the
-     decimal case requires the quantum exponent to be preserved.  */
+  /* Following N3082, a real type cannot be initialized from a complex
+     type and a binary type cannot be initialized from a decimal type
+     (but initializing a decimal type from a binary type is OK).
+     Signaling NaN initializers are OK only if the types are
+     compatible (not just the same mode); all quiet NaN and infinity
+     initializations are considered to preserve the value.  */
   if (TREE_CODE (TREE_TYPE (init)) == COMPLEX_TYPE
       && TREE_CODE (type) == REAL_TYPE)
     {
@@ -8213,39 +8244,33 @@  check_constexpr_init (location_t loc, tree type, tree init,
 		"complex type");
       return;
     }
-  if (TREE_CODE (type) == COMPLEX_TYPE
-      && TREE_CODE (TREE_TYPE (init)) != COMPLEX_TYPE)
-    {
-      error_at (loc, "%<constexpr%> initializer for a complex type is of "
-		"real type");
-      return;
-    }
   if (TREE_CODE (type) == REAL_TYPE
-      && TREE_CODE (TREE_TYPE (init)) == REAL_TYPE)
+      && TREE_CODE (TREE_TYPE (init)) == REAL_TYPE
+      && DECIMAL_FLOAT_TYPE_P (TREE_TYPE (init))
+      && !DECIMAL_FLOAT_TYPE_P (type))
     {
-      if (DECIMAL_FLOAT_TYPE_P (type)
-	  && !DECIMAL_FLOAT_TYPE_P (TREE_TYPE (init)))
-	{
-	  error_at (loc, "%<constexpr%> initializer for a decimal "
-		    "floating-point type is of binary type");
-	  return;
-	}
-      else if (DECIMAL_FLOAT_TYPE_P (TREE_TYPE (init))
-	       && !DECIMAL_FLOAT_TYPE_P (type))
-	{
-	  error_at (loc, "%<constexpr%> initializer for a binary "
-		    "floating-point type is of decimal type");
-	  return;
-	}
+      error_at (loc, "%<constexpr%> initializer for a binary "
+		"floating-point type is of decimal type");
+      return;
     }
   bool fits;
   if (TREE_CODE (type) == COMPLEX_TYPE)
     {
-      gcc_assert (TREE_CODE (init) == COMPLEX_CST);
-      fits = (constexpr_init_fits_real_type (TREE_TYPE (type),
-					     TREE_REALPART (init))
-	      && constexpr_init_fits_real_type (TREE_TYPE (type),
-						TREE_IMAGPART (init)));
+      switch (TREE_CODE (init))
+	{
+	case INTEGER_CST:
+	case REAL_CST:
+	  fits = constexpr_init_fits_real_type (TREE_TYPE (type), init);
+	  break;
+	case COMPLEX_CST:
+	  fits = (constexpr_init_fits_real_type (TREE_TYPE (type),
+						 TREE_REALPART (init))
+		  && constexpr_init_fits_real_type (TREE_TYPE (type),
+						    TREE_IMAGPART (init)));
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
     }
   else
     fits = constexpr_init_fits_real_type (type, init);
diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-1.c b/gcc/testsuite/gcc.dg/c2x-constexpr-1.c
index d43d95ddd7c..97b54f17428 100644
--- a/gcc/testsuite/gcc.dg/c2x-constexpr-1.c
+++ b/gcc/testsuite/gcc.dg/c2x-constexpr-1.c
@@ -174,6 +174,8 @@  constexpr int v94 = alignof (int);
 alignas (v94) int v95;
 constexpr int v97[100] = { [v82.x.f] = 7 };
 static int v98[v94];
+constexpr _Complex double v99 = 1.0;
+constexpr _Complex float v100 = 12345;
 
 void
 f0 ()
@@ -247,6 +249,8 @@  f0 ()
   (constexpr union u58) { 0 };
   (constexpr union u58) { { } }; /* { dg-warning "braces around scalar initializer" } */
   (constexpr union u58) { { 0 } }; /* { dg-warning "braces around scalar initializer" } */
+  (constexpr _Complex double) { 1.0 };
+  (constexpr _Complex float) { 12345 };
   /* It's not entirely clear if constexpr declarations are allowed in this
      position in a for loop; presume they are, as implicitly auto just as if no
      storage class specifiers were used.  */
diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-3.c b/gcc/testsuite/gcc.dg/c2x-constexpr-3.c
index 29fedc03afd..4f6b8ed6779 100644
--- a/gcc/testsuite/gcc.dg/c2x-constexpr-3.c
+++ b/gcc/testsuite/gcc.dg/c2x-constexpr-3.c
@@ -112,13 +112,9 @@  constexpr int v95 = (unsigned int) -1; /* { dg-error "'constexpr' initializer no
 constexpr unsigned char v96 = -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
 constexpr signed char v97 = 1234567LL; /* { dg-error "'constexpr' initializer not representable in type of object" } */
 /* { dg-warning "overflow in conversion" "overflow warning" { target *-*-* } .-1 } */
-/* Disallow all real/complex conversions (the C2x CD is unclear about
-   real-to-complex and about complex-to-real with imaginary part positive 0, if
-   the real parts can be exactly represented in the relevant types).  */
 constexpr double v98 = __builtin_complex (1.0, 0.0); /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
 constexpr double v99 = __builtin_complex (1.0, 1.0); /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
 constexpr double v100 = __builtin_complex (1.0, -0.0); /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
-constexpr _Complex double v101 = 1.0; /* { dg-error "'constexpr' initializer for a complex type is of real type" } */
 constexpr float v102 = (unsigned long long) -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
 constexpr double v103 = (unsigned long long) -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
 constexpr float v104 = __LONG_LONG_MAX__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
@@ -216,7 +212,6 @@  f0 ()
   (constexpr double) { __builtin_complex (1.0, 0.0) }; /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
   (constexpr double) { __builtin_complex (1.0, 1.0) }; /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
   (constexpr double) { __builtin_complex (1.0, -0.0) }; /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
-  (constexpr _Complex double) { 1.0 }; /* { dg-error "'constexpr' initializer for a complex type is of real type" } */
   (constexpr float) { (unsigned long long) -1 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
   (constexpr double) { (unsigned long long) -1 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
   (constexpr float) { __LONG_LONG_MAX__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-8.c b/gcc/testsuite/gcc.dg/c2x-constexpr-8.c
index c7119c97a69..11372cf1d5a 100644
--- a/gcc/testsuite/gcc.dg/c2x-constexpr-8.c
+++ b/gcc/testsuite/gcc.dg/c2x-constexpr-8.c
@@ -10,6 +10,7 @@  constexpr float fn = __builtin_nan ("");
 constexpr double dn = __builtin_nanf ("");
 constexpr float fns = __builtin_nansf ("");
 constexpr double dns = __builtin_nans ("");
+constexpr _Complex double cdns = __builtin_nans ("");
 
 void
 f0 (void)
@@ -20,4 +21,5 @@  f0 (void)
   (constexpr double) { __builtin_nanf ("") };
   (constexpr float) { __builtin_nansf ("") };
   (constexpr double) { __builtin_nans ("") };
+  (constexpr _Complex double) { __builtin_nans ("") };
 }
diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-9.c b/gcc/testsuite/gcc.dg/c2x-constexpr-9.c
index c62fc738fa0..8a07ed5b383 100644
--- a/gcc/testsuite/gcc.dg/c2x-constexpr-9.c
+++ b/gcc/testsuite/gcc.dg/c2x-constexpr-9.c
@@ -4,10 +4,11 @@ 
 /* { dg-add-options ieee } */
 /* { dg-require-effective-target inff } */
 
-/* A conversion from signaling NaN to quiet NaN in a different format is not
-   valid for constexpr.  */
+/* A conversion from signaling NaN to quiet NaN in a different format or type
+   is not valid for constexpr.  */
 constexpr float fns = __builtin_nans (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
 constexpr double dns = __builtin_nansf (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
+constexpr long double ldns = __builtin_nans (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
 
 /* Test out-of-range values.  */
 constexpr float fu = __DBL_MIN__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
@@ -22,6 +23,9 @@  constexpr _Complex float cfui = __builtin_complex (0.0, __DBL_MIN__); /* { dg-er
 constexpr _Complex float cfoi = __builtin_complex (0.0, __DBL_MAX__); /* { dg-error "'constexpr' initializer not representable in type of object" } */
 constexpr _Complex float cfpi = __builtin_complex (0.0, 0x1.ffffffp0); /* { dg-error "'constexpr' initializer not representable in type of object" } */
 
+constexpr _Complex float cfd = __DBL_MAX__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
+constexpr _Complex float cfi = __LONG_LONG_MAX__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
+
 void
 f0 ()
 {
@@ -36,4 +40,6 @@  f0 ()
   (constexpr _Complex float) { __builtin_complex (0.0, __DBL_MIN__) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
   (constexpr _Complex float) { __builtin_complex (0.0, __DBL_MAX__) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
   (constexpr _Complex float) { __builtin_complex (0.0, 0x1.ffffffp0) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
+  (constexpr _Complex float) { __DBL_MAX__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
+  (constexpr _Complex float) { __LONG_LONG_MAX__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
 }
diff --git a/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c
index 568f1428b40..4ac6629ff5a 100644
--- a/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c
+++ b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c
@@ -37,6 +37,11 @@  constexpr _Decimal128 v32 = __builtin_nansd128 ("");
 constexpr _Decimal32 v33 = {};
 constexpr _Decimal64 v34 = {};
 constexpr _Decimal128 v35 = {};
+constexpr _Decimal32 v36 = 0.0;
+constexpr _Decimal32 v37 = 0.0009765625;
+constexpr _Decimal64 v38 = 6.103515625e-05;
+constexpr _Decimal32 v39 = __builtin_inf ();
+constexpr _Decimal32 v40 = __builtin_nan ("");
 
 void
 f0 ()
@@ -76,4 +81,9 @@  f0 ()
   (constexpr _Decimal32) {};
   (constexpr _Decimal64) {};
   (constexpr _Decimal128) {};
+  (constexpr _Decimal32) { 0.0 };
+  (constexpr _Decimal32) { 0.0009765625 };
+  (constexpr _Decimal64) { 6.103515625e-05 };
+  (constexpr _Decimal32) { __builtin_inf () };
+  (constexpr _Decimal32) { __builtin_nan ("") };
 }
diff --git a/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c
index 8b1ecf23908..8ce0d088ac3 100644
--- a/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c
+++ b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c
@@ -3,8 +3,10 @@ 
 /* { dg-options "-std=c2x -pedantic-errors" } */
 
 /* Test conversions between binary and decimal.  */
-constexpr _Decimal32 v1 = 0.0; /* { dg-error "'constexpr' initializer for a decimal floating-point type is of binary type" } */
 constexpr double v2 = 0.0DF; /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
+constexpr double v2i = __builtin_infd32 (); /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
+constexpr double v2n = __builtin_nand32 (""); /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
+constexpr _Decimal128 v2d = 0x1p-100f; /* { dg-error "'constexpr' initializer not representable in type of object" } */
 
 /* A conversion from signaling NaN to quiet NaN in a different format is not
    valid for constexpr.  */
@@ -30,8 +32,10 @@  constexpr _Decimal32 v15 = 0e200DL; /* { dg-error "'constexpr' initializer not r
 void
 f0 ()
 {
-  (constexpr _Decimal32) { 0.0 }; /* { dg-error "'constexpr' initializer for a decimal floating-point type is of binary type" } */
   (constexpr double) { 0.0DF }; /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
+  (constexpr double) { __builtin_infd32 () }; /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
+  (constexpr double) { __builtin_nand32 ("") }; /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
+  (constexpr _Decimal128) { 0x1p-100f }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
   (constexpr _Decimal32) { __builtin_nansd64 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
   (constexpr _Decimal32) { __builtin_nansd128 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
   (constexpr _Decimal64) { __builtin_nansd32 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */