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)

Aperta
#20,912 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 3 giorni

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
28/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Ambito
compilers

Direzione di ricerca

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.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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
Lingua principale
F#
Stelle
4.3k
Fork
883
Merge medio
3g 17h
PR unite (30g)
114

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di dotnet/fsharp

Tutte le issue di dotnet/fsharp

Issue simili

Altre issue su Compilers

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.