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