https://gcc.gnu.org/g:74cc1893acada349114d17f65909c0f0c293061c

commit 74cc1893acada349114d17f65909c0f0c293061c
Author: Thomas Koenig <tkoe...@gcc.gnu.org>
Date:   Thu Aug 1 20:53:59 2024 +0200

    A few fixes, add unsigned truncation warning.

Diff:
---
 gcc/fortran/arith.cc                     | 62 +++++++++++++++++++++++++++++---
 gcc/fortran/gfortran.h                   |  9 +++--
 gcc/fortran/primary.cc                   |  7 ++++
 gcc/fortran/simplify.cc                  |  6 ++--
 gcc/fortran/trans-types.cc               | 50 ++++++++++++++++++--------
 gcc/testsuite/gfortran.dg/unsigned_3.f90 | 10 ++++++
 6 files changed, 119 insertions(+), 25 deletions(-)

diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
index 849fa784241d..1b304b114dd8 100644
--- a/gcc/fortran/arith.cc
+++ b/gcc/fortran/arith.cc
@@ -86,7 +86,7 @@ gfc_set_model (mpfr_t x)
 /* Given an arithmetic error code, return a pointer to a string that
    explains the error.  */
 
-static const char *
+const char *
 gfc_arith_error (arith code)
 {
   const char *p;
@@ -121,7 +121,12 @@ gfc_arith_error (arith code)
     case ARITH_INVALID_TYPE:
       p = G_("Invalid type in arithmetic operation at %L");
       break;
-
+    case ARITH_UNSIGNED_TRUNCATED:
+      p = G_("Unsigned constant truncated at %L");
+      break;
+    case ARITH_UNSIGNED_NEGATIVE:
+      p = G_("Truncated negative unsigned constant at %L");
+      break;
     default:
       gfc_internal_error ("gfc_arith_error(): Bad error code");
     }
