[Fortran] PR58787 ICE (error recovery) in check_proc_interface

Message ID 6f21174f-436f-d9d5-9230-0b9bd85db703@net-b.de
State New
Headers show
Series
  • [Fortran] PR58787 ICE (error recovery) in check_proc_interface
Related show

Commit Message

Tobias Burnus Oct. 11, 2018, 11:16 p.m.
Next patch, next try …

The problem is the following: We have a use-associated symbol (a 
function) – which is once referenced (by use association).

We now declare a same-name function in the 'contains' section.

gfortran now parses the function name, fetches the symbol and reports 
that one duplicates the function (gfc_error_now). However, it continues 
afterwards and creates a sym_tree in the gfc_current_ns – and duly 
increases the sym->refs to 2.

A bit later, the function gets rejected. (Parsing of the formal 
arguments fails when setting a attribute of a use-associated symbol, but 
I think this doesn't matter. reject_statement() is called, which rolls 
back the symbol, but again, I think that doesn't matter – it just makes 
some other solutions a tad more difficult.)

Now, the contains namespace is cleaned up: As the function name is in 
the namespace's sym_tree, gfc_release_symbol() is invoked.

The symbol itself is not freed (as --refs > 0), but sym->formal_ns is. 
(There are some safety nets, like sym->rev == 2 and sym->ns != 
sym->formal_ns, but they don't help.)

Later, the symbol of the parent's namespace is resolved – and this 
includes resolving the formal arguments. – And accessing freed memory 
might crash gfortran.

As solution, I return early and with an error from get_proc_name(). I 
think that's cleaner than waiting longer – and the early return avoids 
creating the symbol at the first place. (Otherwise, modifications have 
to go beyond rolling back of the symbols; doing refs++ probably helps, 
but is rather intransparent.)


Pro of the early return: Avoids accessing already freed memory, might 
generate shorter error output as some later fails (like for not being 
able to modify attributes of a use-associated symbol) are gone.

Contra: As one returns before entering a procedure, all following lines 
are in the outer scope (usually 'contains') and there probably 
unexpected. Hence, one might get a bunch of follow-up errors which do 
not help.


Build on x86-64 and even more carefully regtested.

OK?

Tobias

Comments

Paul Richard Thomas Oct. 12, 2018, 6:28 a.m. | #1
Hi Tobias,

This is OK.

Thanks for the patch and the extra careful regtesting :-)

Paul

On Fri, 12 Oct 2018 at 00:17, Tobias Burnus <burnus@net-b.de> wrote:
>
> Next patch, next try …
>
> The problem is the following: We have a use-associated symbol (a
> function) – which is once referenced (by use association).
>
> We now declare a same-name function in the 'contains' section.
>
> gfortran now parses the function name, fetches the symbol and reports
> that one duplicates the function (gfc_error_now). However, it continues
> afterwards and creates a sym_tree in the gfc_current_ns – and duly
> increases the sym->refs to 2.
>
> A bit later, the function gets rejected. (Parsing of the formal
> arguments fails when setting a attribute of a use-associated symbol, but
> I think this doesn't matter. reject_statement() is called, which rolls
> back the symbol, but again, I think that doesn't matter – it just makes
> some other solutions a tad more difficult.)
>
> Now, the contains namespace is cleaned up: As the function name is in
> the namespace's sym_tree, gfc_release_symbol() is invoked.
>
> The symbol itself is not freed (as --refs > 0), but sym->formal_ns is.
> (There are some safety nets, like sym->rev == 2 and sym->ns !=
> sym->formal_ns, but they don't help.)
>
> Later, the symbol of the parent's namespace is resolved – and this
> includes resolving the formal arguments. – And accessing freed memory
> might crash gfortran.
>
> As solution, I return early and with an error from get_proc_name(). I
> think that's cleaner than waiting longer – and the early return avoids
> creating the symbol at the first place. (Otherwise, modifications have
> to go beyond rolling back of the symbols; doing refs++ probably helps,
> but is rather intransparent.)
>
>
> Pro of the early return: Avoids accessing already freed memory, might
> generate shorter error output as some later fails (like for not being
> able to modify attributes of a use-associated symbol) are gone.
>
> Contra: As one returns before entering a procedure, all following lines
> are in the outer scope (usually 'contains') and there probably
> unexpected. Hence, one might get a bunch of follow-up errors which do
> not help.
>
>
> Build on x86-64 and even more carefully regtested.
>
> OK?
>
> Tobias
>

Patch

2018-10-12  Tobias Burnus <burnus@net-b.de>

	PR fortran/58787
	* decl.c (get_proc_name): Return with error before
	creating sym_tree.

	PR fortran/58787
	* gfortran.dg/goacc/pr77765.f90: Modify dg-error.
	* gfortran.dg/interface_42.f90: Ditto.
	* gfortran.dg/internal_references_1.f90: Ditto.
	* gfortran.dg/invalid_procedure_name.f90: Ditto.
	* gfortran.dg/pr65453.f90: Ditto.
	* gfortran.dg/pr77414.f90: Ditto.
	* gfortran.dg/pr78741.f90: Ditto.
	* gfortran.dg/same_name_2.f90: Ditto.

diff --git a/gcc/fortran/decl.c b/gcc/fortran/decl.c
index 7f79811d152..87c736fb2db 100644
--- a/gcc/fortran/decl.c
+++ b/gcc/fortran/decl.c
@@ -1231,28 +1231,39 @@  get_proc_name (const char *name, gfc_symbol **result, bool module_fcn_entry)
 	  && sym->attr.proc != 0
 	  && (sym->attr.subroutine || sym->attr.function || sym->attr.entry)
 	  && sym->attr.if_source != IFSRC_UNKNOWN)
