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)

Đang mở
#20,912 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 3 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
28/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Lĩnh vực
compilers

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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
Ngôn ngữ chính
F#
Star
4.3k
Fork
883
Merge trung bình
3 ngày 17 giờ
Pull request đã merge (30 ngày)
110

Chuẩn bị môi trường

Mở trong Codespaces

Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của dotnet/fsharp

Tất cả issue của dotnet/fsharp

Issue tương tự

Thêm issue về Compilers

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.