@@ -208,13 +213,21 @@ gfc_arith_init_1 (void)
     {
       for (uint_info = gfc_unsigned_kinds; uint_info->kind != 0; uint_info++)
        {
+         /* UNSIGNED is radix 2.  */
+         gcc_assert (uint_info->radix == 2);
          /* Huge.  */
          mpz_init (uint_info->huge);
-         mpz_set_ui (uint_info->huge, uint_info->radix);
+         mpz_set_ui (uint_info->huge, 2);
          mpz_pow_ui (uint_info->huge, uint_info->huge, uint_info->digits);
+         mpz_sub_ui (uint_info->huge, uint_info->huge, 1);
 
-         /* UNSIGNED is radix 2.  */
-         gcc_assert (uint_info->radix == 2);
+         /* int_min - the smallest number we can reasonably convert from.  */
+
+         mpz_init (uint_info->int_min);
+         mpz_set_ui (uint_info->int_min, 2);
+         mpz_pow_ui (uint_info->int_min, uint_info->int_min,
+                     uint_info->digits - 1);
+         mpz_neg (uint_info->int_min, uint_info->int_min);
 
          /* Range.  */
          mpfr_set_z (a, uint_info->huge, GFC_RND_MODE);
@@ -367,6 +380,24 @@ gfc_check_integer_range (mpz_t p, int kind)
   return result;
 }
 
+/* Same as above.  */
+arith
+gfc_check_unsigned_range (mpz_t p, int kind)
+{
+  arith result;
+  int i;
+
+  i = gfc_validate_kind (BT_UNSIGNED, kind, false);
+  result = ARITH_OK;
+
+  if (mpz_cmp (p, gfc_unsigned_kinds[i].int_min) < 0)
+    result = ARITH_UNSIGNED_TRUNCATED;
+
+  if (mpz_cmp (p, gfc_unsigned_kinds[i].huge) > 0)
+    result = ARITH_UNSIGNED_TRUNCATED;
+
+  return result;
+}
 
 /* Given a real and a kind, make sure that the real lies within the
    range of the kind.  Returns ARITH_OK, ARITH_OVERFLOW or
@@ -564,6 +595,10 @@ gfc_range_check (gfc_expr *e)
       rc = gfc_check_integer_range (e->value.integer, e->ts.kind);
       break;
 
+    case BT_UNSIGNED:
+      rc = gfc_check_unsigned_range (e->value.integer, e->ts.kind);
+      break;
+
     case BT_REAL:
       rc = gfc_check_real_range (e->value.real, e->ts.kind);
       if (rc == ARITH_UNDERFLOW)
@@ -653,6 +688,7 @@ gfc_arith_uminus (gfc_expr *op1, gfc_expr **resultp)
 {
   gfc_expr *result;
   arith rc;
+  int k;
 
   result = gfc_get_constant_expr (op1->ts.type, op1->ts.kind, &op1->where);
 
@@ -662,6 +698,22 @@ gfc_arith_uminus (gfc_expr *op1, gfc_expr **resultp)
       mpz_neg (result->value.integer, op1->value.integer);
       break;
 
+    case BT_UNSIGNED:
+      {
+       arith neg_rc;
+       mpz_neg (result->value.integer, op1->value.integer);
+       k = gfc_validate_kind (BT_UNSIGNED, op1->ts.kind, false);
+       neg_rc = gfc_range_check (result);
+       if (neg_rc != ARITH_OK)
+         gfc_warning (0, gfc_arith_error (neg_rc), &result->where);
+
+       mpz_and (result->value.integer, result->value.integer,
+                gfc_unsigned_kinds[k].huge);
+       if (pedantic)
+         rc = neg_rc;
+      }
+      break;
+
     case BT_REAL:
       mpfr_neg (result->value.real, op1->value.real, GFC_RND_MODE);
       break;
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 1e3262f97bf2..b241e2834b9b 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -227,7 +227,8 @@ enum gfc_intrinsic_op
 enum arith
 { ARITH_OK = 1, ARITH_OVERFLOW, ARITH_UNDERFLOW, ARITH_NAN,
   ARITH_DIV0, ARITH_INCOMMENSURATE, ARITH_ASYMMETRIC, ARITH_PROHIBIT,
-  ARITH_WRONGCONCAT, ARITH_INVALID_TYPE, ARITH_NOT_REDUCED
+  ARITH_WRONGCONCAT, ARITH_INVALID_TYPE, ARITH_NOT_REDUCED,
+  ARITH_UNSIGNED_TRUNCATED, ARITH_UNSIGNED_NEGATIVE
 };
 
 /* Statements.  */
@@ -2744,7 +2745,7 @@ extern gfc_integer_info gfc_integer_kinds[];
 
 typedef struct
 {
-  mpz_t huge;
+  mpz_t huge, int_min;
 
   int kind, radix, digits, bit_size, range;
 
@@ -3470,7 +3471,9 @@ void gfc_errors_to_warnings (bool);
 void gfc_arith_init_1 (void);
 void gfc_arith_done_1 (void);
 arith gfc_check_integer_range (mpz_t p, int kind);
+arith gfc_check_unsigned_range (mpz_t p, int kind);
 bool gfc_check_character_range (gfc_char_t, int);
+const char *gfc_arith_error (arith);
 
 extern bool gfc_seen_div0;
 
@@ -4112,7 +4115,7 @@ void gfc_convert_mpz_to_signed (mpz_t, int);
 gfc_expr *gfc_simplify_ieee_functions (gfc_expr *);
 bool gfc_is_constant_array_expr (gfc_expr *);
 bool gfc_is_size_zero_array (gfc_expr *);
-void gfc_convert_mpz_to_unsigned (mpz_t, int, bool check = true);
+void gfc_convert_mpz_to_unsigned (mpz_t, int, bool sign = true);
 
 /* trans-array.cc  */
 
diff --git a/gcc/fortran/primary.cc b/gcc/fortran/primary.cc
index 63b0bcf6784d..24bfdb55e41d 100644
--- a/gcc/fortran/primary.cc
+++ b/gcc/fortran/primary.cc
@@ -218,6 +218,7 @@ convert_unsigned (const char *buffer, int kind, int radix, 
locus *where)
   gfc_expr *e;
   const char *t;
   int k;
+  arith rc;
 
   e = gfc_get_constant_expr (BT_UNSIGNED, kind, where);
   /* A leading plus is allowed, but not by mpz_set_str.  */
@@ -229,6 +230,12 @@ convert_unsigned (const char *buffer, int kind, int radix, 
locus *where)
   mpz_set_str (e->value.integer, t, radix);
 
   k = gfc_validate_kind (BT_UNSIGNED, kind, false);
+
+  /* XXX Maybe move this somewhere else.  */
+  rc = gfc_range_check (e);
+  if (rc != ARITH_OK)
+    gfc_warning (0, gfc_arith_error (rc), &e->where);
+
   gfc_convert_mpz_to_unsigned (e->value.integer, 
gfc_unsigned_kinds[k].bit_size,
                               false);
 
diff --git a/gcc/fortran/simplify.cc b/gcc/fortran/simplify.cc
index a8c9397edf97..919d9d560e11 100644
--- a/gcc/fortran/simplify.cc
+++ b/gcc/fortran/simplify.cc
@@ -148,7 +148,7 @@ get_kind (bt type, gfc_expr *k, const char *name, int 
default_kind)
    be accomplished by masking out the high bits.  */
 
 void
-gfc_convert_mpz_to_unsigned (mpz_t x, int bitsize, bool check)
+gfc_convert_mpz_to_unsigned (mpz_t x, int bitsize, bool sign)
 {
   mpz_t mask;
 
@@ -156,7 +156,7 @@ gfc_convert_mpz_to_unsigned (mpz_t x, int bitsize, bool 
check)
     {
       /* Confirm that no bits above the signed range are unset if we
         are doing range checking.  */
-      if (flag_range_check != 0)
+      if (sign && flag_range_check != 0)
        gcc_assert (mpz_scan0 (x, bitsize-1) == ULONG_MAX);
 
       mpz_init_set_ui (mask, 1);
@@ -171,7 +171,7 @@ gfc_convert_mpz_to_unsigned (mpz_t x, int bitsize, bool 
check)
     {
       /* Confirm that no bits above the signed range are set if we
         are doing range checking.  */
-      if (check && flag_range_check != 0)
+      if (sign && flag_range_check != 0)
        gcc_assert (mpz_scan1 (x, bitsize-1) == ULONG_MAX);
     }
 }
diff --git a/gcc/fortran/trans-types.cc b/gcc/fortran/trans-types.cc
index a00dc80bf596..552e46dcc8ca 100644
--- a/gcc/fortran/trans-types.cc
+++ b/gcc/fortran/trans-types.cc
@@ -1038,18 +1038,6 @@ gfc_init_types (void)
       PUSH_TYPE (name_buf, type);
     }
 
-  if (flag_unsigned)
-    {
-      for (index = 0; gfc_unsigned_kinds[index].kind != 0;++index)
-       {
-         type = gfc_build_unsigned_type (&gfc_unsigned_kinds[index]);
-         gfc_unsigned_types[index] = type;
-         snprintf (name_buf, sizeof(name_buf), "unsigned(kind=%d",
-                   gfc_integer_kinds[index].kind);
-         PUSH_TYPE (name_buf, type);
-       }
-    }
-
   for (index = 0; gfc_logical_kinds[index].kind != 0; ++index)
     {
       type = gfc_build_logical_type (&gfc_logical_kinds[index]);
@@ -1092,6 +1080,40 @@ gfc_init_types (void)
     }
   gfc_character1_type_node = gfc_character_types[0];
 
+  /* The middle end only recognizes a single unsigned type.  For
+     compatibility of existing test cases, let's just use the
+     character type.  The reader of tree dumps is expected to be able
+     to deal with this.  */
+
+  if (flag_unsigned)
+    {
+      for (index = 0; gfc_unsigned_kinds[index].kind != 0;++index)
+       {
+         int index_char = -1;
+         for (int i=0; gfc_character_kinds[i].kind != 0; i++)
+           {
+             if (gfc_character_kinds[i].bit_size ==
+                 gfc_unsigned_kinds[index].bit_size)
+               {
+                 index_char = i;
+                 break;
+               }
+           }
+         if (index_char > 0)
+           {
+             gfc_unsigned_types[index] = gfc_character_types[index_char];
+           }
+         else
+           {
+             type = gfc_build_unsigned_type (&gfc_unsigned_kinds[index]);
+             gfc_unsigned_types[index] = type;
+             snprintf (name_buf, sizeof(name_buf), "unsigned(kind=%d)",
+                       gfc_integer_kinds[index].kind);
+             PUSH_TYPE (name_buf, type);
+           }
+       }
+    }
+
   PUSH_TYPE ("byte", unsigned_char_type_node);
   PUSH_TYPE ("void", void_type_node);
 
@@ -1153,8 +1175,8 @@ gfc_get_int_type (int kind)
 tree
 gfc_get_unsigned_type (int kind)
 {
-  int index = gfc_validate_kind (BT_INTEGER, kind, true);
-  return index < 0 ? 0 : gfc_integer_types[index];
+  int index = gfc_validate_kind (BT_UNSIGNED, kind, true);
+  return index < 0 ? 0 : gfc_unsigned_types[index];
 }
 
 tree
diff --git a/gcc/testsuite/gfortran.dg/unsigned_3.f90 
b/gcc/testsuite/gfortran.dg/unsigned_3.f90
new file mode 100644
index 000000000000..7d5b4d67cfd3
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/unsigned_3.f90
@@ -0,0 +1,10 @@
+! { dg-do compile }
+! { dg-options "-funsigned" }
+! Test that overflow warned about.
+program main
+  unsigned(1) :: u
+  u = 256u_1 ! { dg-warning "Unsigned constant truncated" }
+  u = -127u_1
+  u = 255u_1
+  u = -129u_1 ! { dg-warning "Unsigned constant truncated" }
+end

Reply via email to