c: Fold implicit integer-to-floating conversions in static initializers with -frounding-math [PR103031]

Message ID alpine.DEB.2.22.394.2111030020420.2032394@digraph.polyomino.org.uk
State Committed
Commit 600dcd74b8e614c996b492d97878660faf484094
Headers
Series c: Fold implicit integer-to-floating conversions in static initializers with -frounding-math [PR103031] |

Commit Message

Joseph Myers Nov. 3, 2021, 12:21 a.m. UTC
  Recent fixes to avoid inappropriate folding of some conversions to
floating-point types with -frounding-math also prevented such folding
in C static initializers, when folding (in the default rounding mode,
exceptions discarded) is required for correctness.

Folding for static initializers is handled via functions in
fold-const.c calling START_FOLD_INIT and END_FOLD_INIT to adjust flags
such as flag_rounding_math that should not apply in static initializer
context, but no such function was being called for the folding of
these implicit conversions to the type of the object being
initialized, only for explicit conversions as part of the initializer.

Arrange for relevant folding (a fold call in convert, in particular)
to use this special initializer handling (via a new fold_init
function, in particular).

Because convert is used by language-independent code but defined in
each front end, this isn't as simple as just adding a new default
argument to it.  Instead, I added a new convert_init function; that
then gets called by c-family code, and C and C++ need convert_init
implementations (the C++ one does nothing different from convert and
will never actually get called because the new convert_and_check
argument will never be true from C++), but other languages don't.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.  OK to commit 
(the changes outside the C front end)?

gcc/
	PR c/103031
	* fold-const.c (fold_init): New function.
	* fold-const.h (fold_init): New prototype.

gcc/c-family/
	PR c/103031
	* c-common.c (convert_and_check): Add argument init_const.  Call
	convert_init if init_const.
	* c-common.h (convert_and_check): Update prototype.
	(convert_init): New prototype.

gcc/c/
	PR c/103031
	* c-convert.c (c_convert): New function, based on convert.
	(convert): Make into wrapper of c_convert.
	(convert_init): New function.
	* c-typeck.h (enum impl_conv): Add ic_init_const.
	(convert_for_assignment): Handle ic_init_const like ic_init.  Add
	new argument to convert_and_check call.
	(digest_init): Pass ic_init_const to convert_for_assignment for
	initializers required to be constant.

gcc/cp/
	PR c/103031
	* cvt.c (convert_init): New function.

gcc/testsuite/
	PR c/103031
	* gcc.dg/init-rounding-math-1.c: New test.
  

Comments

Richard Biener Nov. 3, 2021, 12:38 p.m. UTC | #1
On Wed, Nov 3, 2021 at 1:22 AM Joseph Myers <joseph@codesourcery.com> wrote:
>
> Recent fixes to avoid inappropriate folding of some conversions to
> floating-point types with -frounding-math also prevented such folding
> in C static initializers, when folding (in the default rounding mode,
> exceptions discarded) is required for correctness.
>
> Folding for static initializers is handled via functions in
> fold-const.c calling START_FOLD_INIT and END_FOLD_INIT to adjust flags
> such as flag_rounding_math that should not apply in static initializer
> context, but no such function was being called for the folding of
> these implicit conversions to the type of the object being
> initialized, only for explicit conversions as part of the initializer.
>
> Arrange for relevant folding (a fold call in convert, in particular)
> to use this special initializer handling (via a new fold_init
> function, in particular).
>
> Because convert is used by language-independent code but defined in
> each front end, this isn't as simple as just adding a new default
> argument to it.  Instead, I added a new convert_init function; that
> then gets called by c-family code, and C and C++ need convert_init
> implementations (the C++ one does nothing different from convert and
> will never actually get called because the new convert_and_check
> argument will never be true from C++), but other languages don't.
>
> Bootstrapped with no regressions for x86_64-pc-linux-gnu.  OK to commit
> (the changes outside the C front end)?

OK.

Thanks,
Richard.