-	gfc_error_now ("Procedure %qs at %C is already defined at %L",
-		       name, &sym->declared_at);
-
+	{
+	  gfc_error_now ("Procedure %qs at %C is already defined at %L",
+			 name, &sym->declared_at);
+	  return true;
+	}
       if (sym->attr.flavor != 0
 	  && sym->attr.entry && sym->attr.if_source != IFSRC_UNKNOWN)
-	gfc_error_now ("Procedure %qs at %C is already defined at %L",
-		       name, &sym->declared_at);
+	{
+	  gfc_error_now ("Procedure %qs at %C is already defined at %L",
+			 name, &sym->declared_at);
+	  return true;
+	}
 
       if (sym->attr.external && sym->attr.procedure
 	  && gfc_current_state () == COMP_CONTAINS)
-	gfc_error_now ("Contained procedure %qs at %C clashes with "
-			"procedure defined at %L",
-		       name, &sym->declared_at);
+	{
+	  gfc_error_now ("Contained procedure %qs at %C clashes with "
+			 "procedure defined at %L",
+			 name, &sym->declared_at);
+	  return true;
+	}
 
       /* Trap a procedure with a name the same as interface in the
 	 encompassing scope.  */
       if (sym->attr.generic != 0
 	  && (sym->attr.subroutine || sym->attr.function)
 	  && !sym->attr.mod_proc)
