https://gcc.gnu.org/g:b4e4cb4254048475e13d599c88da283ba1aa83c8

commit b4e4cb4254048475e13d599c88da283ba1aa83c8
Author: Thomas Koenig <tkoe...@gcc.gnu.org>
Date:   Sat Aug 3 11:20:49 2024 +0200

    Iplement conversions from unsigned to different data types.

Diff:
---
 gcc/fortran/arith.cc                     | 110 ++++++++++++++++++++++++++-
 gcc/fortran/arith.h                      |   5 ++
 gcc/fortran/intrinsic.cc                 |  18 ++++-
 gcc/fortran/resolve.cc                   |   7 ++
 gcc/fortran/simplify.cc                  |  25 +++++++
 gcc/testsuite/gfortran.dg/unsigned_5.f90 | 123 +++++++++++++++++++++++++++++++
 libgfortran/io/read.c                    |   2 +-
 7 files changed, 286 insertions(+), 4 deletions(-)

diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
index 1b304b114dd8..73e0610689e6 100644
--- a/gcc/fortran/arith.cc
+++ b/gcc/fortran/arith.cc
@@ -2258,7 +2258,8 @@ wprecision_int_real (mpz_t n, mpfr_t r)
   return ret;
 }
 
-/* Convert integers to integers.  */
+/* Convert integers to integers; we can reuse this for also converting
+   unsigneds.  */
 
 gfc_expr *
 gfc_int2int (gfc_expr *src, int kind)
@@ -2266,7 +2267,7 @@ gfc_int2int (gfc_expr *src, int kind)
   gfc_expr *result;
   arith rc;
 
-  if (src->ts.type != BT_INTEGER)
+  if (src->ts.type != BT_INTEGER && src->ts.type != BT_UNSIGNED)
     return NULL;
 
   result = gfc_get_constant_expr (BT_INTEGER, kind, &src->where);
@@ -2375,6 +2376,111 @@ gfc_int2complex (gfc_expr *src, int kind)
   return result;
 }
 
