I all, the attached patch regression tested on x86_64

I have not used the gfortran.dg/lto/ testsuite things so it took me a while to come up with the test cases. Both are based on Andrew's original examples provided in the PR.

OK for mainline?

I don't think we need to backport this. Let me know otherwise.

Regards,

Jerry
---
fortran: [PR126170] Fix LTO ICE in copy_function_or_variable

    A redundant USE of a module already visible via host association,
    written inside a contained procedure, caused gfortran's module
    reader to create a second copy of that module's derived
    type.

    PR fortran/126170

    gcc/fortran/ChangeLog:

            PR fortran/126170
            * class.cc (gfc_find_derived_vtab): Fix up a stale
            non-use-associated duplicate vtab symbol.
            (gfc_is_finalizable): Same fix-up, for the finalizer wrapper
            symbol.
            * module.cc (read_module): Skip re-importing a symbol already
            visible via host association from the same module.

    gcc/testsuite/ChangeLog:

            PR fortran/126170
            * gfortran.dg/lto/pr126170_0.f90: New test.
            * gfortran.dg/lto/pr126170_1.f90: New test.
---
commit 97fc4e5d8482a24b8fc5319b60c6e0ecb13fc7eb
Author: Jerry DeLisle <[email protected]>
Date:   Thu Jul 9 19:24:49 2026 -0700

    fortran: [PR126170] Fix LTO ICE in copy_function_or_variable
    
    A redundant USE of a module already visible via host association,
    written inside a contained procedure, caused gfortran's module
    reader to create a second copy of that module's derived
    type.
    
    PR fortran/126170
    
    gcc/fortran/ChangeLog:
    
            PR fortran/126170
            * class.cc (gfc_find_derived_vtab): Fix up a stale
            non-use-associated duplicate vtab symbol.
            (gfc_is_finalizable): Same fix-up, for the finalizer wrapper
            symbol.
            * module.cc (read_module): Skip re-importing a symbol already
            visible via host association from the same module.
    
    gcc/testsuite/ChangeLog:
    
            PR fortran/126170
            * gfortran.dg/lto/pr126170_0.f90: New test.
            * gfortran.dg/lto/pr126170_1.f90: New test.

diff --git a/gcc/fortran/class.cc b/gcc/fortran/class.cc
index aa9bdb8f279..c6a1de630fe 100644
--- a/gcc/fortran/class.cc
+++ b/gcc/fortran/class.cc
@@ -2523,6 +2523,15 @@ gfc_find_derived_vtab (gfc_symbol *derived)
       if (vtab == NULL && module_ns != ns)
 	gfc_find_symbol (name, module_ns, 0, &vtab);
 
+      /* Fix up a stale non-use-associated duplicate vtab.  */
+      if (vtab
+	  && (derived->attr.use_assoc || derived->attr.used_in_submodule)
+	  && !vtab->attr.use_assoc && !vtab->module)
+	{
+	  vtab->attr.use_assoc = 1;
+	  vtab->module = derived->module;
+	}
+
       if (vtab == NULL)
 	{
 	  gfc_get_symbol (name, ns, &vtab);
@@ -2860,6 +2869,19 @@ yes:
       gcc_assert (final->initializer
 		  && final->initializer->expr_type != EXPR_NULL);
       *final_expr = final->initializer;
+
+      /* Same fix-up, for the finalizer wrapper symbol.  */
+      if ((derived->attr.use_assoc || derived->attr.used_in_submodule)
+	  && (*final_expr)->expr_type == EXPR_VARIABLE
+	  && (*final_expr)->symtree)
+	{
+	  gfc_symbol *final_sym = (*final_expr)->symtree->n.sym;
+	  if (final_sym && !final_sym->attr.use_assoc && !final_sym->module)
+	    {
+	      final_sym->attr.use_assoc = 1;
+	      final_sym->module = derived->module;
+	    }
+	}
     }
   return true;
 }
diff --git a/gcc/fortran/module.cc b/gcc/fortran/module.cc
index 3bb81bab68a..f7bf5854d74 100644
--- a/gcc/fortran/module.cc
+++ b/gcc/fortran/module.cc
@@ -6053,6 +6053,19 @@ read_module (void)
 				module_name, 0))
 	    continue;
 
+	  /* Skip re-importing a symbol already visible via host association
+	     from the same module.  */
+	  if (!only_flag && !info->u.rsym.renamed
+		&& strcmp (name, module_name) != 0
+		&& gfc_current_ns->parent)
+	    {
+	      gfc_symbol *host_sym;
+	      gfc_find_symbol (name, gfc_current_ns, 1, &host_sym);
+	      if (host_sym && host_sym->module
+		  && strcmp (host_sym->module, module_name) == 0)
+		continue;
+	    }
+
 	  st = gfc_find_symtree (gfc_current_ns->sym_root, p);
 
 	  if (st != NULL
diff --git a/gcc/testsuite/gfortran.dg/lto/pr126170_0.f90 b/gcc/testsuite/gfortran.dg/lto/pr126170_0.f90
new file mode 100644
index 00000000000..d4d2e6907e1
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/lto/pr126170_0.f90
@@ -0,0 +1,37 @@
+! { dg-lto-do link }
+!
+! PR fortran/126170
+! A redundant, already host-associated USE inside a CONTAINS'ed function
+! caused gfortran to serialize a spurious duplicate of a derived type with
+! a FINAL binding.  The compiler-generated vtable and finalizer-wrapper
+! symbols for that duplicate ended up without the module/use association
+! attributes needed to mark them as externally defined elsewhere.  Under
+! -flto this led to an ICE in get_partitioning_class (vtable) and, once
+! that was fixed, an undefined reference to the finalizer wrapper at link
+! time, both triggered when another translation unit referenced the same
+! type.
+
+module vsm
+  type :: vs
+     integer :: c
+   contains
+     final :: vdst
+  end type vs
+contains
+  subroutine vdst(v)
+    type(vs), intent(inout) :: v
+  end subroutine vdst
+end module vsm
+
+module m_ds
+  use vsm
+  type :: mdcc
+  end type mdcc
+contains
+  function mdot(self)
+    use vsm
+    class(mdcc), intent(inout) :: self
+    type(vs) :: mdot
+    mdot%c = 1
+  end function mdot
+end module m_ds
diff --git a/gcc/testsuite/gfortran.dg/lto/pr126170_1.f90 b/gcc/testsuite/gfortran.dg/lto/pr126170_1.f90
new file mode 100644
index 00000000000..b13fd6c8edd
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/lto/pr126170_1.f90
@@ -0,0 +1,14 @@
+module caller
+  use m_ds
+contains
+  subroutine s()
+    type(mdcc) :: self
+    type(vs) :: vs_
+    vs_ = mdot(self)
+  end subroutine s
+end module caller
+
+program main
+  use caller
+  call s()
+end program main

Reply via email to