Consider, the program

   program foo
     i = 10**23
     x = 6.02 * 10**23
     print *, i, x
   end

% gfc8 -o z a.f90 && ./z
  -159383552   6.01999981E+23

Now, if 10**23 = -159383552, then 6.02 * 10**23 should be -959488983.04.  
Moreover raising a positive number to a positive exponent should result
in a positive number.  Yes, I'm aware of twos-complement wrap around.
And, finally, the code compiles without so much as a warning.

The attached patch brings some consistency to gfortran.

% gfcx -c a.f90 
a.f90:1:9:

    1 |    i = 10**23
      |         1
Error: Result of exponentiation at (1) exceeds the range of INTEGER(4)
a.f90:2:16:

    2 |    x = 6.02 * 10**23
      |                1
Error: Result of exponentiation at (1) exceeds the range of INTEGER(4)

It has been regression tested on x86_64-*-freebsd.  OK to commit?

2019-03-01  Steven G. Kargl  <ka...@gcc.gnu.org>

        * arith.c (arith_power): Rework overflow of an integer to an integer
        exponent.

2019-03-01  Steven G. Kargl  <ka...@gcc.gnu.org>

        * gfortran.dg/integer_exponentiation_4.f90: Update test.
        * gfortran.dg/integer_exponentiation_5.F90: Ditto.
        * gfortran.dg/no_range_check_1.f90: Ditto.

-- 
Steve
Index: gcc/fortran/arith.c
===================================================================
--- gcc/fortran/arith.c	(revision 269260)
+++ gcc/fortran/arith.c	(working copy)
@@ -848,8 +848,6 @@ arith_power (gfc_expr *op1, gfc_expr *op2, gfc_expr **
 	    {
 	    case BT_INTEGER:
 	      {
-		int power;
-
 		/* First, we simplify the cases of op1 == 1, 0 or -1.  */
 		if (mpz_cmp_si (op1->value.integer, 1) == 0)
 		  {
@@ -884,29 +882,36 @@ arith_power (gfc_expr *op1, gfc_expr *op2, gfc_expr **
 				       "exponent of integer has zero "
 				       "result at %L", &result->where);
 		  }
-		else if (gfc_extract_int (op2, &power))
+		else
 		  {
-		    /* If op2 doesn't fit in an int, the exponentiation will
-		       overflow, because op2 > 0 and abs(op1) > 1.  */
-		    mpz_t max;
-		    int i;
-		    i = gfc_validate_kind (BT_INTEGER, result->ts.kind, false);
+		    /* We have abs(op1) > 1 and op2 > 1.
+		       If op2 > bit_size(op1), we'll have an out-of-range
+		       result.  */
+		    int k, power;
 
-		    if (flag_range_check)
-		      rc = ARITH_OVERFLOW;
-
-		    /* Still, we want to give the same value as the
-		       processor.  */
-		    mpz_init (max);
-		    mpz_add_ui (max, gfc_integer_kinds[i].huge, 1);
-		    mpz_mul_ui (max, max, 2);
-		    mpz_powm (result->value.integer, op1->value.integer,
-			      op2->value.integer, max);
-		    mpz_clear (max);
+		    k = gfc_validate_kind (BT_INTEGER, op1->ts.kind, false);
+		    power = gfc_integer_kinds[k].bit_size;
+		    if (mpz_cmp_si (op2->value.integer, power) < 0)
+		      {
+			gfc_extract_int (op2, &power);
+			mpz_pow_ui (result->value.integer, op1->value.integer,
+				    power);
+			rc = gfc_range_check (result);
+			if (rc == ARITH_OVERFLOW)
+			  gfc_error_now ("Result of exponentiation at %L "
+					 "exceeds the range of %s", &op1->where,
+					 gfc_typename (&(op1->ts)));
+		      }
+		    else
+		      {
+			/* Provide a nonsense value to propagate up. */
+			mpz_set (result->value.integer,
+				 gfc_integer_kinds[k].huge);
+			mpz_add_ui (result->value.integer,
+				    result->value.integer, 1);
+			rc = ARITH_OVERFLOW;
+		      }
 		  }
-		else
-		  mpz_pow_ui (result->value.integer, op1->value.integer,
-			      power);
 	      }
 	      break;
 
Index: gcc/testsuite/gfortran.dg/integer_exponentiation_4.f90
===================================================================
--- gcc/testsuite/gfortran.dg/integer_exponentiation_4.f90	(revision 269260)
+++ gcc/testsuite/gfortran.dg/integer_exponentiation_4.f90	(working copy)
@@ -21,10 +21,10 @@ program test
   print *, (-1)**huge(0_8)
   print *, (-1)**(-huge(0_8)-1_8)
 
-  print *, 2**huge(0) ! { dg-error "Arithmetic overflow" }
-  print *, 2**huge(0_8) ! { dg-error "Arithmetic overflow" }
-  print *, (-2)**huge(0) ! { dg-error "Arithmetic overflow" }
-  print *, (-2)**huge(0_8) ! { dg-error "Arithmetic overflow" }
+  print *, 2**huge(0) ! { dg-error "Arithmetic overflow|exceeds the range" }
+  print *, 2**huge(0_8) ! { dg-error "Arithmetic overflow|exceeds the range" }
+  print *, (-2)**huge(0) ! { dg-error "Arithmetic overflow|exceeds the range" }
+  print *, (-2)**huge(0_8) ! { dg-error "Arithmetic overflow|exceeds the range" }
 
   print *, 2**(-huge(0)-1)
   print *, 2**(-huge(0_8)-1_8)
Index: gcc/testsuite/gfortran.dg/integer_exponentiation_5.F90
===================================================================
--- gcc/testsuite/gfortran.dg/integer_exponentiation_5.F90	(revision 269260)
+++ gcc/testsuite/gfortran.dg/integer_exponentiation_5.F90	(working copy)
@@ -67,8 +67,6 @@ program test
   TEST(3_8,43_8,i8)
   TEST(-3_8,43_8,i8)
 
-  TEST(17_8,int(huge(0_4),kind=8)+1,i8)
-
 !!!!! REAL BASE !!!!!
   TEST(0.0,-1,r4)
   TEST(0.0,-huge(0)-1,r4)
Index: gcc/testsuite/gfortran.dg/no_range_check_1.f90
===================================================================
--- gcc/testsuite/gfortran.dg/no_range_check_1.f90	(revision 269260)
+++ gcc/testsuite/gfortran.dg/no_range_check_1.f90	(working copy)
@@ -4,11 +4,8 @@
 ! This testcase arose from PR 31262
   integer :: a
   integer(kind=8) :: b
-  a = -3
   b = -huge(b) / 7
-  a = a ** 73
   b = 7894_8 * b - 78941_8
-  if ((-3)**73 /= a) STOP 1
   if (7894_8 * (-huge(b) / 7) - 78941_8 /= b) STOP 2
 
   a = 1234789786453123

Reply via email to