> gcc/
>         PR c/103031
>         * fold-const.c (fold_init): New function.
>         * fold-const.h (fold_init): New prototype.
>
> gcc/c-family/
>         PR c/103031
>         * c-common.c (convert_and_check): Add argument init_const.  Call
>         convert_init if init_const.
>         * c-common.h (convert_and_check): Update prototype.
>         (convert_init): New prototype.
>
> gcc/c/
>         PR c/103031
>         * c-convert.c (c_convert): New function, based on convert.
>         (convert): Make into wrapper of c_convert.
>         (convert_init): New function.
>         * c-typeck.h (enum impl_conv): Add ic_init_const.
>         (convert_for_assignment): Handle ic_init_const like ic_init.  Add
>         new argument to convert_and_check call.
>         (digest_init): Pass ic_init_const to convert_for_assignment for
>         initializers required to be constant.
>
> gcc/cp/
>         PR c/103031
>         * cvt.c (convert_init): New function.
>
> gcc/testsuite/
>         PR c/103031
>         * gcc.dg/init-rounding-math-1.c: New test.
>
> diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
> index 32c7e3e8972..436df45df68 100644
> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c
> @@ -1739,10 +1739,13 @@ unsafe_conversion_p (tree type, tree expr, tree result, bool check_sign)
>
>  /* Convert EXPR to TYPE, warning about conversion problems with constants.
>     Invoke this function on every expression that is converted implicitly,
> -   i.e. because of language rules and not because of an explicit cast.  */
> +   i.e. because of language rules and not because of an explicit cast.
> +   INIT_CONST is true if the conversion is for arithmetic types for a static
> +   initializer and folding must apply accordingly (discarding floating-point
> +   exceptions and assuming the default rounding mode is in effect).  */
>
>  tree
> -convert_and_check (location_t loc, tree type, tree expr)
> +convert_and_check (location_t loc, tree type, tree expr, bool init_const)
>  {
>    tree result;
>    tree expr_for_warning;
> @@ -1754,7 +1757,9 @@ convert_and_check (location_t loc, tree type, tree expr)
>      {
>        tree orig_type = TREE_TYPE (expr);
>        expr = TREE_OPERAND (expr, 0);
> -      expr_for_warning = convert (orig_type, expr);
> +      expr_for_warning = (init_const
> +                         ? convert_init (orig_type, expr)
> +                         : convert (orig_type, expr));
>        if (orig_type == type)
>         return expr_for_warning;
>      }
> @@ -1764,7 +1769,7 @@ convert_and_check (location_t loc, tree type, tree expr)
>    if (TREE_TYPE (expr) == type)
>      return expr;
>
> -  result = convert (type, expr);
> +  result = init_const ? convert_init (type, expr) : convert (type, expr);
>
>    if (c_inhibit_evaluation_warnings == 0
>        && !TREE_OVERFLOW_P (expr)
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index f60714e3416..d5dad99ff97 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -886,7 +886,7 @@ extern tree c_alignof_expr (location_t, tree);
>     NOP_EXPR is used as a special case (see truthvalue_conversion).  */
>  extern void binary_op_error (rich_location *, enum tree_code, tree, tree);
>  extern tree fix_string_type (tree);
> -extern tree convert_and_check (location_t, tree, tree);
> +extern tree convert_and_check (location_t, tree, tree, bool = false);
>  extern bool c_determine_visibility (tree);
>  extern bool vector_types_compatible_elements_p (tree, tree);
>  extern void mark_valid_location_for_stdc_pragma (bool);
> @@ -908,6 +908,8 @@ extern tree c_common_get_narrower (tree, int *);
>  extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
>  extern void c_common_finalize_early_debug (void);
>
> +/* Used by convert_and_check; in front ends.  */
> +extern tree convert_init (tree, tree);
>
>  #define c_sizeof(LOC, T)  c_sizeof_or_alignof_type (LOC, T, true, false, 1)
>  #define c_alignof(LOC, T) c_sizeof_or_alignof_type (LOC, T, false, false, 1)
> diff --git a/gcc/c/c-convert.c b/gcc/c/c-convert.c
> index d0035a31723..905b26a09a1 100644
> --- a/gcc/c/c-convert.c
> +++ b/gcc/c/c-convert.c
> @@ -60,10 +60,13 @@ along with GCC; see the file COPYING3.  If not see
>     converted to type TYPE.  The TREE_TYPE of the value
>     is always TYPE.  This function implements all reasonable
>     conversions; callers should filter out those that are
> -   not permitted by the language being compiled.  */
> +   not permitted by the language being compiled.
> +   INIT_CONST is true if the conversion is for arithmetic types for a static
> +   initializer and folding must apply accordingly (discarding floating-point
> +   exceptions and assuming the default rounding mode is in effect).  */
>
> -tree
> -convert (tree type, tree expr)
> +static tree
> +c_convert (tree type, tree expr, bool init_const)
>  {
>    tree e = expr;
>    enum tree_code code = TREE_CODE (type);
> @@ -115,7 +118,7 @@ convert (tree type, tree expr)
>           && COMPLETE_TYPE_P (type))
>         {
>           expr = save_expr (expr);
> -         expr = c_fully_fold (expr, false, NULL);
> +         expr = c_fully_fold (expr, init_const, NULL);
>           tree check = ubsan_instrument_float_cast (loc, type, expr);
>           expr = fold_build1 (FIX_TRUNC_EXPR, type, expr);
>           if (check == NULL_TREE)
> @@ -173,10 +176,32 @@ convert (tree type, tree expr)
>
>      maybe_fold:
>        if (TREE_CODE (ret) != C_MAYBE_CONST_EXPR)
> -       ret = fold (ret);
> +       ret = init_const ? fold_init (ret) : fold (ret);
>        return ret;
>      }
>
>    error ("conversion to non-scalar type requested");
>    return error_mark_node;
>  }
> +
> +/* Create an expression whose value is that of EXPR, converted to type TYPE.
> +   The TREE_TYPE of the value is always TYPE.  This function implements all
> +   reasonable conversions; callers should filter out those that are not
> +   permitted by the language being compiled.  */
> +
> +tree
> +convert (tree type, tree expr)
> +{
> +  return c_convert (type, expr, false);
> +}
> +
> +/* Create an expression whose value is that of EXPR, converted to type TYPE, in
> +   a static initializer.  The TREE_TYPE of the value is always TYPE.  This
> +   function implements all reasonable conversions; callers should filter out
> +   those that are not permitted by the language being compiled.  */
> +
> +tree
> +convert_init (tree type, tree expr)
> +{
> +  return c_convert (type, expr, true);
> +}
> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index 0aac978c02e..782414f8c8c 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -53,12 +53,13 @@ along with GCC; see the file COPYING3.  If not see
>  #include "attribs.h"
>  #include "asan.h"
>
> -/* Possible cases of implicit bad conversions.  Used to select
> -   diagnostic messages in convert_for_assignment.  */
> +/* Possible cases of implicit conversions.  Used to select diagnostic messages
> +   and control folding initializers in convert_for_assignment.  */
>  enum impl_conv {
>    ic_argpass,
>    ic_assign,
>    ic_init,
> +  ic_init_const,
>    ic_return
>  };
>
> @@ -6802,6 +6803,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>          pedwarn (LOCATION, OPT, AS);                                     \
>          break;                                                           \
>        case ic_init:                                                      \
> +      case ic_init_const:                                                \
>          pedwarn_init (LOCATION, OPT, IN);                                \
>          break;                                                           \
>        case ic_return:                                                    \
> @@ -6838,6 +6840,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>           warning_at (LOCATION, OPT, AS, QUALS);                         \
>          break;                                                           \
>        case ic_init:                                                      \
> +      case ic_init_const:                                                \
>         if (PEDWARN)                                                     \
>           pedwarn (LOCATION, OPT, IN, QUALS);                            \
>         else                                                             \
> @@ -6886,6 +6889,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>           break;
>
>         case ic_init:
> +       case ic_init_const:
>           parmno = -2;
>           break;
>
> @@ -6919,6 +6923,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                      "%qT in assignment is invalid in C++", rhstype, type);
>             break;
>           case ic_init:
> +         case ic_init_const:
>             pedwarn_init (location, OPT_Wc___compat, "enum conversion from "
>                           "%qT to %qT in initialization is invalid in C++",
>                           rhstype, type);
> @@ -7029,7 +7034,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>               && sanitize_flags_p (SANITIZE_FLOAT_CAST)))
>         in_late_binary_op = true;
>        tree ret = convert_and_check (expr_loc != UNKNOWN_LOCATION
> -                                   ? expr_loc : location, type, orig_rhs);
> +                                   ? expr_loc : location, type, orig_rhs,
> +                                   errtype == ic_init_const);
>        in_late_binary_op = save;
>        return ret;
>      }
> @@ -7252,6 +7258,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                 break;
>               }
>             case ic_init:
> +           case ic_init_const:
>               {
>                 const char msg[] = G_("initialization from pointer to "
>                                       "non-enclosed address space");
> @@ -7296,6 +7303,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                         "a candidate for a format attribute");
>             break;
>           case ic_init:
> +         case ic_init_const:
>             warning_at (location, OPT_Wsuggest_attribute_format,
>                         "initialization left-hand side might be "
>                         "a candidate for a format attribute");
> @@ -7339,6 +7347,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                           "incompatible scalar storage order", type, rhstype);
>             break;
>           case ic_init:
> +         case ic_init_const:
>             /* Likewise.  */
>             if (TREE_CODE (rhs) != CALL_EXPR
>                 || (t = get_callee_fndecl (rhs)) == NULL_TREE
> @@ -7465,6 +7474,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                              "differ in signedness", rhstype, type);
>                     break;
>                   case ic_init:
> +                 case ic_init_const:
>                     pedwarn_init (location, OPT_Wpointer_sign,
>                                   "pointer targets in initialization of %qT "
>                                   "from %qT differ in signedness", type,
> @@ -7530,6 +7540,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                          type, rhstype);
>               break;
>             case ic_init:
> +           case ic_init_const:
>               if (bltin)
>                 pedwarn_init (location, OPT_Wincompatible_pointer_types,
>                               "initialization of %qT from pointer to "
> @@ -7599,6 +7610,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                      "without a cast", type, rhstype);
>             break;
>           case ic_init:
> +         case ic_init_const:
>             pedwarn_init (location, OPT_Wint_conversion,
>                           "initialization of %qT from %qT makes pointer from "
>                           "integer without a cast", type, rhstype);
> @@ -7635,6 +7647,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>                    "without a cast", type, rhstype);
>           break;
>         case ic_init:
> +       case ic_init_const:
>           pedwarn_init (location, OPT_Wint_conversion,
>                         "initialization of %qT from %qT makes integer from "
>                         "pointer without a cast", type, rhstype);
> @@ -7686,6 +7699,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>         break;
>        }
>      case ic_init:
> +    case ic_init_const:
>        {
>         const char msg[]
>           = G_("incompatible types when initializing type %qT using type %qT");
> @@ -8195,7 +8209,9 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>        if (TREE_CODE (TREE_TYPE (inside_init)) == POINTER_TYPE)
>         inside_init = convert_for_assignment (init_loc, UNKNOWN_LOCATION,
>                                               type, inside_init, origtype,
> -                                             ic_init, null_pointer_constant,
> +                                             (require_constant
> +                                              ? ic_init_const
> +                                              : ic_init), null_pointer_constant,
>                                               NULL_TREE, NULL_TREE, 0);
>        return inside_init;
>      }
> @@ -8215,7 +8231,8 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>                               inside_init);
>        inside_init
>         = convert_for_assignment (init_loc, UNKNOWN_LOCATION, type,
> -                                 inside_init, origtype, ic_init,
> +                                 inside_init, origtype,
> +                                 require_constant ? ic_init_const : ic_init,
>                                   null_pointer_constant, NULL_TREE, NULL_TREE,
>                                   0);
>
> diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
> index d035e611be4..7e6752af1df 100644
> --- a/gcc/cp/cvt.c
> +++ b/gcc/cp/cvt.c
> @@ -1685,6 +1685,15 @@ convert (tree type, tree expr)
>                       tf_warning_or_error);
>  }
>
> +/* Like convert, but in a static initializer (called from
> +   convert_and_check).  */
> +
> +tree
> +convert_init (tree type, tree expr)
> +{
> +  return convert (type, expr);
> +}
> +
>  /* Like cp_convert, except permit conversions to take place which
>     are not normally allowed due to access restrictions
>     (such as conversion from sub-type to private super-type).  */
> diff --git a/gcc/fold-const.c b/gcc/fold-const.c
> index 2d3ba07e541..90d82257ae7 100644
> --- a/gcc/fold-const.c
> +++ b/gcc/fold-const.c
> @@ -13940,6 +13940,18 @@ fold_build_call_array_loc (location_t loc, tree type, tree fn,
>    flag_trapv = saved_trapv;\
>    folding_initializer = saved_folding_initializer;
>
> +tree
> +fold_init (tree expr)
> +{
> +  tree result;
> +  START_FOLD_INIT;
> +
> +  result = fold (expr);
> +
> +  END_FOLD_INIT;
> +  return result;
> +}
> +
>  tree
>  fold_build1_initializer_loc (location_t loc, enum tree_code code,
>                              tree type, tree op)
> diff --git a/gcc/fold-const.h b/gcc/fold-const.h
> index fed476842c7..56e9d399c0d 100644
> --- a/gcc/fold-const.h
> +++ b/gcc/fold-const.h
> @@ -44,6 +44,7 @@ extern void shift_bytes_in_array_right (unsigned char *, unsigned int,
>     subexpressions are not changed.  */
>
>  extern tree fold (tree);
> +extern tree fold_init (tree);
>  #define fold_unary(CODE,T1,T2)\
>     fold_unary_loc (UNKNOWN_LOCATION, CODE, T1, T2)
>  extern tree fold_unary_loc (location_t, enum tree_code, tree, tree);
> diff --git a/gcc/testsuite/gcc.dg/init-rounding-math-1.c b/gcc/testsuite/gcc.dg/init-rounding-math-1.c
> new file mode 100644
> index 00000000000..2bece1a09d5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/init-rounding-math-1.c
> @@ -0,0 +1,11 @@
> +/* Test static initializer folding of implicit conversions to floating point
> +   types, even with -frounding-math and related options.  Bug 103031.  */
> +/* { dg-do compile } */
> +/* { dg-options "-frounding-math -ftrapping-math -fsignaling-nans" } */
> +
> +float f1 = -1ULL;
> +float f2 = __DBL_MAX__;
> +float f3 = __DBL_MIN__;
> +float f4 = 0.1;
> +float f5 = __builtin_nans ("");
> +double d1 = -1ULL;
>
> --
> Joseph S. Myers
> joseph@codesourcery.com
  

Patch

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 32c7e3e8972..436df45df68 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -1739,10 +1739,13 @@  unsafe_conversion_p (tree type, tree expr, tree result, bool check_sign)
 
 /* Convert EXPR to TYPE, warning about conversion problems with constants.
    Invoke this function on every expression that is converted implicitly,
-   i.e. because of language rules and not because of an explicit cast.  */
+   i.e. because of language rules and not because of an explicit cast.
+   INIT_CONST is true if the conversion is for arithmetic types for a static
+   initializer and folding must apply accordingly (discarding floating-point
+   exceptions and assuming the default rounding mode is in effect).  */
 
 tree
-convert_and_check (location_t loc, tree type, tree expr)
+convert_and_check (location_t loc, tree type, tree expr, bool init_const)
 {
   tree result;
   tree expr_for_warning;
@@ -1754,7 +1757,9 @@  convert_and_check (location_t loc, tree type, tree expr)
     {
       tree orig_type = TREE_TYPE (expr);
       expr = TREE_OPERAND (expr, 0);
-      expr_for_warning = convert (orig_type, expr);
+      expr_for_warning = (init_const
+			  ? convert_init (orig_type, expr)
+			  : convert (orig_type, expr));
       if (orig_type == type)
 	return expr_for_warning;
     }
@@ -1764,7 +1769,7 @@  convert_and_check (location_t loc, tree type, tree expr)
   if (TREE_TYPE (expr) == type)
     return expr;
 
-  result = convert (type, expr);
+  result = init_const ? convert_init (type, expr) : convert (type, expr);
 
   if (c_inhibit_evaluation_warnings == 0
       && !TREE_OVERFLOW_P (expr)
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index f60714e3416..d5dad99ff97 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -886,7 +886,7 @@  extern tree c_alignof_expr (location_t, tree);
    NOP_EXPR is used as a special case (see truthvalue_conversion).  */
 extern void binary_op_error (rich_location *, enum tree_code, tree, tree);
 extern tree fix_string_type (tree);
-extern tree convert_and_check (location_t, tree, tree);
+extern tree convert_and_check (location_t, tree, tree, bool = false);
 extern bool c_determine_visibility (tree);
 extern bool vector_types_compatible_elements_p (tree, tree);
 extern void mark_valid_location_for_stdc_pragma (bool);
@@ -908,6 +908,8 @@  extern tree c_common_get_narrower (tree, int *);
 extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 
+/* Used by convert_and_check; in front ends.  */
+extern tree convert_init (tree, tree);
 
 #define c_sizeof(LOC, T)  c_sizeof_or_alignof_type (LOC, T, true, false, 1)
 #define c_alignof(LOC, T) c_sizeof_or_alignof_type (LOC, T, false, false, 1)
diff --git a/gcc/c/c-convert.c b/gcc/c/c-convert.c
index d0035a31723..905b26a09a1 100644
--- a/gcc/c/c-convert.c
+++ b/gcc/c/c-convert.c
@@ -60,10 +60,13 @@  along with GCC; see the file COPYING3.  If not see
    converted to type TYPE.  The TREE_TYPE of the value
    is always TYPE.  This function implements all reasonable
    conversions; callers should filter out those that are
-   not permitted by the language being compiled.  */
+   not permitted by the language being compiled.
+   INIT_CONST is true if the conversion is for arithmetic types for a static
+   initializer and folding must apply accordingly (discarding floating-point
+   exceptions and assuming the default rounding mode is in effect).  */
 
-tree
-convert (tree type, tree expr)
+static tree
+c_convert (tree type, tree expr, bool init_const)
 {
   tree e = expr;
   enum tree_code code = TREE_CODE (type);
@@ -115,7 +118,7 @@  convert (tree type, tree expr)
 	  && COMPLETE_TYPE_P (type))
 	{
 	  expr = save_expr (expr);
-	  expr = c_fully_fold (expr, false, NULL);
+	  expr = c_fully_fold (expr, init_const, NULL);
 	  tree check = ubsan_instrument_float_cast (loc, type, expr);
 	  expr = fold_build1 (FIX_TRUNC_EXPR, type, expr);
 	  if (check == NULL_TREE)
@@ -173,10 +176,32 @@  convert (tree type, tree expr)
 
     maybe_fold:
       if (TREE_CODE (ret) != C_MAYBE_CONST_EXPR)
-	ret = fold (ret);
+	ret = init_const ? fold_init (ret) : fold (ret);
       return ret;
     }
 
   error ("conversion to non-scalar type requested");
   return error_mark_node;
 }
+
+/* Create an expression whose value is that of EXPR, converted to type TYPE.
+   The TREE_TYPE of the value is always TYPE.  This function implements all
+   reasonable conversions; callers should filter out those that are not
+   permitted by the language being compiled.  */
+
+tree
+convert (tree type, tree expr)
+{
+  return c_convert (type, expr, false);
+}
+
+/* Create an expression whose value is that of EXPR, converted to type TYPE, in
+   a static initializer.  The TREE_TYPE of the value is always TYPE.  This
+   function implements all reasonable conversions; callers should filter out
+   those that are not permitted by the language being compiled.  */
+
+tree
+convert_init (tree type, tree expr)
+{
+  return c_convert (type, expr, true);
+}
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 0aac978c02e..782414f8c8c 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -53,12 +53,13 @@  along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 
-/* Possible cases of implicit bad conversions.  Used to select
-   diagnostic messages in convert_for_assignment.  */
+/* Possible cases of implicit conversions.  Used to select diagnostic messages
+   and control folding initializers in convert_for_assignment.  */
 enum impl_conv {
   ic_argpass,
   ic_assign,
   ic_init,
+  ic_init_const,
   ic_return
 };
 
@@ -6802,6 +6803,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
         pedwarn (LOCATION, OPT, AS);                                     \
         break;                                                           \
       case ic_init:                                                      \
+      case ic_init_const:                                                \
         pedwarn_init (LOCATION, OPT, IN);                                \
         break;                                                           \
       case ic_return:                                                    \
@@ -6838,6 +6840,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	  warning_at (LOCATION, OPT, AS, QUALS);                         \
         break;                                                           \
       case ic_init:                                                      \
+      case ic_init_const:                                                \
 	if (PEDWARN)							 \
 	  pedwarn (LOCATION, OPT, IN, QUALS);                            \
 	else								 \
@@ -6886,6 +6889,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	  break;
 
 	case ic_init:
+	case ic_init_const:
 	  parmno = -2;
 	  break;
 
@@ -6919,6 +6923,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 		     "%qT in assignment is invalid in C++", rhstype, type);
 	    break;
 	  case ic_init:
+	  case ic_init_const:
 	    pedwarn_init (location, OPT_Wc___compat, "enum conversion from "
 			  "%qT to %qT in initialization is invalid in C++",
 			  rhstype, type);
@@ -7029,7 +7034,8 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	      && sanitize_flags_p (SANITIZE_FLOAT_CAST)))
 	in_late_binary_op = true;
       tree ret = convert_and_check (expr_loc != UNKNOWN_LOCATION
-				    ? expr_loc : location, type, orig_rhs);
+				    ? expr_loc : location, type, orig_rhs,
+				    errtype == ic_init_const);
       in_late_binary_op = save;
       return ret;
     }
