Release: `inline` function taking the address of a module-level `let mutable` emits a field reference to the wrong startup class when inlined into another assembly (TypeLoadException)

Abierto
#20,912 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 3 días

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
28/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Área
compilers

Línea de trabajo

Reproduce with the two projects in the report: Lib/State.fs defines the inline read that takes &value, and App/Program.fs calls it. Build both in Release, then inspect App.dll for the value@3 MemberRef and compare its parent type with the startup classes in Lib.dll. Done means the Release app prints ok without a TypeLoadException, or the compiler stops inlining such a function across assemblies.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Needs-Triage

Describe the bug

When an inline function takes the address of a module-level let mutable value (&value, e.g. to call Volatile.Read) and that function is inlined into another assembly, the optimized (Release) build of the calling assembly emits a MemberRef to the backing static field on a type named after the assembly (<StartupCode$Lib>.$Lib) instead of the startup class of the file that defines the value (<StartupCode$Lib>.$State). At run time this fails with TypeLoadException. Debug builds (no cross-assembly inlining) work.

To Reproduce

Lib/Lib.fsproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="State.fs" />
  </ItemGroup>
</Project>

Lib/State.fs:

module State

let mutable value : string = "ok"

let inline read () : string = System.Threading.Volatile.Read(&value)

App/App.fsproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Lib\Lib.fsproj" />
  </ItemGroup>
</Project>

App/Program.fs:

[<EntryPoint>]
let main _ =
    printfn "%s" (State.read ())
    0
dotnet run --project App -c Debug     # prints: ok
dotnet run --project App -c Release   # fails:
Unhandled exception. System.TypeLoadException: Could not load type '<StartupCode$Lib>.$Lib' from assembly 'Lib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at Program.main(String[] _arg1)

Metadata of the Release App.dll versus the types that actually exist in Lib.dll:

App TypeRef:   ns=<StartupCode$Lib> name=$Lib
App MemberRef: value@3 on <StartupCode$Lib>.$Lib

Lib types:     State
               <StartupCode$Lib>.$State
               <StartupCode$Lib>.$Lib.AssemblyInfo

Expected behavior

Either the inlined code references the correct, accessible storage for value, or the compiler does not inline such a function across assemblies (the backing field is assembly-internal, so even the correct type name would fail with FieldAccessException). It should not compile into a reference to a non-existent type.

Actual behavior

TypeLoadException at run time in Release only. In a larger project this surfaced as hundreds of failing tests in Release (and in a Microsoft.NET.Sdk.WebAssembly publish) while Debug was fully green. The assembly name does not matter: the same happens for CasAlgebra and Cas.Algebra (<StartupCode$Cas-Algebra>.$Cas.Algebra).

Known workarounds

  • Do not take the address of a module-level let mutable in an inline function that may be inlined into another assembly: plain reads/assignments (optionally with Thread.MemoryBarrier()) work.
  • Not inlining the function (inline removed, or [<MethodImpl(MethodImplOptions.NoInlining)>]) also avoids the problem.
  • --crossoptimize- on the consuming project did not help in our case.

Related information

  • .NET SDK 10.0.400, F# 10.0 (dotnet fsi --version: 15.2.400), FSharp.Core 10.1.400
  • Windows 10 x64; also reproduced in a .NET WebAssembly (AOT) host
  • Occurs with Volatile.Read(&x), Volatile.Write(&x, v) on module-level let mutable values used from inline functions
Lenguaje dominante
F#
Estrellas
4.3k
Forks
883
Merge medio
3 d 17 h
PR fusionados (30 d)
114

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de dotnet/fsharp

Todos los issues de dotnet/fsharp

Issues similares

Más issues de Compilers

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.