Fortran: error recovery for invalid types in array constructors [PR107000]

Message ID trinity-79a6df2f-08a1-4f6d-9431-70f884d1c05c-1664918395982@3c-app-gmx-bs23
State New, archived
Headers
Series Fortran: error recovery for invalid types in array constructors [PR107000] |

Commit Message

Harald Anlauf Oct. 4, 2022, 9:19 p.m. UTC
  Dear all,

we did not recover well from bad expressions in array constructors,
especially when there was a typespec and a unary '+' or '-', and
when the array constructor was used in an arithmetic expression.

The attached patch introduces an ARITH_INVALID_TYPE that is used
when we try to recover from these errors, and tries to handle
all unary and binary arithmetic expressions.

Regtested on x86_64-pc-linux-gnu.  OK for mainline?

Thanks,
Harald
  

Comments

Mikael Morin Oct. 5, 2022, 8:51 a.m. UTC | #1
Hello

Le 04/10/2022 à 23:19, Harald Anlauf via Fortran a écrit :
> Dear all,
> 
> we did not recover well from bad expressions in array constructors,
> especially when there was a typespec and a unary '+' or '-', and
> when the array constructor was used in an arithmetic expression.
> 
> The attached patch introduces an ARITH_INVALID_TYPE that is used
> when we try to recover from these errors, and tries to handle
> all unary and binary arithmetic expressions.
> 

In the PR, you noted an inconsistency in the error message reported, 
depending on the presence or lack of an operator.
I'm not sure you saw the suggestion to do the following in the last 
message I posted:

diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
index e6e35ef3c42..ed93ddb2882 100644
--- a/gcc/fortran/arith.cc
+++ b/gcc/fortran/arith.cc
@@ -1654,6 +1654,8 @@ eval_intrinsic (gfc_intrinsic_op op,
    else
      rc = reduce_binary (eval.f3, op1, op2, &result);

+  if (rc == ARITH_INVALID_TYPE)
+    goto runtime;

    /* Something went wrong.  */
    if (op == INTRINSIC_POWER && rc == ARITH_PROHIBIT)


In the testcase, it improves the situation slightly.
For example, from:
>     9 |   x = (1.0, 2.0) * [complex :: +'1'] ! { dg-error "Invalid type" }
>       |                1
> Error: Invalid type in arithmetic operation at (1)
to:
>     9 |   x = (1.0, 2.0) * [complex :: +'1'] ! { dg-error "Invalid type" }
>       |                              1
> Error: Operand of unary numeric operator ‘+’ at (1) is UNKNOWN


or from:
>    29 |   print *, 2 * [real :: 0, 1+'1']  ! { dg-error "Invalid type" }
>       |            1
> Error: Invalid type in arithmetic operation at (1)
to:
>    29 |   print *, 2 * [real :: 0, 1+'1']  ! { dg-error "Invalid type" }
>       |                          1
> Error: Operands of binary numeric operator ‘+’ at (1) are INTEGER(4)/CHARACTER(1)

Unfortunately, it doesn't fix the bogus incommensurate arrays errors.
  
Mikael Morin Oct. 5, 2022, 9:23 a.m. UTC | #2
Le 05/10/2022 à 10:51, Mikael Morin a écrit :
> 
> Unfortunately, it doesn't fix the bogus incommensurate arrays errors.
> 

The following does.


diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
index e6e35ef3c42..2c57c796270 100644
--- a/gcc/fortran/arith.cc
+++ b/gcc/fortran/arith.cc
@@ -1443,7 +1443,7 @@ reduce_binary_aa (arith (*eval) (gfc_expr *, 
gfc_expr *, gfc_expr **),
         gfc_replace_expr (c->expr, r);
      }

-  if (c || d)
+  if (rc == ARITH_OK && (c || d))
      rc = ARITH_INCOMMENSURATE;

    if (rc != ARITH_OK)


There is one last thing that I'm dissatisfied with.
The handling of unknown types should be moved to reduce_binary, because 
the dispatching in reduce_binary doesn't handle EXPR_OP, so even if 
either or both operands are scalar, they are handled by the (array vs 
array) reduce_binary_aa function.  That's confusing.
  
Harald Anlauf Oct. 5, 2022, 9:40 p.m. UTC | #3
Hi Mikael,

> Gesendet: Mittwoch, 05. Oktober 2022 um 11:23 Uhr
> Von: "Mikael Morin" <morin-mikael@orange.fr>
> An: "Harald Anlauf" <anlauf@gmx.de>, "fortran" <fortran@gcc.gnu.org>, "gcc-patches" <gcc-patches@gcc.gnu.org>
> Betreff: Re: [PATCH] Fortran: error recovery for invalid types in array constructors [PR107000]

> The following does.
>
>
> diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
> index e6e35ef3c42..2c57c796270 100644
> --- a/gcc/fortran/arith.cc
> +++ b/gcc/fortran/arith.cc
> @@ -1443,7 +1443,7 @@ reduce_binary_aa (arith (*eval) (gfc_expr *,
> gfc_expr *, gfc_expr **),
>          gfc_replace_expr (c->expr, r);
>       }
>
> -  if (c || d)
> +  if (rc == ARITH_OK && (c || d))
>       rc = ARITH_INCOMMENSURATE;
>
>     if (rc != ARITH_OK)

that's great!  It fixes several rather weird cases.  (There is at least
another PR on the incommensurate arrays, but we should not attempt to
fix everything today.)