@@ -7252,6 +7258,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 		break;
 	      }
 	    case ic_init:
+	    case ic_init_const:
 	      {
 		const char msg[] = G_("initialization from pointer to "
 				      "non-enclosed address space");
@@ -7296,6 +7303,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 			"a candidate for a format attribute");
 	    break;
 	  case ic_init:
+	  case ic_init_const:
 	    warning_at (location, OPT_Wsuggest_attribute_format,
 			"initialization left-hand side might be "
 			"a candidate for a format attribute");
@@ -7339,6 +7347,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 			  "incompatible scalar storage order", type, rhstype);
 	    break;
 	  case ic_init:
+	  case ic_init_const:
 	    /* Likewise.  */
 	    if (TREE_CODE (rhs) != CALL_EXPR
 		|| (t = get_callee_fndecl (rhs)) == NULL_TREE
@@ -7465,6 +7474,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 			     "differ in signedness", rhstype, type);
 		    break;
 		  case ic_init:
+		  case ic_init_const:
 		    pedwarn_init (location, OPT_Wpointer_sign,
 				  "pointer targets in initialization of %qT "
 				  "from %qT differ in signedness", type,
@@ -7530,6 +7540,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 			 type, rhstype);
 	      break;
 	    case ic_init:
+	    case ic_init_const:
 	      if (bltin)
 		pedwarn_init (location, OPT_Wincompatible_pointer_types,
 			      "initialization of %qT from pointer to "
@@ -7599,6 +7610,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 		     "without a cast", type, rhstype);
 	    break;
 	  case ic_init:
