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

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.

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.c (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.
This commit is contained in:
Joseph Myers 2021-11-03 14:58:25 +00:00
parent 502ffb1f38
commit 600dcd74b8
8 changed files with 97 additions and 15 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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);
}

View file

@ -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);

View file

@ -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). */

View file

@ -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)

View file

@ -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);

View file

@ -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;