> There is one last thing that I'm dissatisfied with.
> The handling of unknown types should be moved to reduce_binary, because
> the dispatching in reduce_binary doesn't handle EXPR_OP, so even if
> either or both operands are scalar, they are handled by the (array vs
> array) reduce_binary_aa function.  That's confusing.

Do you have an example?

Anyway, please find attached an updated patch that incorporates your
two changes and regtests fine on x86_64-pc-linux-gnu.

Even if you disagree, I think this is really a significant step
forwards... (error-recovery wise).

OK for mainline?

Thanks,
Harald
  
Mikael Morin Oct. 6, 2022, 8:14 p.m. UTC | #4
Le 05/10/2022 à 23:40, Harald Anlauf a écrit :
> 
>> There is one last thing that I'm dissatisfied with.
>> The handling of unknown types should be moved to reduce_binary, because
>> the dispatching in reduce_binary doesn't handle EXPR_OP, so even if
>> either or both operands are scalar, they are handled by the (array vs
>> array) reduce_binary_aa function.  That's confusing.

Thinking about it again, I'm not sure my suggestion is right here.
> 
> Do you have an example?
> 
No.  Actually, I think it works, but a weird way.


For example, for this case:

[real :: 2] * [real :: +(.true.)]

First there is a "root" invocation of reduce binary with arguments [real 
:: 2] and [real :: +(.true.)]
The root invocation of reduce_binary will call reduce_binary_aa. This is 
normal.

Then reduce_binary_aa calls reduce_binary again with arguments 2 and 
+(.true.).  And reduce_binary calls again reduce_binary_aa with those 
arguments.  This is weird, reduce_binary_aa is supposed to have arrays 
for both arguments.


The same goes for the array vs constant case, reduce_binary_ca (or 
reduce_binary_ac) is invoked with two scalars, while if you look at 
reduce_binary, you would expect that we only get to reduce_binary_ca 
with a scalar constant and an array as arguments.


I think the checks in the three reduce_binary_* functions should be 
moved into their respective loops, so that we detect the invalid type 
just before these weird recursive calls instead of just after entering 
into them.

OK with that change.
  
Harald Anlauf Oct. 6, 2022, 9:36 p.m. UTC | #5
Hi Mikael,

