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

Richard Biener richard.guenther@gmail.com
Wed Nov 3 12:38:27 GMT 2021


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


More information about the Gcc-patches mailing list