summaryrefslogtreecommitdiff
path: root/gcc/fortran/arith.c
diff options
context:
space:
mode:
authortkoenig <tkoenig@138bc75d-0d04-0410-961f-82ee72b054a4>2015-06-06 16:12:39 +0000
committertkoenig <tkoenig@138bc75d-0d04-0410-961f-82ee72b054a4>2015-06-06 16:12:39 +0000
commita74c2d0dc9f6431a4b99afc3ecaa1d69083fe989 (patch)
tree795f1293f203f94163c691716e122d7f76b5c07b /gcc/fortran/arith.c
parent9668af776f66ba6be4ce89c330849bea51de254e (diff)
downloadgcc-a74c2d0dc9f6431a4b99afc3ecaa1d69083fe989.tar.gz
2015-06-06 Thomas Koenig <tkoenig@netcologne.de>
PR fortran/47359 * arith.c (eval_intrinsic_op): Set warn flag for gfc_type_convert_binary if -Wconversion or -Wconversion-extra are set. (wprecision_real_real): New function. (wprecision_int_real): New function. (gfc_int2int): If -fno-range-check and -Wconversion are specified and it is a narrowing conversion, warn. (gfc_int2real): If there is a change in value for the conversion, warn. (gfc_int2complex): Likewise. (gfc_real2int): If there is a fractional part to the real number, warn with -Wconversion, otherwise warn with -Wconversion-extra. (gfc_real2real): Emit warning if the constant was changed by conversion with either -Wconversion or -Wconversion-extra. With -Wconversion-extra, warn if no warning was issued earlier. (gfc_real2complex): Likewise. (gfc_complex2int): For -Wconversion or -Wconversion-extra, if there was an imaginary part, warn; otherwise, warn for change in value. Warn with -Wconversion-extra if no other warning was issued. (gfc_complex2real): For -Wconversion or -Wconversion-extra, if there was an imaginary part, warn; otherwise, warn for change in value. Warn with -Wconversion-extra if no other warning was issued. (gfc_complex2complex): For -Wconversion, warn if the value of either the real or the imaginary part was changed. Warn for -Wconversion-extra if no prior warning was issued. * expr.c (gfc_check_assign): Remove check for change in value. * primary.c (match_real_constant): For -Wconversion-extra, check against a number in which the last non-zero digit has been replaced with a zero. If the number compares equal, warn. * intrinsic.c (gfc_convert_type_warn): Do not warn about constant conversions. 2015-06-06 Thomas Koenig <tkoenig@netcologne.de> PR fortran/47359 * gfortran.dg/array_constructor_type_17.f03: Adjust error message. * gfortran.dg/warn_conversion.f90: Add warning for change in value for assignment. * gfortran.dg/warn_conversion_3.f90: Add warnings. * gfortran.dg/warn_conversion_5.f90: New test. * gfortran.dg/warn_conversion_6.f90: New test. * gfortran.dg/warn_conversion_7.f90: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@224190 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/fortran/arith.c')
-rw-r--r--gcc/fortran/arith.c228
1 files changed, 226 insertions, 2 deletions
diff --git a/gcc/fortran/arith.c b/gcc/fortran/arith.c
index b9c25c10e89..d51fbc26be4 100644
--- a/gcc/fortran/arith.c
+++ b/gcc/fortran/arith.c
@@ -1521,7 +1521,7 @@ eval_intrinsic (gfc_intrinsic_op op,
temp.value.op.op1 = op1;
temp.value.op.op2 = op2;
- gfc_type_convert_binary (&temp, 0);
+ gfc_type_convert_binary (&temp, warn_conversion || warn_conversion_extra);
if (op == INTRINSIC_EQ || op == INTRINSIC_NE
|| op == INTRINSIC_GE || op == INTRINSIC_GT
@@ -1949,6 +1949,42 @@ arith_error (arith rc, gfc_typespec *from, gfc_typespec *to, locus *where)
NaN, etc. */
}
+/* Returns true if significant bits were lost when converting real
+ constant r from from_kind to to_kind. */
+
+static bool
+wprecision_real_real (mpfr_t r, int from_kind, int to_kind)
+{
+ mpfr_t rv, diff;
+ bool ret;
+
+ gfc_set_model_kind (to_kind);
+ mpfr_init (rv);
+ gfc_set_model_kind (from_kind);
+ mpfr_init (diff);
+
+ mpfr_set (rv, r, GFC_RND_MODE);
+ mpfr_sub (diff, rv, r, GFC_RND_MODE);
+
+ ret = ! mpfr_zero_p (diff);
+ mpfr_clear (rv);
+ mpfr_clear (diff);
+ return ret;
+}
+
+/* Return true if conversion from an integer to a real loses precision. */
+
+static bool
+wprecision_int_real (mpz_t n, mpfr_t r)
+{
+ mpz_t i;
+ mpz_init (i);
+ mpfr_get_z (i, r, GFC_RND_MODE);
+ mpz_sub (i, i, n);
+ return mpz_cmp_si (i, 0) != 0;
+ mpz_clear (i);
+
+}
/* Convert integers to integers. */
@@ -1985,8 +2021,12 @@ gfc_int2int (gfc_expr *src, int kind)
k = gfc_validate_kind (BT_INTEGER, kind, false);
gfc_convert_mpz_to_signed (result->value.integer,
gfc_integer_kinds[k].bit_size);
- }
+ if (warn_conversion && kind < src->ts.kind)
+ gfc_warning_now (OPT_Wconversion, "Conversion from %qs to %qs at %L",
+ gfc_typename (&src->ts), gfc_typename (&result->ts),
+ &src->where);
+ }
return result;
}
@@ -2010,6 +2050,14 @@ gfc_int2real (gfc_expr *src, int kind)
return NULL;
}
+ if (warn_conversion
+ && wprecision_int_real (src->value.integer, result->value.real))
+ gfc_warning_now (OPT_Wconversion, "Change of value in conversion "
+ "from %qs to %qs at %L",
+ gfc_typename (&src->ts),
+ gfc_typename (&result->ts),
+ &src->where);
+
return result;
}
@@ -2034,6 +2082,15 @@ gfc_int2complex (gfc_expr *src, int kind)
return NULL;
}
+ if (warn_conversion
+ && wprecision_int_real (src->value.integer,
+ mpc_realref (result->value.complex)))
+ gfc_warning_now (OPT_Wconversion, "Change of value in conversion "
+ "from %qs to %qs at %L",
+ gfc_typename (&src->ts),
+ gfc_typename (&result->ts),
+ &src->where);
+
return result;
}
@@ -2045,6 +2102,7 @@ gfc_real2int (gfc_expr *src, int kind)
{
gfc_expr *result;
arith rc;
+ bool did_warn = false;
result = gfc_get_constant_expr (BT_INTEGER, kind, &src->where);
@@ -2057,6 +2115,28 @@ gfc_real2int (gfc_expr *src, int kind)
return NULL;
}
+ /* If there was a fractional part, warn about this. */
+
+ if (warn_conversion)
+ {
+ mpfr_t f;
+ mpfr_init (f);
+ mpfr_frac (f, src->value.real, GFC_RND_MODE);
+ if (mpfr_cmp_si (f, 0) != 0)
+ {
+ gfc_warning_now (OPT_Wconversion, "Change of value in conversion "
+ "from %qs to %qs at %L", gfc_typename (&src->ts),
+ gfc_typename (&result->ts), &src->where);
+ did_warn = true;
+ }
+ }
+ if (!did_warn && warn_conversion_extra)
+ {
+ gfc_warning_now (OPT_Wconversion_extra, "Conversion from %qs to %qs "
+ "at %L", gfc_typename (&src->ts),
+ gfc_typename (&result->ts), &src->where);
+ }
+
return result;
}
@@ -2068,6 +2148,7 @@ gfc_real2real (gfc_expr *src, int kind)
{
gfc_expr *result;
arith rc;
+ bool did_warn = false;
result = gfc_get_constant_expr (BT_REAL, kind, &src->where);
@@ -2088,6 +2169,33 @@ gfc_real2real (gfc_expr *src, int kind)
return NULL;
}
+ /* As a special bonus, don't warn about REAL values which are not changed by
+ the conversion if -Wconversion is specified and -Wconversion-extra is
+ not. */
+
+ if ((warn_conversion || warn_conversion_extra) && src->ts.kind > kind)
+ {
+ int w = warn_conversion ? OPT_Wconversion : OPT_Wconversion_extra;
+
+ /* Calculate the difference between the constant and the rounded
+ value and check it against zero. */
+
+ if (wprecision_real_real (src->value.real, src->ts.kind, kind))
+ {
+ gfc_warning_now (w, "Change of value in conversion from "
+ "%qs to %qs at %L",
+ gfc_typename (&src->ts), gfc_typename (&result->ts),
+ &src->where);
+ /* Make sure the conversion warning is not emitted again. */
+ did_warn = true;
+ }
+ }
+
+ if (!did_warn && warn_conversion_extra)
+ gfc_warning_now (OPT_Wconversion_extra, "Conversion from %qs to %qs "
+ "at %L", gfc_typename(&src->ts),
+ gfc_typename(&result->ts), &src->where);
+
return result;
}
@@ -2099,6 +2207,7 @@ gfc_real2complex (gfc_expr *src, int kind)
{
gfc_expr *result;
arith rc;
+ bool did_warn = false;
result = gfc_get_constant_expr (BT_COMPLEX, kind, &src->where);
@@ -2119,6 +2228,26 @@ gfc_real2complex (gfc_expr *src, int kind)
return NULL;
}
+ if ((warn_conversion || warn_conversion_extra) && src->ts.kind > kind)
+ {
+ int w = warn_conversion ? OPT_Wconversion : OPT_Wconversion_extra;
+
+ if (wprecision_real_real (src->value.real, src->ts.kind, kind))
+ {
+ gfc_warning_now (w, "Change of value in conversion from "
+ "%qs to %qs at %L",
+ gfc_typename (&src->ts), gfc_typename (&result->ts),
+ &src->where);
+ /* Make sure the conversion warning is not emitted again. */
+ did_warn = true;
+ }
+ }
+
+ if (!did_warn && warn_conversion_extra)
+ gfc_warning_now (OPT_Wconversion_extra, "Conversion from %qs to %qs "
+ "at %L", gfc_typename(&src->ts),
+ gfc_typename(&result->ts), &src->where);
+
return result;
}
@@ -2130,6 +2259,7 @@ gfc_complex2int (gfc_expr *src, int kind)
{
gfc_expr *result;
arith rc;
+ bool did_warn = false;
result = gfc_get_constant_expr (BT_INTEGER, kind, &src->where);
@@ -2143,6 +2273,43 @@ gfc_complex2int (gfc_expr *src, int kind)
return NULL;
}
+ if (warn_conversion || warn_conversion_extra)
+ {
+ int w = warn_conversion ? OPT_Wconversion : OPT_Wconversion_extra;
+
+ /* See if we discarded an imaginary part. */
+ if (mpfr_cmp_si (mpc_imagref (src->value.complex), 0) != 0)
+ {
+ gfc_warning_now (w, "Non-zero imaginary part discarded "
+ "in conversion from %qs to %qs at %L",
+ gfc_typename(&src->ts), gfc_typename (&result->ts),
+ &src->where);
+ did_warn = true;
+ }
+
+ else {
+ mpfr_t f;
+
+ mpfr_init (f);
+ mpfr_frac (f, src->value.real, GFC_RND_MODE);
+ if (mpfr_cmp_si (f, 0) != 0)
+ {
+ gfc_warning_now (w, "Change of value in conversion from "
+ "%qs to %qs at %L", gfc_typename (&src->ts),
+ gfc_typename (&result->ts), &src->where);
+ did_warn = true;
+ }
+ mpfr_clear (f);
+ }
+
+ if (!did_warn && warn_conversion_extra)
+ {
+ gfc_warning_now (OPT_Wconversion_extra, "Conversion from %qs to %qs "
+ "at %L", gfc_typename (&src->ts),
+ gfc_typename (&result->ts), &src->where);
+ }
+ }
+
return result;
}
@@ -2154,6 +2321,7 @@ gfc_complex2real (gfc_expr *src, int kind)
{
gfc_expr *result;
arith rc;
+ bool did_warn = false;
result = gfc_get_constant_expr (BT_REAL, kind, &src->where);
@@ -2174,6 +2342,41 @@ gfc_complex2real (gfc_expr *src, int kind)
return NULL;
}
+ if (warn_conversion || warn_conversion_extra)
+ {
+ int w = warn_conversion ? OPT_Wconversion : OPT_Wconversion_extra;
+
+ /* See if we discarded an imaginary part. */
+ if (mpfr_cmp_si (mpc_imagref (src->value.complex), 0) != 0)
+ {
+ gfc_warning_now (w, "Non-zero imaginary part discarded "
+ "in conversion from %qs to %qs at %L",
+ gfc_typename(&src->ts), gfc_typename (&result->ts),
+ &src->where);
+ did_warn = true;
+ }
+
+ /* Calculate the difference between the real constant and the rounded
+ value and check it against zero. */
+
+ if (kind > src->ts.kind
+ && wprecision_real_real (mpc_realref (src->value.complex),
+ src->ts.kind, kind))
+ {
+ gfc_warning_now (w, "Change of value in conversion from "
+ "%qs to %qs at %L",
+ gfc_typename (&src->ts), gfc_typename (&result->ts),
+ &src->where);
+ /* Make sure the conversion warning is not emitted again. */
+ did_warn = true;
+ }
+ }
+
+ if (!did_warn && warn_conversion_extra)
+ gfc_warning_now (OPT_Wconversion, "Conversion from %qs to %qs at %L",
+ gfc_typename(&src->ts), gfc_typename (&result->ts),
+ &src->where);
+
return result;
}
@@ -2185,6 +2388,7 @@ gfc_complex2complex (gfc_expr *src, int kind)
{
gfc_expr *result;
arith rc;
+ bool did_warn = false;
result = gfc_get_constant_expr (BT_COMPLEX, kind, &src->where);
@@ -2220,6 +2424,26 @@ gfc_complex2complex (gfc_expr *src, int kind)
return NULL;
}
+ if ((warn_conversion || warn_conversion_extra) && src->ts.kind > kind
+ && (wprecision_real_real (mpc_realref (src->value.complex),
+ src->ts.kind, kind)
+ || wprecision_real_real (mpc_imagref (src->value.complex),
+ src->ts.kind, kind)))
+ {
+ int w = warn_conversion ? OPT_Wconversion : OPT_Wconversion_extra;
+
+ gfc_warning_now (w, "Change of value in conversion from "
+ " %qs to %qs at %L",
+ gfc_typename (&src->ts), gfc_typename (&result->ts),
+ &src->where);
+ did_warn = true;
+ }
+
+ if (!did_warn && warn_conversion_extra && src->ts.kind != kind)
+ gfc_warning_now (OPT_Wconversion_extra, "Conversion from %qs to %qs "
+ "at %L", gfc_typename(&src->ts),
+ gfc_typename (&result->ts), &src->where);
+
return result;
}