+	  case ic_init_const:
 	    pedwarn_init (location, OPT_Wint_conversion,
 			  "initialization of %qT from %qT makes pointer from "
 			  "integer without a cast", type, rhstype);
@@ -7635,6 +7647,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 		   "without a cast", type, rhstype);
 	  break;
 	case ic_init:
+	case ic_init_const:
 	  pedwarn_init (location, OPT_Wint_conversion,
 			"initialization of %qT from %qT makes integer from "
 			"pointer without a cast", type, rhstype);
@@ -7686,6 +7699,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	break;
       }
     case ic_init:
+    case ic_init_const:
       {
 	const char msg[]
 	  = G_("incompatible types when initializing type %qT using type %qT");
@@ -8195,7 +8209,9 @@  digest_init (location_t init_loc, tree type, tree init, tree origtype,
       if (TREE_CODE (TREE_TYPE (inside_init)) == POINTER_TYPE)
 	inside_init = convert_for_assignment (init_loc, UNKNOWN_LOCATION,
 					      type, inside_init, origtype,
-					      ic_init, null_pointer_constant,
+					      (require_constant
+					       ? ic_init_const
+					       : ic_init), null_pointer_constant,
 					      NULL_TREE, NULL_TREE, 0);
       return inside_init;
     }
@@ -8215,7 +8231,8 @@  digest_init (location_t init_loc, tree type, tree init, tree origtype,
 			      inside_init);
       inside_init
 	= convert_for_assignment (init_loc, UNKNOWN_LOCATION, type,
-				  inside_init, origtype, ic_init,
+				  inside_init, origtype,
+				  require_constant ? ic_init_const : ic_init,
 				  null_pointer_constant, NULL_TREE, NULL_TREE,
 				  0);
 
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index d035e611be4..7e6752af1df 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1685,6 +1685,15 @@  convert (tree type, tree expr)
 		      tf_warning_or_error);
 }
 