-	gfc_error_now ("Name %qs at %C is already defined"
-		       " as a generic interface at %L",
-		       name, &sym->declared_at);
+	{
+	  gfc_error_now ("Name %qs at %C is already defined"
+			 " as a generic interface at %L",
+			 name, &sym->declared_at);
+	  return true;
+	}
 
       /* Trap declarations of attributes in encompassing scope.  The
 	 signature for this is that ts.kind is set.  Legitimate
@@ -1263,8 +1274,11 @@  get_proc_name (const char *name, gfc_symbol **result, bool module_fcn_entry)
 	  && gfc_current_ns->parent != NULL
 	  && sym->attr.access == 0
 	  && !module_fcn_entry)
-	gfc_error_now ("Procedure %qs at %C has an explicit interface "
+	{
+	  gfc_error_now ("Procedure %qs at %C has an explicit interface "
 		       "from a previous declaration",  name);
+	  return true;
+	}
     }
 
   /* C1246 (R1225) MODULE shall appear only in the function-stmt or
@@ -1276,17 +1290,23 @@  get_proc_name (const char *name, gfc_symbol **result, bool module_fcn_entry)
       && !current_attr.module_procedure
       && sym->attr.proc == PROC_MODULE
       && gfc_state_stack->state == COMP_CONTAINS)
-    gfc_error_now ("Procedure %qs defined in interface body at %L "
-		   "clashes with internal procedure defined at %C",
-		    name, &sym->declared_at);
+    {
+      gfc_error_now ("Procedure %qs defined in interface body at %L "
+		     "clashes with internal procedure defined at %C",
+		     name, &sym->declared_at);
+      return true;
+    }
 
   if (sym && !sym->gfc_new
       && sym->attr.flavor != FL_UNKNOWN
       && sym->attr.referenced == 0 && sym->attr.subroutine == 1
       && gfc_state_stack->state == COMP_CONTAINS
       && gfc_state_stack->previous->state == COMP_SUBROUTINE)
-    gfc_error_now ("Procedure %qs at %C is already defined at %L",
-		    name, &sym->declared_at);
+    {
+      gfc_error_now ("Procedure %qs at %C is already defined at %L",
+		     name, &sym->declared_at);
+      return true;
+    }
 
   if (gfc_current_ns->parent == NULL || *result == NULL)
     return rc;
diff --git a/gcc/testsuite/gfortran.dg/goacc/pr77765.f90 b/gcc/testsuite/gfortran.dg/goacc/pr77765.f90
index 3819cf70c04..afa0a56a632 100644
--- a/gcc/testsuite/gfortran.dg/goacc/pr77765.f90
+++ b/gcc/testsuite/gfortran.dg/goacc/pr77765.f90
@@ -13,7 +13,6 @@  contains
 end module m
 
 ! { dg-error "Procedure 'f' at .1. is already defined" "" { target *-*-* } 8 }
-! { dg-error "Duplicate RECURSIVE attribute specified" "" { target *-*-* } 8 }
 ! { dg-error ".1." "" { target *-*-* } 10 }
-! { dg-error "Unexpected ..ACC ROUTINE" "" { target *-*-* } 11 }
+! { dg-error "Syntax error in ..ACC ROUTINE . NAME . at .1., invalid function name f" "" { target *-*-* } 11 }
 ! { dg-error "Expecting END MODULE statement" "" { target *-*-* } 12 }
diff --git a/gcc/testsuite/gfortran.dg/interface_42.f90 b/gcc/testsuite/gfortran.dg/interface_42.f90
index 1fd47b920df..d260755eb6a 100644
--- a/gcc/testsuite/gfortran.dg/interface_42.f90
+++ b/gcc/testsuite/gfortran.dg/interface_42.f90
@@ -13,7 +13,7 @@  module copy
 
    contains
 
-      subroutine foo_da(da, copy) ! { dg-error "defined in interface body" }
+      subroutine foo_da(da, copy) ! { dg-error "defined in interface body|PROCEDURE attribute conflicts with PROCEDURE attribute" }
          integer, intent(in) :: da(:)
          integer, allocatable, intent(out) :: copy(:)
          allocate( copy( size(da) ) )
@@ -21,4 +21,4 @@  module copy
       end subroutine foo_da
 
 end module copy
-{ dg-prune-output "compilation terminated" }
+! { dg-prune-output "compilation terminated" }
diff --git a/gcc/testsuite/gfortran.dg/internal_references_1.f90 b/gcc/testsuite/gfortran.dg/internal_references_1.f90
index 2434e28d5e3..b53ab325ade 100644
--- a/gcc/testsuite/gfortran.dg/internal_references_1.f90
+++ b/gcc/testsuite/gfortran.dg/internal_references_1.f90
@@ -16,8 +16,8 @@  contains
   end subroutine
 
   subroutine p (i)   ! { dg-error "is already defined" }
-   integer :: i
-  end subroutine
+   integer :: i   ! { dg-error "Unexpected data declaration statement in CONTAINS section" }
+  end subroutine  ! { dg-error "Expecting END MODULE statement" }
 end module
 !
 ! PR25124 - would happily ignore the declaration of foo in the main program.
@@ -27,8 +27,8 @@  x = bar ()          ! This is OK because it is a regular reference.
 x = foo ()
 contains
     function foo () ! { dg-error "explicit interface from a previous" }
-      foo = 1.0
-    end function foo
+      foo = 1.0  ! { dg-error "Unexpected assignment statement in CONTAINS section" }
+    end function foo ! { dg-error "Expecting END PROGRAM statement" }
     function bar ()
       bar = 1.0
     end function bar
diff --git a/gcc/testsuite/gfortran.dg/invalid_procedure_name.f90 b/gcc/testsuite/gfortran.dg/invalid_procedure_name.f90
index dd319382b4d..74eaa636ae0 100644
--- a/gcc/testsuite/gfortran.dg/invalid_procedure_name.f90
+++ b/gcc/testsuite/gfortran.dg/invalid_procedure_name.f90
@@ -9,6 +9,6 @@  INTERFACE I1 ! { dg-error "" }
 END INTERFACE I1
 CONTAINS
  SUBROUTINE I1(I) ! { dg-error "already defined as a generic" }
- END SUBROUTINE I1
+ END SUBROUTINE I1 ! { dg-error "Expecting END PROGRAM statement" }
 END
 
diff --git a/gcc/testsuite/gfortran.dg/pr65453.f90 b/gcc/testsuite/gfortran.dg/pr65453.f90
index 8d30116b79d..9f40121b443 100644
--- a/gcc/testsuite/gfortran.dg/pr65453.f90
+++ b/gcc/testsuite/gfortran.dg/pr65453.f90
@@ -5,4 +5,4 @@  procedure() :: foo   ! { dg-error "(1)" }
   contains
     subroutine foo() ! { dg-error "clashes with procedure" }
     end
-end
+end ! { dg-error "Two main PROGRAMs" }
diff --git a/gcc/testsuite/gfortran.dg/pr77414.f90 b/gcc/testsuite/gfortran.dg/pr77414.f90
index 222c1a31542..00f14e35a8d 100644
--- a/gcc/testsuite/gfortran.dg/pr77414.f90
+++ b/gcc/testsuite/gfortran.dg/pr77414.f90
@@ -4,6 +4,6 @@  subroutine a(x)               ! { dg-error "(1)" }
    character(*) :: x
    contains
       subroutine a(x)         ! { dg-error " is already defined at" }
-         character(*) :: x
+         character(*) :: x    ! { dg-error "Unexpected data declaration statement in CONTAINS section" }
       end subroutine a
-end subroutine a
+end subroutine a  ! { dg-error "Expecting END PROGRAM statement" }
diff --git a/gcc/testsuite/gfortran.dg/pr78741.f90 b/gcc/testsuite/gfortran.dg/pr78741.f90
index 6eb85789f94..707b2996307 100644
--- a/gcc/testsuite/gfortran.dg/pr78741.f90
+++ b/gcc/testsuite/gfortran.dg/pr78741.f90
@@ -11,6 +11,6 @@  entry g(n, x)           ! { dg-error "is already defined" }
    x = 'b'
 contains
    subroutine g         ! { dg-error "(1)" }
-      z(1) = x(1:1)
+      z(1) = x(1:1) ! { dg-error "Unclassifiable statement" }
    end
 end
diff --git a/gcc/testsuite/gfortran.dg/same_name_2.f90 b/gcc/testsuite/gfortran.dg/same_name_2.f90
index 463ac8533f8..f37de55e651 100644
--- a/gcc/testsuite/gfortran.dg/same_name_2.f90
+++ b/gcc/testsuite/gfortran.dg/same_name_2.f90
@@ -10,6 +10,6 @@  subroutine aa ! { dg-error "Procedure" }
    write(*,*) 'AA'
 end subroutine aa
 subroutine aa ! { dg-error "is already defined" }
-   write(*,*) 'BB'
-end subroutine aa
+   write(*,*) 'BB' ! { dg-error "Unexpected WRITE statement in CONTAINS section" }
+end subroutine aa ! { dg-error "Expecting END MODULE statement" }
 end module