+/* Convert unsigned to unsigned, or integer to unsigned.  */
+
+gfc_expr *
+gfc_uint2uint (gfc_expr *src, int kind)
+{
+  gfc_expr *result;
+  arith rc;
+  int k;
+
+  if (src->ts.type != BT_UNSIGNED && src->ts.type != BT_INTEGER)
+    return NULL;
+
+  result = gfc_get_constant_expr (BT_UNSIGNED, kind, &src->where);
+  mpz_set (result->value.integer, src->value.integer);
+
+  rc = gfc_range_check (result);
+  if (rc != ARITH_OK)
+    gfc_warning (0, gfc_arith_error (rc), &result->where);
+
+  k = gfc_validate_kind (BT_UNSIGNED, kind, false);
+  mpz_and (result->value.integer, result->value.integer,
+          gfc_unsigned_kinds[k].huge);
+
+  return result;
+}
+
+gfc_expr *
+gfc_int2uint (gfc_expr *src, int kind)
+{
+  return gfc_uint2uint (src, kind);
+}
+
+gfc_expr *
+gfc_uint2int (gfc_expr *src, int kind)
+{
+  return gfc_int2int (src, kind);
+}
+
+/* Convert UNSIGNED to reals.  */
+
+gfc_expr *
+gfc_uint2real (gfc_expr *src, int kind)
+{
+  gfc_expr *result;
+  arith rc;
+
+  if (src->ts.type != BT_UNSIGNED)
+    return NULL;
+
+  result = gfc_get_constant_expr (BT_REAL, kind, &src->where);
+
+  mpfr_set_z (result->value.real, src->value.integer, GFC_RND_MODE);
+
+  if ((rc = gfc_check_real_range (result->value.real, kind)) != ARITH_OK)
+    {
+      arith_error (rc, &src->ts, &result->ts, &src->where);
+      gfc_free_expr (result);
+      return NULL;
+    }
+
+  if (warn_conversion
+      && wprecision_int_real (src->value.integer, result->value.real))
+    gfc_warning (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;
+}
+
+/* Convert default integer to default complex.  */
+
+gfc_expr *
+gfc_uint2complex (gfc_expr *src, int kind)
+{
+  gfc_expr *result;
+  arith rc;
+
+  if (src->ts.type != BT_UNSIGNED)
+    return NULL;
+
+  result = gfc_get_constant_expr (BT_COMPLEX, kind, &src->where);
+
+  mpc_set_z (result->value.complex, src->value.integer, GFC_MPC_RND_MODE);
+
+  if ((rc = gfc_check_real_range (mpc_realref (result->value.complex), kind))
+      != ARITH_OK)
+    {
+      arith_error (rc, &src->ts, &result->ts, &src->where);
+      gfc_free_expr (result);
+      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;
+}
 
 /* Convert default real to default integer.  */
 
diff --git a/gcc/fortran/arith.h b/gcc/fortran/arith.h
index f2e63bca2154..e796d4dac293 100644
--- a/gcc/fortran/arith.h
+++ b/gcc/fortran/arith.h
@@ -63,6 +63,11 @@ gfc_expr *gfc_le (gfc_expr *, gfc_expr *, gfc_intrinsic_op);
 gfc_expr *gfc_int2int (gfc_expr *, int);
 gfc_expr *gfc_int2real (gfc_expr *, int);
 gfc_expr *gfc_int2complex (gfc_expr *, int);
+gfc_expr *gfc_int2uint (gfc_expr *, int);
+gfc_expr *gfc_uint2uint (gfc_expr *, int);
+gfc_expr *gfc_uint2int (gfc_expr *, int);
+gfc_expr *gfc_uint2real (gfc_expr *, int);
+gfc_expr *gfc_uint2complex (gfc_expr *, int);
 gfc_expr *gfc_real2int (gfc_expr *, int);
 gfc_expr *gfc_real2real (gfc_expr *, int);
 gfc_expr *gfc_real2complex (gfc_expr *, int);
diff --git a/gcc/fortran/intrinsic.cc b/gcc/fortran/intrinsic.cc
index 9074fe3c186b..65ca14af6765 100644
--- a/gcc/fortran/intrinsic.cc
+++ b/gcc/fortran/intrinsic.cc
@@ -95,6 +95,12 @@ gfc_type_letter (bt type, bool logical_equals_int)
       c = 'h';
       break;
 
+      /* 'u' would be the logical choice, but it is used for
+        "unknown", see below.  */
+    case BT_UNSIGNED:
+      c = 'm';
+      break;
+
     default:
       c = 'u';
       break;
@@ -4053,6 +4059,15 @@ add_conversions (void)
                  BT_INTEGER, gfc_integer_kinds[i].kind, GFC_STD_F77);
       }
 