> Gesendet: Donnerstag, 06. Oktober 2022 um 22:14 Uhr
> Von: "Mikael Morin" <morin-mikael@orange.fr>
> An: "Harald Anlauf" <anlauf@gmx.de>
> Cc: "fortran" <fortran@gcc.gnu.org>, "gcc-patches" <gcc-patches@gcc.gnu.org>
> Betreff: Re: [PATCH, v2] Fortran: error recovery for invalid types in array constructors [PR107000]
>
> Le 05/10/2022 à 23:40, Harald Anlauf a écrit :
> > 
> >> There is one last thing that I'm dissatisfied with.
> >> The handling of unknown types should be moved to reduce_binary, because
> >> the dispatching in reduce_binary doesn't handle EXPR_OP, so even if
> >> either or both operands are scalar, they are handled by the (array vs
> >> array) reduce_binary_aa function.  That's confusing.
> 
> Thinking about it again, I'm not sure my suggestion is right here.
> > 
> > Do you have an example?
> > 
> No.  Actually, I think it works, but a weird way.
> 
> 
> For example, for this case:
> 
> [real :: 2] * [real :: +(.true.)]
> 
> First there is a "root" invocation of reduce binary with arguments [real 
> :: 2] and [real :: +(.true.)]
> The root invocation of reduce_binary will call reduce_binary_aa. This is 
> normal.
> 
> Then reduce_binary_aa calls reduce_binary again with arguments 2 and 
> +(.true.).  And reduce_binary calls again reduce_binary_aa with those 
> arguments.  This is weird, reduce_binary_aa is supposed to have arrays 
> for both arguments.

Am I seeing something different from you?  My gdb says
that one argument of reduce_binary is EXPR_CONSTANT,
the other EXPR_OP and BT_UNKNOWN.  Both rank 0.

> The same goes for the array vs constant case, reduce_binary_ca (or 
> reduce_binary_ac) is invoked with two scalars, while if you look at 
> reduce_binary, you would expect that we only get to reduce_binary_ca 
> with a scalar constant and an array as arguments.
> 
> 
> I think the checks in the three reduce_binary_* functions should be 
> moved into their respective loops, so that we detect the invalid type 
> just before these weird recursive calls instead of just after entering 
> into them.

I think I tried that before, and it didn't work.
There was always one weird case that lead to a bad or
invalid constructor for one of the arrays you want to
look at in the respective loop,  and this is why the
testcase tries to cover everything that I hit then and
there... (hopefully).  So I ended up with the check
before the loop.

What do we actually gain with your suggested change?
Moving the check into the loop does not really make
the code more readable to me.  And the recursion is
needed anyway.

Cheers,
Harald

> OK with that change.
>
  
Mikael Morin Oct. 7, 2022, 8:01 a.m. UTC | #6
Le 06/10/2022 à 23:36, Harald Anlauf a écrit :
>>
>> For example, for this case:
>>
>> [real :: 2] * [real :: +(.true.)]
>>
>> First there is a "root" invocation of reduce binary with arguments [real
>> :: 2] and [real :: +(.true.)]
>> The root invocation of reduce_binary will call reduce_binary_aa. This is
>> normal.
>>
>> Then reduce_binary_aa calls reduce_binary again with arguments 2 and
>> +(.true.).  And reduce_binary calls again reduce_binary_aa with those
>> arguments.  This is weird, reduce_binary_aa is supposed to have arrays
>> for both arguments.
> 
> Am I seeing something different from you?  My gdb says
> that one argument of reduce_binary is EXPR_CONSTANT,
> the other EXPR_OP and BT_UNKNOWN.  Both rank 0.
> 
No, I get the same, and the program goes to reduce_binary_aa with those 
arguments; this is the problem.

>> The same goes for the array vs constant case, reduce_binary_ca (or
>> reduce_binary_ac) is invoked with two scalars, while if you look at
>> reduce_binary, you would expect that we only get to reduce_binary_ca
>> with a scalar constant and an array as arguments.
>>
>>
>> I think the checks in the three reduce_binary_* functions should be
>> moved into their respective loops, so that we detect the invalid type
>> just before these weird recursive calls instead of just after entering
>> into them.
> 
> I think I tried that before, and it didn't work.
> There was always one weird case that lead to a bad or
> invalid constructor for one of the arrays you want to
> look at in the respective loop,  and this is why the
> testcase tries to cover everything that I hit then and
> there... (hopefully).  So I ended up with the check
> before the loop.
> 
I see, I'll have a look.

> What do we actually gain with your suggested change?
> Moving the check into the loop does not really make
> the code more readable to me.  And the recursion is
> needed anyway.
> 
I think we gain clarity, consistency.

I try to rephrase again.
 From a high level point of view, to evaluate a binary operator you need 
