From 7a37fa90703f62301130a3e1ef7ad4fade1bd786 Mon Sep 17 00:00:00 2001 From: Mikhail Maltsev Date: Fri, 15 May 2015 18:02:50 +0000 Subject: [PATCH] re PR c/48956 (-Wconversion should warn when a complex value is assigned to a real result) PR c/48956 gcc/c-family/ * c-common.c (int_safely_convertible_to_real_p): Define. (unsafe_conversion_p): Check conversions involving complex types. (conversion_warning): Add new warning message for conversions which discard imaginary component. * c-common.h: (enum conversion_safety): Add new enumerator for such conversions. gcc/testsuite/ * gcc.dg/Wconversion-complex-c99.c: New test. * gcc.dg/Wconversion-complex-gnu.c: New test. From-SVN: r223223 --- gcc/c-family/ChangeLog | 10 ++ gcc/c-family/c-common.c | 163 ++++++++++++++++-- gcc/c-family/c-common.h | 25 ++- gcc/testsuite/ChangeLog | 6 + .../gcc.dg/Wconversion-complex-c99.c | 138 +++++++++++++++ .../gcc.dg/Wconversion-complex-gnu.c | 127 ++++++++++++++ 6 files changed, 446 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wconversion-complex-c99.c create mode 100644 gcc/testsuite/gcc.dg/Wconversion-complex-gnu.c diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 48d483e7b1b..199ba43de4f 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,13 @@ +2015-05-15 Mikhail Maltsev + + PR c/48956 + * c-common.c (int_safely_convertible_to_real_p): Define. + (unsafe_conversion_p): Check conversions involving complex types. + (conversion_warning): Add new warning message for conversions which + discard imaginary component. + * c-common.h: (enum conversion_safety): Add new enumerator for such + conversions. + 2015-05-14 Marek Polacek PR c/66066 diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 31c4c0d16e0..8c7fdd23991 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -2707,17 +2707,42 @@ shorten_binary_op (tree result_type, tree op0, tree op1, bool bitwise) return result_type; } -/* Checks if expression EXPR of real/integer type cannot be converted - to the real/integer type TYPE. Function returns non-zero when: +/* Returns true iff any integer value of type FROM_TYPE can be represented as + real of type TO_TYPE. This is a helper function for unsafe_conversion_p. */ + +static bool +int_safely_convertible_to_real_p (const_tree from_type, const_tree to_type) +{ + tree type_low_bound = TYPE_MIN_VALUE (from_type); + tree type_high_bound = TYPE_MAX_VALUE (from_type); + REAL_VALUE_TYPE real_low_bound = + real_value_from_int_cst (0, type_low_bound); + REAL_VALUE_TYPE real_high_bound = + real_value_from_int_cst (0, type_high_bound); + + return exact_real_truncate (TYPE_MODE (to_type), &real_low_bound) + && exact_real_truncate (TYPE_MODE (to_type), &real_high_bound); +} + +/* Checks if expression EXPR of complex/real/integer type cannot be converted + to the complex/real/integer type TYPE. Function returns non-zero when: * EXPR is a constant which cannot be exactly converted to TYPE. * EXPR is not a constant and size of EXPR's type > than size of TYPE, - for EXPR type and TYPE being both integers or both real. + for EXPR type and TYPE being both integers or both real, or both + complex. + * EXPR is not a constant of complex type and TYPE is a real or + an integer. * EXPR is not a constant of real type and TYPE is an integer. * EXPR is not a constant of integer type which cannot be exactly converted to real type. + Function allows conversions between types of different signedness and can return SAFE_CONVERSION (zero) in that case. Function can produce - signedness warnings if PRODUCE_WARNS is true. */ + signedness warnings if PRODUCE_WARNS is true. + + Function allows conversions from complex constants to non-complex types, + provided that imaginary part is zero and real part can be safely converted + to TYPE. */ enum conversion_safety unsafe_conversion_p (location_t loc, tree type, tree expr, bool produce_warns) @@ -2728,6 +2753,11 @@ unsafe_conversion_p (location_t loc, tree type, tree expr, bool produce_warns) if (TREE_CODE (expr) == REAL_CST || TREE_CODE (expr) == INTEGER_CST) { + /* If type is complex, we are interested in compatibility with + underlying type. */ + if (TREE_CODE (type) == COMPLEX_TYPE) + type = TREE_TYPE (type); + /* Warn for real constant that is not an exact integer converted to integer type. */ if (TREE_CODE (expr_type) == REAL_TYPE @@ -2777,6 +2807,63 @@ unsafe_conversion_p (location_t loc, tree type, tree expr, bool produce_warns) } } } + + else if (TREE_CODE (expr) == COMPLEX_CST) + { + tree imag_part = TREE_IMAGPART (expr); + /* Conversion from complex constant with zero imaginary part, + perform check for conversion of real part. */ + if ((TREE_CODE (imag_part) == REAL_CST + && real_zerop (imag_part)) + || (TREE_CODE (imag_part) == INTEGER_CST + && integer_zerop (imag_part))) + /* Note: in this branch we use recursive call to unsafe_conversion_p + with different type of EXPR, but it is still safe, because when EXPR + is a constant, it's type is not used in text of generated warnings + (otherwise they could sound misleading). */ + return unsafe_conversion_p (loc, type, TREE_REALPART (expr), + produce_warns); + /* Conversion from complex constant with non-zero imaginary part. */ + else + { + /* Conversion to complex type. + Perform checks for both real and imaginary parts. */ + if (TREE_CODE (type) == COMPLEX_TYPE) + { + /* Unfortunately, produce_warns must be false in two subsequent + calls of unsafe_conversion_p, because otherwise we could + produce strange "double" warnings, if both real and imaginary + parts have conversion problems related to signedness. + + For example: + int32_t _Complex a = 0x80000000 + 0x80000000i; + + Possible solution: add a separate function for checking + constants and combine result of two calls appropriately. */ + enum conversion_safety re_safety = + unsafe_conversion_p (loc, type, TREE_REALPART (expr), false); + enum conversion_safety im_safety = + unsafe_conversion_p (loc, type, imag_part, false); + + /* Merge the results into appropriate single warning. */ + + /* Note: this case includes SAFE_CONVERSION, i.e. success. */ + if (re_safety == im_safety) + give_warning = re_safety; + else if (!re_safety && im_safety) + give_warning = im_safety; + else if (re_safety && !im_safety) + give_warning = re_safety; + else + give_warning = UNSAFE_OTHER; + } + /* Warn about conversion from complex to real or integer type. */ + else + give_warning = UNSAFE_IMAGINARY; + } + } + + /* Checks for remaining case: EXPR is not constant. */ else { /* Warn for real types converted to integer types. */ @@ -2856,20 +2943,11 @@ unsafe_conversion_p (location_t loc, tree type, tree expr, bool produce_warns) else if (TREE_CODE (expr_type) == INTEGER_TYPE && TREE_CODE (type) == REAL_TYPE) { - tree type_low_bound, type_high_bound; - REAL_VALUE_TYPE real_low_bound, real_high_bound; - /* Don't warn about char y = 0xff; float x = (int) y; */ expr = get_unwidened (expr, 0); expr_type = TREE_TYPE (expr); - type_low_bound = TYPE_MIN_VALUE (expr_type); - type_high_bound = TYPE_MAX_VALUE (expr_type); - real_low_bound = real_value_from_int_cst (0, type_low_bound); - real_high_bound = real_value_from_int_cst (0, type_high_bound); - - if (!exact_real_truncate (TYPE_MODE (type), &real_low_bound) - || !exact_real_truncate (TYPE_MODE (type), &real_high_bound)) + if (!int_safely_convertible_to_real_p (expr_type, type)) give_warning = UNSAFE_OTHER; } @@ -2878,6 +2956,58 @@ unsafe_conversion_p (location_t loc, tree type, tree expr, bool produce_warns) && TREE_CODE (type) == REAL_TYPE && TYPE_PRECISION (type) < TYPE_PRECISION (expr_type)) give_warning = UNSAFE_REAL; + + /* Check conversion between two complex types. */ + else if (TREE_CODE (expr_type) == COMPLEX_TYPE + && TREE_CODE (type) == COMPLEX_TYPE) + { + /* Extract underlying types (i.e., type of real and imaginary + parts) of expr_type and type. */ + tree from_type = TREE_TYPE (expr_type); + tree to_type = TREE_TYPE (type); + + /* Warn for real types converted to integer types. */ + if (TREE_CODE (from_type) == REAL_TYPE + && TREE_CODE (to_type) == INTEGER_TYPE) + give_warning = UNSAFE_REAL; + + /* Warn for real types converted to smaller real types. */ + else if (TREE_CODE (from_type) == REAL_TYPE + && TREE_CODE (to_type) == REAL_TYPE + && TYPE_PRECISION (to_type) < TYPE_PRECISION (from_type)) + give_warning = UNSAFE_REAL; + + /* Check conversion for complex integer types. Here implementation + is simpler than for real-domain integers because it does not + involve sophisticated cases, such as bitmasks, casts, etc. */ + else if (TREE_CODE (from_type) == INTEGER_TYPE + && TREE_CODE (to_type) == INTEGER_TYPE) + { + /* Warn for integer types converted to smaller integer types. */ + if (TYPE_PRECISION (to_type) < TYPE_PRECISION (from_type)) + give_warning = UNSAFE_OTHER; + + /* Check for different signedness, see case for real-domain + integers (above) for a more detailed comment. */ + else if (((TYPE_PRECISION (to_type) == TYPE_PRECISION (from_type) + && TYPE_UNSIGNED (to_type) != TYPE_UNSIGNED (from_type)) + || (TYPE_UNSIGNED (to_type) && !TYPE_UNSIGNED (from_type))) + && produce_warns) + warning_at (loc, OPT_Wsign_conversion, + "conversion to %qT from %qT " + "may change the sign of the result", + type, expr_type); + } + else if (TREE_CODE (from_type) == INTEGER_TYPE + && TREE_CODE (to_type) == REAL_TYPE + && !int_safely_convertible_to_real_p (from_type, to_type)) + give_warning = UNSAFE_OTHER; + } + + /* Warn for complex types converted to real or integer types. */ + else if (TREE_CODE (expr_type) == COMPLEX_TYPE + && TREE_CODE (type) != COMPLEX_TYPE) + give_warning = UNSAFE_IMAGINARY; } return give_warning; @@ -2927,6 +3057,7 @@ conversion_warning (location_t loc, tree type, tree expr) case REAL_CST: case INTEGER_CST: + case COMPLEX_CST: conversion_kind = unsafe_conversion_p (loc, type, expr, true); if (conversion_kind == UNSAFE_REAL) warning_at (loc, OPT_Wfloat_conversion, @@ -2956,6 +3087,10 @@ conversion_warning (location_t loc, tree type, tree expr) warning_at (loc, OPT_Wfloat_conversion, "conversion to %qT from %qT may alter its value", type, expr_type); + else if (conversion_kind == UNSAFE_IMAGINARY) + warning_at (loc, OPT_Wconversion, + "conversion to %qT from %qT discards imaginary component", + type, expr_type); else if (conversion_kind) warning_at (loc, OPT_Wconversion, "conversion to %qT from %qT may alter its value", diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 444d80d3fed..62eac9f74b6 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -728,15 +728,22 @@ struct visibility_flags unsigned inlines_hidden : 1; /* True when -finlineshidden in effect. */ }; -/* These enumerators are possible types of unsafe conversions. - SAFE_CONVERSION The conversion is safe - UNSAFE_OTHER Another type of conversion with problems - UNSAFE_SIGN Conversion between signed and unsigned integers - which are all warned about immediately, so this is unused - UNSAFE_REAL Conversions that reduce the precision of reals - including conversions from reals to integers - */ -enum conversion_safety { SAFE_CONVERSION = 0, UNSAFE_OTHER, UNSAFE_SIGN, UNSAFE_REAL }; +/* These enumerators are possible types of unsafe conversions. */ +enum conversion_safety { + /* The conversion is safe. */ + SAFE_CONVERSION = 0, + /* Another type of conversion with problems. */ + UNSAFE_OTHER, + /* Conversion between signed and unsigned integers + which are all warned about immediately, so this is unused. */ + UNSAFE_SIGN, + /* Conversions that reduce the precision of reals including conversions + from reals to integers. */ + UNSAFE_REAL, + /* Conversions from complex to reals or integers, that discard imaginary + component. */ + UNSAFE_IMAGINARY +}; /* Global visibility options. */ extern struct visibility_flags visibility_options; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5875476821b..cdd1c2885d8 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2015-05-15 Mikhail Maltsev + + PR c/48956 + * gcc.dg/Wconversion-complex-c99.c: New test. + * gcc.dg/Wconversion-complex-gnu.c: New test. + 2015-05-15 Marc Glisse PR tree-optimization/64454 diff --git a/gcc/testsuite/gcc.dg/Wconversion-complex-c99.c b/gcc/testsuite/gcc.dg/Wconversion-complex-c99.c new file mode 100644 index 00000000000..0e6c39022f4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wconversion-complex-c99.c @@ -0,0 +1,138 @@ +/* PR c/48956: Test for diagnostics for implicit conversions from complex + to real types and narrowing conversions of complex types. */ + +/* Architecture restrictions taken from Wconversion-real-integer.c. + Likewise, the magic value 16777217. */ + +/* { dg-do compile } */ +/* { dg-skip-if "doubles are floats,ints are 16bits" { "avr-*-*" } { "*" } { "" } } */ +/* { dg-options " -std=c99 -pedantic -Wconversion " } */ +/* { dg-require-effective-target int32plus } */ +/* { dg-require-effective-target double64plus } */ + +/* A number which does not fit into float. */ +#define MAX_FLOAT_PLUS 16777217. + +/* Other types could be added, but that won't affect test coverage. */ +void ffloatc (float _Complex); +void fdoublec (double _Complex); + +void ffloat (float); +void fdouble (double); + +void fsi (int); +void fui (unsigned); + +float _Complex vfloatc; +double _Complex vdoublec; + +float vfloat; +double vdouble; + +int vsi; +unsigned vui; + +/* Check implicit conversions of complex values to reals. */ +void +var_complex_to_real (void) +{ + float _Complex floatc = 0.; + double _Complex doublec = 0.; + + ffloatc (floatc); + fdoublec (doublec); + vfloatc = floatc; + vdoublec = doublec; + + ffloat (floatc); /* { dg-warning "conversion" } */ + fdouble (floatc); /* { dg-warning "conversion" } */ + vfloat = floatc; /* { dg-warning "conversion" } */ + vdouble = floatc; /* { dg-warning "conversion" } */ + + ffloat (doublec); /* { dg-warning "conversion" } */ + fdouble (doublec); /* { dg-warning "conversion" } */ + vfloat = doublec; /* { dg-warning "conversion" } */ + vdouble = doublec; /* { dg-warning "conversion" } */ +} + +/* Check implicit narrowing conversions of complex values. */ +void +var_complex_narrowing (void) +{ + float _Complex floatc = 0.; + double _Complex doublec = 0.; + + vdoublec = floatc; + vfloatc = doublec; /* { dg-warning "float-conversion" } */ + + fdoublec (floatc); + ffloatc (doublec); /* { dg-warning "float-conversion" } */ +} + +/* Check implicit conversions of complex values to integers. */ +void +var_complex_to_int (void) +{ + float _Complex floatc = 0.; + double _Complex doublec = 0.; + + fsi (floatc); /* { dg-warning "conversion" } */ + fui (floatc); /* { dg-warning "conversion" } */ + vsi = floatc; /* { dg-warning "conversion" } */ + vui = floatc; /* { dg-warning "conversion" } */ + + fsi (doublec); /* { dg-warning "conversion" } */ + fui (doublec); /* { dg-warning "conversion" } */ + vsi = doublec; /* { dg-warning "conversion" } */ + vui = doublec; /* { dg-warning "conversion" } */ +} + +/* Check implicit conversion of constant complex values to floats. */ +void +const_complex_to_real (void) +{ + ffloat (__builtin_complex (0., 1.)); /* { dg-warning "conversion" } */ + fdouble (__builtin_complex (0., 1.)); /* { dg-warning "conversion" } */ + + vfloat = __builtin_complex (0., 1.); /* { dg-warning "conversion" } */ + vdouble = __builtin_complex (0., 1.); /* { dg-warning "conversion" } */ + + vfloat = __builtin_complex (1., 0.) + __builtin_complex (1., 0.); + vdouble = __builtin_complex (0., 0.) * __builtin_complex (1., 1.); + ffloat (__builtin_complex (1., 0.) + __builtin_complex (1., 0.)); + fdouble (__builtin_complex (1., 0.) + __builtin_complex (1., 0.)); + + vfloat = __builtin_complex (MAX_FLOAT_PLUS, 0.); /* { dg-warning "float-conversion" } */ + ffloat (__builtin_complex (MAX_FLOAT_PLUS, 0.)); /* { dg-warning "float-conversion" } */ +} + +/* Check implicit conversion of constant complex values to integers. */ +void +const_complex_to_int (void) +{ + vsi = __builtin_complex (-1., 0.); + vui = __builtin_complex (1., 0.); + fsi (__builtin_complex (-1., 0.)); + fui (__builtin_complex (1., 0.)); + + vui = __builtin_complex (-1., 0.); /* { dg-warning "overflow" } */ + fui (__builtin_complex (-1., 0.)); /* { dg-warning "overflow" } */ + + vsi = __builtin_complex (0.5, 0.); /* { dg-warning "float-conversion" } */ + fui (__builtin_complex (0.5, 0.)); /* { dg-warning "float-conversion" } */ + + vsi = __builtin_complex (-0.5, 0.); /* { dg-warning "float-conversion" } */ + fui (__builtin_complex (-0.5, 0.)); /* { dg-warning "float-conversion" } */ +} + +/* Check implicit narrowing conversion of constant complex values to. */ +void +const_complex_narrowing (void) +{ + ffloatc (__builtin_complex (-100., 100.)); + + ffloatc (__builtin_complex (MAX_FLOAT_PLUS, 0.)); /* { dg-warning "float-conversion" } */ + ffloatc (__builtin_complex (0., MAX_FLOAT_PLUS)); /* { dg-warning "float-conversion" } */ + ffloatc (__builtin_complex (MAX_FLOAT_PLUS, MAX_FLOAT_PLUS)); /* { dg-warning "float-conversion" } */ +} + diff --git a/gcc/testsuite/gcc.dg/Wconversion-complex-gnu.c b/gcc/testsuite/gcc.dg/Wconversion-complex-gnu.c new file mode 100644 index 00000000000..3839a39bce4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wconversion-complex-gnu.c @@ -0,0 +1,127 @@ +/* PR c/48956: Test for diagnostics for implicit conversions involving complex + types. See also Wconversion-complex-c99.c. + + These tests cover integer complex values (which are GNU extensions). */ + +/* { dg-do compile } */ +/* { dg-skip-if "doubles are floats,ints are 16bits" { "avr-*-*" } { "*" } { "" } } */ +/* { dg-options " -std=gnu99 -Wconversion " } */ +/* { dg-require-effective-target int32plus } */ +/* { dg-require-effective-target double64plus } */ + +#include + +void fsi (int); +void fui (unsigned); +void ffloat (float); +int vsi; +unsigned int vui; +float vfloat; + +void fsic (int _Complex); +void fuic (unsigned _Complex); +void ffloatc (float _Complex); +int _Complex vsic; +unsigned _Complex vuic; +float _Complex vfloatc; + +/* Check implicit conversions of float complex-domain values to integer + complex-domain types. */ +void +var_float_to_int (void) +{ + double _Complex doublec = 0.; + + fsic (doublec); /* { dg-warning "float-conversion" } */ + fuic (doublec); /* { dg-warning "float-conversion" } */ + + vsic = doublec; /* { dg-warning "float-conversion" } */ + vuic = doublec; /* { dg-warning "float-conversion" } */ +} + +/* Check implicit conversions of integer complex-domain values to integer + real-domain types. */ +void +var_complex_to_real (void) +{ + int _Complex ic = 0; + unsigned _Complex uc = 0; + unsigned long long _Complex ullc = 0; + + fsic (ic); + fuic (uc); + vsic = ic; + vuic = uc; + + fsi (ic); /* { dg-warning "conversion" } */ + vsi = ic; /* { dg-warning "conversion" } */ + fui (uc); /* { dg-warning "conversion" } */ + vui = uc; /* { dg-warning "conversion" } */ + + fuic (ullc); /* { dg-warning "conversion" } */ + vuic = ullc; /* { dg-warning "conversion" } */ + + fui (ic); /* { dg-warning "conversion" } */ + vui = ic; /* { dg-warning "conversion" } */ +} + +/* Check implicit conversions of float complex-domain constants to integer + types. */ +void +const_float_to_int (void) +{ + fsic (1. - 1.i); + fuic (1. + 1.i); + vsic = 1. - 1.i; + vuic = 1. + 1.i; + + fsic (0.5 + 0.i); /* { dg-warning "float-conversion" } */ + vsic = 0.5 + 0.i; /* { dg-warning "float-conversion" } */ + fuic (0.5 + 0.i); /* { dg-warning "float-conversion" } */ +} + +/* Check implicit conversions of integer complex-domain constants to integer + types. */ +void +const_complex_int_to_real_int (void) +{ + fsi (-1 + 0i); + fui (1 + 0i); + vsi = -1 + 0i; + vui = 1 + 0i; + + fui (1 + 1i); /* { dg-warning "conversion" } */ + vui = 1 + 1i; /* { dg-warning "conversion" } */ + + fui (UINT_MAX + 1ull + 0i); /* { dg-warning "conversion" } */ + vui = UINT_MAX + 1ull + 0i; /* { dg-warning "conversion" } */ + + ffloat (UINT_MAX + 0i); /* { dg-warning "float-conversion" } */ + vfloat = UINT_MAX + 0i; /* { dg-warning "float-conversion" } */ +} + +void +const_complex_int_narrowing (void) +{ + fsic (1 - 1i); + fuic (1 + 1i); + vsic = 1 - 1i; + vuic = 1 + 1i; + + fuic (UINT_MAX + 1ull + 1i); /* { dg-warning "conversion" } */ + fuic ((UINT_MAX + 1ull) * 1i); /* { dg-warning "conversion" } */ + fuic ((UINT_MAX + 1ull) + (UINT_MAX + 1ull) * 1i); /* { dg-warning "conversion" } */ + + vuic = (UINT_MAX + 1ull) * 1i; /* { dg-warning "conversion" } */ + vuic = (UINT_MAX + 1ull) + 1i; /* { dg-warning "conversion" } */ + vuic = (UINT_MAX + 1ull) + (UINT_MAX + 1ull) * 1i; /* { dg-warning "conversion" } */ + + ffloatc (UINT_MAX * 1i); /* { dg-warning "float-conversion" } */ + ffloatc (UINT_MAX + 1i); /* { dg-warning "float-conversion" } */ + ffloatc (UINT_MAX + UINT_MAX * 1i); /* { dg-warning "float-conversion" } */ + + vfloatc = UINT_MAX * 1i; /* { dg-warning "float-conversion" } */ + vfloatc = UINT_MAX + 1i; /* { dg-warning "float-conversion" } */ + vfloatc = UINT_MAX + UINT_MAX * 1i; /* { dg-warning "float-conversion" } */ +} + -- 2.30.2