+  if (flag_unsigned)
+    {
+      for (i = 0; gfc_unsigned_kinds[i].kind != 0; i++)
+       for (j = 0; gfc_unsigned_kinds[j].kind != 0; j++)
+         if (i != j)
+           add_conv (BT_UNSIGNED, gfc_unsigned_kinds[i].kind,
+                     BT_UNSIGNED, gfc_unsigned_kinds[j].kind, GFC_STD_GNU);
+    }
+
   if ((gfc_option.allow_std & GFC_STD_LEGACY) != 0)
     {
       /* Hollerith-Integer conversions.  */
@@ -5326,7 +5341,8 @@ gfc_convert_type_warn (gfc_expr *expr, gfc_typespec *ts, 
int eflag, int wflag,
       else if (from_ts.type == ts->type
               || (from_ts.type == BT_INTEGER && ts->type == BT_REAL)
               || (from_ts.type == BT_INTEGER && ts->type == BT_COMPLEX)
-              || (from_ts.type == BT_REAL && ts->type == BT_COMPLEX))
+              || (from_ts.type == BT_REAL && ts->type == BT_COMPLEX)
+              || (from_ts.type == BT_UNSIGNED && ts->type == BT_UNSIGNED))
        {
          /* Larger kinds can hold values of smaller kinds without problems.
             Hence, only warn if target kind is smaller than the source
diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc
index 98ad7aef42b4..8f70acf84ca0 100644
--- a/gcc/fortran/resolve.cc
+++ b/gcc/fortran/resolve.cc
@@ -11549,6 +11549,13 @@ resolve_ordinary_assign (gfc_code *code, gfc_namespace 
*ns)
       return false;
     }
 
+  if (flag_unsigned && gfc_invalid_unsigned_ops (lhs, rhs))
+    {
+      gfc_error (_("Cannot assign %s to %s at %L"), gfc_typename (rhs),
+                  gfc_typename (lhs), &rhs->where);
+      return false;
+    }
+
   /* Handle the case of a BOZ literal on the RHS.  */
   if (rhs->ts.type == BT_BOZ)
     {
diff --git a/gcc/fortran/simplify.cc b/gcc/fortran/simplify.cc
index 919d9d560e11..8d622702b4ad 100644
--- a/gcc/fortran/simplify.cc
+++ b/gcc/fortran/simplify.cc
@@ -1798,6 +1798,7 @@ simplify_cmplx (const char *name, gfc_expr *x, gfc_expr 
*y, int kind)
   switch (x->ts.type)
     {
       case BT_INTEGER:
+      case BT_UNSIGNED:
        mpc_set_z (result->value.complex, x->value.integer, GFC_MPC_RND_MODE);
        break;
 
@@ -1819,6 +1820,7 @@ simplify_cmplx (const char *name, gfc_expr *x, gfc_expr 
*y, int kind)
   switch (y->ts.type)
     {
       case BT_INTEGER:
+      case BT_UNSIGNED:
        mpfr_set_z (mpc_imagref (result->value.complex),
                    y->value.integer, GFC_RND_MODE);
        break;
@@ -8816,6 +8818,9 @@ gfc_convert_constant (gfc_expr *e, bt type, int kind)
        case BT_INTEGER:
          f = gfc_int2int;
          break;
+       case BT_UNSIGNED:
+         f = gfc_int2uint;
+         break;
        case BT_REAL:
          f = gfc_int2real;
          break;
@@ -8830,6 +8835,26 @@ gfc_convert_constant (gfc_expr *e, bt type, int kind)
        }
       break;
 
+    case BT_UNSIGNED:
+      switch (type)
+       {
+       case BT_INTEGER:
+         f = gfc_uint2int;
+         break;
+       case BT_UNSIGNED:
+         f = gfc_uint2uint;
+         break;
+       case BT_REAL:
+         f = gfc_uint2real;
+         break;
+       case BT_COMPLEX:
+         f = gfc_uint2complex;
+         break;
+       default:
+         goto oops;
+       }
+      break;
+
     case BT_REAL:
       switch (type)
        {
diff --git a/gcc/testsuite/gfortran.dg/unsigned_5.f90 
b/gcc/testsuite/gfortran.dg/unsigned_5.f90
new file mode 100644
index 000000000000..5fbd1b4150b9
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/unsigned_5.f90
@@ -0,0 +1,123 @@
+! { dg-do run }
+! { dg-options "-funsigned" }
+! Test conversions from unsigned to different data types by
+! doing some I/O.
+program main
+  implicit none
+  integer :: vi,i
+  integer, parameter :: n_int = 16, n_real = 8
+  unsigned(kind=1) :: u1
+  unsigned(kind=2) :: u2
+  unsigned(kind=4) :: u4
+  unsigned(kind=8) :: u8
+  unsigned :: u
+  integer, dimension(n_int) :: ires
+  real(kind=8), dimension(n_real) :: rres
+  real(kind=8) :: vr
+  complex (kind=8) :: vc
+  data ires /11,12,14,18,21,22,24,28,41,42,44,48,81,82,84,88/
+  data rres /14., 18., 24., 28., 44., 48., 84., 88./
+  open (10,status="scratch")
+
+  write (10,*) int(11u_1,1)
+  write (10,*) int(12u_1,2)
+  write (10,*) int(14u_1,4)
+  write (10,*) int(18u_1,8)
+
+  write (10,*) int(21u_2,1)
+  write (10,*) int(22u_2,2)
+  write (10,*) int(24u_2,4)
+  write (10,*) int(28u_2,8)
+
+  write (10,*) int(41u_4,1)
+  write (10,*) int(42u_4,2)
+  write (10,*) int(44u_4,4)
+  write (10,*) int(48u_4,8)
+
+  write (10,*) int(81u_8,1)
+  write (10,*) int(82u_8,2)
+  write (10,*) int(84u_8,4)
+  write (10,*) int(88u_8,8)
+
+  rewind 10
+  do i=1,n_int
+     read (10,*) vi
+     if (vi /= ires(i)) stop 1
+  end do
+
+  rewind 10
+  u1 = 11u; write (10,*) int(u1,1)
+  u1 = 12u; write (10,*) int(u1,2)
+  u1 = 14u; write (10,*) int(u1,4)
+  u1 = 18u; write (10,*) int(u1,8)
+
+  u2 = 21u; write (10,*) int(u2,1)
+  u2 = 22u; write (10,*) int(u2,2)
+  u2 = 24u; write (10,*) int(u2,4)
+  u2 = 28u; write (10,*) int(u2,8)
+
+  u4 = 41u; write (10,*) int(u4,1)
+  u4 = 42u; write (10,*) int(u4,2)
+  u4 = 44u; write (10,*) int(u4,4)
+  u4 = 48u; write (10,*) int(u4,8)
+
+  u8 = 81u; write (10,*) int(u8,1)
+  u8 = 82u; write (10,*) int(u8,2)
+  u8 = 84u; write (10,*) int(u8,4)
+  u8 = 88u; write (10,*) int(u8,8)
+
+  rewind 10
+  do i=1,n_int
+     read (10,*) vi
+     if (vi /= ires(i)) stop 2
+  end do
+
+  rewind 10
+  write (10,*) real(14u_1,4)
+  write (10,*) real(18u_1,8)
+  write (10,*) real(24u_2,4)
+  write (10,*) real(28u_2,8)
+  write (10,*) real(44u_4,4)
+  write (10,*) real(48u_4,8)
+  write (10,*) real(84u_8,4)
+  write (10,*) real(88u_8,8)
+
+  rewind 10
+  do i=1, n_real
+     read (10, *) vr
+     if (vr /= rres(i)) stop 3
+  end do
+
+  rewind 10
+  u1 = 14u_1; write (10,*) real(u1,4)
+  u1 = 18u_1; write (10,*) real(u1,8)
+  u2 = 24u_2; write (10,*) real(u2,4)
+  u2 = 28u_2; write (10,*) real(u2,8)
+  u4 = 44u_4; write (10,*) real(u4,4)
+  u4 = 48u_4; write (10,*) real(u4,8)
+  u8 = 84u_4; write (10,*) real(u8,4)
+  u8 = 88u_4; write (10,*) real(u8,8)
+
+  rewind 10
+  do i=1, n_real
+     read (10, *) vr
+     if (vr /= rres(i)) stop 4
+  end do
+
+  rewind 10
+  u1 = 14u_1; write (10,*) cmplx(14u_1,u1,kind=4)
+  u1 = 18u_1; write (10,*) cmplx(18u_1,u1,kind=8)
+  u2 = 24u_2; write (10,*) cmplx(24u_2,u2,kind=4)
+  u2 = 28u_2; write (10,*) cmplx(28u_2,u2,kind=8)
+  u4 = 44u_4; write (10,*) cmplx(44u_4,u4,kind=4)
+  u4 = 48u_8; write (10,*) cmplx(48u_4,u4,kind=8)
+  u8 = 84u_8; write (10,*) cmplx(84u_8,u8,kind=4)
+  u8 = 88u_8; write (10,*) cmplx(88u_8,u8,kind=8)
+
+  rewind 10
+  do i=1,n_real
+     read (10, *) vc
+     if (real(vc) /= rres(i)) stop 5
+     if (aimag(vc) /= rres(i)) stop 6
+  end do
+end program main
diff --git a/libgfortran/io/read.c b/libgfortran/io/read.c
index 60b497a810d9..eadaea351404 100644
--- a/libgfortran/io/read.c
+++ b/libgfortran/io/read.c
@@ -863,7 +863,7 @@ void
 read_decimal_unsigned (st_parameter_dt *dtp, const fnode *f, char *dest,
                       int length)
 {
-  GFC_UINTEGER_LARGEST value, v;
+  GFC_UINTEGER_LARGEST value;
   size_t w;
   int negative;
   char c, *p;

Reply via email to