a specific (one for each operator) function to evaluate the scalar vs 
scalar case, and three generic (they are common to all the operators) 
functions to handle respectively:
  - the scalar vs array case,
  - the array vs scalar case,
  - the array vs array case,
by calling in a loop the scalar specific function.
Here we are only dealing with constants, arrays of constants, arrays of 
arrays, etc, all valid cases.

Your patch introduces support for invalid cases, that is invalid values 
that can't be reduced to a constant.  This is fine, and it works.
What is weird is that the scalar vs invalid scalar case is caught in the 
array vs array function.
  
Harald Anlauf Oct. 7, 2022, 6:46 p.m. UTC | #7
Am 07.10.22 um 10:01 schrieb Mikael Morin:
> Le 06/10/2022 à 23:36, Harald Anlauf a écrit :
>>>
>>> For example, for this case:
>>>
>>> [real :: 2] * [real :: +(.true.)]
>>>
>>> First there is a "root" invocation of reduce binary with arguments [real
>>> :: 2] and [real :: +(.true.)]
>>> The root invocation of reduce_binary will call reduce_binary_aa. This is
>>> normal.
>>>
>>> Then reduce_binary_aa calls reduce_binary again with arguments 2 and
>>> +(.true.).  And reduce_binary calls again reduce_binary_aa with those
>>> arguments.  This is weird, reduce_binary_aa is supposed to have arrays
>>> for both arguments.
>>
>> Am I seeing something different from you?  My gdb says
>> that one argument of reduce_binary is EXPR_CONSTANT,
>> the other EXPR_OP and BT_UNKNOWN.  Both rank 0.
>>
> No, I get the same, and the program goes to reduce_binary_aa with those
> arguments; this is the problem.
>
>>> The same goes for the array vs constant case, reduce_binary_ca (or
>>> reduce_binary_ac) is invoked with two scalars, while if you look at
>>> reduce_binary, you would expect that we only get to reduce_binary_ca
>>> with a scalar constant and an array as arguments.
>>>
>>>
>>> I think the checks in the three reduce_binary_* functions should be
>>> moved into their respective loops, so that we detect the invalid type
>>> just before these weird recursive calls instead of just after entering
>>> into them.
>>
>> I think I tried that before, and it didn't work.
>> There was always one weird case that lead to a bad or
>> invalid constructor for one of the arrays you want to
>> look at in the respective loop,  and this is why the
>> testcase tries to cover everything that I hit then and
>> there... (hopefully).  So I ended up with the check
>> before the loop.
>>
> I see, I'll have a look.
>
>> What do we actually gain with your suggested change?
>> Moving the check into the loop does not really make
>> the code more readable to me.  And the recursion is
>> needed anyway.
>>
> I think we gain clarity, consistency.
>
> I try to rephrase again.
>  From a high level point of view, to evaluate a binary operator you need
> a specific (one for each operator) function to evaluate the scalar vs
> scalar case, and three generic (they are common to all the operators)
> functions to handle respectively:
>   - the scalar vs array case,
>   - the array vs scalar case,
>   - the array vs array case,
> by calling in a loop the scalar specific function.
> Here we are only dealing with constants, arrays of constants, arrays of
> arrays, etc, all valid cases.
>
> Your patch introduces support for invalid cases, that is invalid values
> that can't be reduced to a constant.  This is fine, and it works.
> What is weird is that the scalar vs invalid scalar case is caught in the
> array vs array function.

OK, that is because reduce_binary dispatches the reduce_binary_*.
We could move the check from reduce_binary_aa to the beginning of
reduce_binary, as with the following change on top of the patch:

diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
index 2c57c796270..91e70655ad3 100644
--- a/gcc/fortran/arith.cc
+++ b/gcc/fortran/arith.cc
@@ -1426,10 +1426,6 @@ reduce_binary_aa (arith (*eval) (gfc_expr *,
gfc_expr *, gfc_expr **),
    if (!gfc_check_conformance (op1, op2, _("elemental binary operation")))
      return ARITH_INCOMMENSURATE;

-  if ((op1->expr_type == EXPR_OP && op1->ts.type == BT_UNKNOWN)
-      || (op2->expr_type == EXPR_OP && op2->ts.type == BT_UNKNOWN))
-    return ARITH_INVALID_TYPE;
-
    head = gfc_constructor_copy (op1->value.constructor);
    for (c = gfc_constructor_first (head),
         d = gfc_constructor_first (op2->value.constructor);
@@ -1467,6 +1463,10 @@ static arith
  reduce_binary (arith (*eval) (gfc_expr *, gfc_expr *, gfc_expr **),
                gfc_expr *op1, gfc_expr *op2, gfc_expr **result)
  {
+  if ((op1->expr_type == EXPR_OP && op1->ts.type == BT_UNKNOWN)
+      || (op2->expr_type == EXPR_OP && op2->ts.type == BT_UNKNOWN))
+    return ARITH_INVALID_TYPE;
+
    if (op1->expr_type == EXPR_CONSTANT && op2->expr_type == EXPR_CONSTANT)
      return eval (op1, op2, result);

However, we cannot remove the checks from reduce_binary_ac
or reduce_binary_ca, as the lengthy testcase proves...

Do you like the above better?

Cheers,
Harald
  
Mikael Morin Oct. 7, 2022, 7:47 p.m. UTC | #8
Le 07/10/2022 à 20:46, Harald Anlauf a écrit :
> 
> OK, that is because reduce_binary dispatches the reduce_binary_*.
> We could move the check from reduce_binary_aa to the beginning of
> reduce_binary, as with the following change on top of the patch:
> 
> diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
> index 2c57c796270..91e70655ad3 100644
> --- a/gcc/fortran/arith.cc
> +++ b/gcc/fortran/arith.cc
> @@ -1426,10 +1426,6 @@ reduce_binary_aa (arith (*eval) (gfc_expr *,
> gfc_expr *, gfc_expr **),
>     if (!gfc_check_conformance (op1, op2, _("elemental binary operation")))
>       return ARITH_INCOMMENSURATE;
> 
> -  if ((op1->expr_type == EXPR_OP && op1->ts.type == BT_UNKNOWN)
> -      || (op2->expr_type == EXPR_OP && op2->ts.type == BT_UNKNOWN))
> -    return ARITH_INVALID_TYPE;
> -
>     head = gfc_constructor_copy (op1->value.constructor);
>     for (c = gfc_constructor_first (head),
>          d = gfc_constructor_first (op2->value.constructor);
> @@ -1467,6 +1463,10 @@ static arith
>   reduce_binary (arith (*eval) (gfc_expr *, gfc_expr *, gfc_expr **),
>                 gfc_expr *op1, gfc_expr *op2, gfc_expr **result)
>   {
> +  if ((op1->expr_type == EXPR_OP && op1->ts.type == BT_UNKNOWN)
> +      || (op2->expr_type == EXPR_OP && op2->ts.type == BT_UNKNOWN))
> +    return ARITH_INVALID_TYPE;
> +
>     if (op1->expr_type == EXPR_CONSTANT && op2->expr_type == EXPR_CONSTANT)
>       return eval (op1, op2, result);
> 
> However, we cannot remove the checks from reduce_binary_ac
> or reduce_binary_ca, as the lengthy testcase proves...
> 
> Do you like the above better?
> 
Yes, definitely, but some less important weirdness remains;
the scalar vs array function catches scalar vs invalid scalar cases.
Let me have a look.
  

Patch

From ad892a270c504def2f8f84494d5c7bcba9aef27f Mon Sep 17 00:00:00 2001
From: Harald Anlauf <anlauf@gmx.de>
Date: Tue, 4 Oct 2022 23:04:06 +0200
Subject: [PATCH] Fortran: error recovery for invalid types in array
 constructors [PR107000]

gcc/fortran/ChangeLog:

	PR fortran/107000
	* arith.cc (gfc_arith_error): Define error message for
	ARITH_INVALID_TYPE.
	(reduce_unary): Catch arithmetic expressions with invalid type.
	(reduce_binary_ac): Likewise.
	(reduce_binary_ca): Likewise.
	(reduce_binary_aa): Likewise.
	(gfc_real2complex): Source expression must be of type REAL.
	* gfortran.h (enum arith): Add ARITH_INVALID_TYPE.

gcc/testsuite/ChangeLog:

	PR fortran/107000
	* gfortran.dg/pr107000.f90: New test.
---
 gcc/fortran/arith.cc                   | 19 ++++++++++
 gcc/fortran/gfortran.h                 |  2 +-
 gcc/testsuite/gfortran.dg/pr107000.f90 | 50 ++++++++++++++++++++++++++
 3 files changed, 70 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gfortran.dg/pr107000.f90

diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
index d57059a375f..e6e35ef3c42 100644
--- a/gcc/fortran/arith.cc
+++ b/gcc/fortran/arith.cc
@@ -118,6 +118,9 @@  gfc_arith_error (arith code)
     case ARITH_WRONGCONCAT:
       p = G_("Illegal type in character concatenation at %L");
       break;
+    case ARITH_INVALID_TYPE:
+      p = G_("Invalid type in arithmetic operation at %L");
+      break;

     default:
       gfc_internal_error ("gfc_arith_error(): Bad error code");
@@ -1261,6 +1264,9 @@  reduce_unary (arith (*eval) (gfc_expr *, gfc_expr **), gfc_expr *op,
   gfc_expr *r;
   arith rc;

+  if (op->expr_type == EXPR_OP && op->ts.type == BT_UNKNOWN)
+    return ARITH_INVALID_TYPE;
+
   if (op->expr_type == EXPR_CONSTANT)
     return eval (op, result);

@@ -1302,6 +1308,9 @@  reduce_binary_ac (arith (*eval) (gfc_expr *, gfc_expr *, gfc_expr **),
   gfc_expr *r;
   arith rc = ARITH_OK;

+  if (op1->expr_type == EXPR_OP && op1->ts.type == BT_UNKNOWN)
+    return ARITH_INVALID_TYPE;
+
   head = gfc_constructor_copy (op1->value.constructor);
   for (c = gfc_constructor_first (head); c; c = gfc_constructor_next (c))
     {
@@ -1354,6 +1363,9 @@  reduce_binary_ca (arith (*eval) (gfc_expr *, gfc_expr *, gfc_expr **),
   gfc_expr *r;
   arith rc = ARITH_OK;

+  if (op2->expr_type == EXPR_OP && op2->ts.type == BT_UNKNOWN)
+    return ARITH_INVALID_TYPE;
+
   head = gfc_constructor_copy (op2->value.constructor);
   for (c = gfc_constructor_first (head); c; c = gfc_constructor_next (c))
     {
@@ -1414,6 +1426,10 @@  reduce_binary_aa (arith (*eval) (gfc_expr *, gfc_expr *, gfc_expr **),
   if (!gfc_check_conformance (op1, op2, _("elemental binary operation")))
     return ARITH_INCOMMENSURATE;

+  if ((op1->expr_type == EXPR_OP && op1->ts.type == BT_UNKNOWN)
+      || (op2->expr_type == EXPR_OP && op2->ts.type == BT_UNKNOWN))
+    return ARITH_INVALID_TYPE;
+
   head = gfc_constructor_copy (op1->value.constructor);
   for (c = gfc_constructor_first (head),
        d = gfc_constructor_first (op2->value.constructor);
@@ -2238,6 +2254,9 @@  gfc_real2complex (gfc_expr *src, int kind)
   arith rc;
   bool did_warn = false;

+  if (src->ts.type != BT_REAL)
+    return NULL;
+
   result = gfc_get_constant_expr (BT_COMPLEX, kind, &src->where);

   mpc_set_fr (result->value.complex, src->value.real, GFC_MPC_RND_MODE);
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 4babd77924b..fc0aa51df57 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -226,7 +226,7 @@  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_WRONGCONCAT, ARITH_INVALID_TYPE
 };

 /* Statements.  */
diff --git a/gcc/testsuite/gfortran.dg/pr107000.f90 b/gcc/testsuite/gfortran.dg/pr107000.f90
new file mode 100644
index 00000000000..c13627f556b
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pr107000.f90
@@ -0,0 +1,50 @@ 
+! { dg-do compile }
+! PR fortran/107000 - ICE in gfc_real2complex, reduce_unary, reduce_binary_*
+! Contributed by G.Steinmetz
+
+program p
+  real    :: y(1)
+  complex :: x(1)
+  x = (1.0, 2.0) * [real :: -'1'] ! { dg-error "Operand of unary numeric operator" }
+  x = (1.0, 2.0) * [complex :: +'1'] ! { dg-error "Invalid type" }
+  x = [complex :: -'1'] * (1.0, 2.0) ! { dg-error "Invalid type" }
+  y = [complex :: -'1'] * 2          ! { dg-error "Invalid type" }
+  y = 2 * [complex :: -'1']        ! { dg-error "Invalid type" }
+  y = 2 * [complex :: -(.true.)]   ! { dg-error "Invalid type" }
+  y = [complex :: -(.true.)] * 2   ! { dg-error "Invalid type" }
+  print *, - [real ::  -'1' ]      ! { dg-error "Invalid type" }
+  print *, - [real :: [-'1']]      ! { dg-error "Invalid type" }
+  print *, - [real ::  +(.true.) ] ! { dg-error "Invalid type" }
+  print *, - [real :: [+(.true.)]] ! { dg-error "Invalid type" }
+  print *, 2 * [real ::  -'1' ]    ! { dg-error "Invalid type" }
+  print *, 2 * [real :: (-'1')]    ! { dg-error "Invalid type" }
+  print *, [real ::  -'1' ] * 2    ! { dg-error "Invalid type" }
+  print *, [real :: (-'1')] * 2    ! { dg-error "Invalid type" }
+  print *, 2 * [integer :: -('1')] ! { dg-error "Invalid type" }
+  print *, [integer :: -('1')] * 2 ! { dg-error "Invalid type" }
+  print *, 2 * [real :: 0, (-'1')] ! { dg-error "Invalid type" }
+  print *, [real :: 0, (-'1')] * 2 ! { dg-error "Invalid type" }
+  print *, 2 * [real :: 0, -'1']   ! { dg-error "Invalid type" }
+  print *, [real :: 0, -'1'] * 2   ! { dg-error "Invalid type" }
+  print *, 2 * [real :: 0, 1+'1']  ! { dg-error "Invalid type" }
+  print *, [real :: 0, 1+'1'] * 2  ! { dg-error "Invalid type" }
+  print *, [real :: 1, +(.true.)]  ! { dg-error "Operand of unary numeric operator" }
+  print *, [real :: 1, -(.true.)]  ! { dg-error "Operand of unary numeric operator" }
+  print *, 2 * [real :: 1, +(.true.)]      ! { dg-error "Invalid type" }
+  print *, [real :: 1, +(.true.)] * 2      ! { dg-error "Invalid type" }
+  print *, [1, 2] * [real :: 1, +(.true.)] ! { dg-error "Operand of unary numeric operator" }
+  print *, [real :: 1, +(.true.)] * [1, 2] ! { dg-error "Operand of unary numeric operator" }
+  print *, [real :: 1, 2] * [real :: 1, +(.true.)] ! { dg-error "operands are incommensurate" }
+  print *, [real :: 1, +(.true.)] * [real :: 1, 2] ! { dg-error "operands are incommensurate" }
+  print *, [real :: 0, -'1'] * [real :: 1, +(+(.true.))] ! { dg-error "operands are incommensurate" }
+  print *, [real :: 1, [(+(.true.))]] * [real :: 0, [(-'1')]] ! { dg-error "operands are incommensurate" }
+
+  ! Legal:
+  print *, 2 * [real :: 1, [2], 3]
+  print *, [real :: 1, [2], 3] * 2
+  print *, [real :: 1, [2], 3] * [real :: 1, [2], 3]
+  print *, [real :: 1, [2], 3] * [integer :: 1, [2], 3]
+  print *, [real :: 1, [2], 3] * [1, [2], 3]
+  print *, [real :: 1,  huge(2.0)] * [real :: 1,  real(1.0)]
+  print *, [real :: 1, -(huge(2.0))] * [real :: 1, +(real(1))]
+end
--
2.35.3