+/* Like convert, but in a static initializer (called from
+   convert_and_check).  */
+
+tree
+convert_init (tree type, tree expr)
+{
+  return convert (type, expr);
+}
+
 /* Like cp_convert, except permit conversions to take place which
    are not normally allowed due to access restrictions
    (such as conversion from sub-type to private super-type).  */
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 2d3ba07e541..90d82257ae7 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -13940,6 +13940,18 @@  fold_build_call_array_loc (location_t loc, tree type, tree fn,
   flag_trapv = saved_trapv;\
   folding_initializer = saved_folding_initializer;
 
+tree
+fold_init (tree expr)
+{
+  tree result;
+  START_FOLD_INIT;
+
+  result = fold (expr);
+
+  END_FOLD_INIT;
+  return result;
+}
+
 tree
 fold_build1_initializer_loc (location_t loc, enum tree_code code,
 			     tree type, tree op)
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index fed476842c7..56e9d399c0d 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -44,6 +44,7 @@  extern void shift_bytes_in_array_right (unsigned char *, unsigned int,
    subexpressions are not changed.  */
 
 extern tree fold (tree);
+extern tree fold_init (tree);
 #define fold_unary(CODE,T1,T2)\
    fold_unary_loc (UNKNOWN_LOCATION, CODE, T1, T2)
 extern tree fold_unary_loc (location_t, enum tree_code, tree, tree);
diff --git a/gcc/testsuite/gcc.dg/init-rounding-math-1.c b/gcc/testsuite/gcc.dg/init-rounding-math-1.c
new file mode 100644
index 00000000000..2bece1a09d5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/init-rounding-math-1.c
@@ -0,0 +1,11 @@ 
+/* Test static initializer folding of implicit conversions to floating point
+   types, even with -frounding-math and related options.  Bug 103031.  */
+/* { dg-do compile } */
+/* { dg-options "-frounding-math -ftrapping-math -fsignaling-nans" } */
+
+float f1 = -1ULL;
+float f2 = __DBL_MAX__;
+float f3 = __DBL_MIN__;
+float f4 = 0.1;
+float f5 = __builtin_nans ("");
+double d1 = -1ULL;