Skip to content

Interpolated string annotated obj produces unverifiable IL #20270

Description

@Smaug123

(Bug found by Claude while I was implementing my deterministic .NET runtime. This report is entirely my understanding, though the code snippets are Claude.)

Interpolated strings can produce unverifiable IL. This appears to be benign on CoreCLR (the reason appears to be because every mismatched type is a reference type, so ldfld only ever sees the same FieldDesc regardless of the type), but it can break other conforming IL consumers.

There are at least two distinct verification errors you can make this way, but the repro below is only one to make it cleaner; you get two simultaneous errors if you use let f (x:int) : System.IComparable = $"{x}".

Repro steps

This is the code:

// Program.fs
let f (x : int) : obj = $"{x}"
System.Console.WriteLine (f 1)
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

</Project>

(The obj annotation is what causes the bug to appear. Without the annotation, the IL is fine.)

Then dotnet build (produces a Debug binary). This is not sensitive to net8 vs net10.

Then rm -rf .tools && dotnet tool install --tool-path .tools dotnet-ilverify and dotnet .tools/.store/dotnet-ilverify/10.0.11/dotnet-ilverify/10.0.11/tools/net10.0/any/ILVerify.dll -r "<locate the runtime here>/*.dll" -r ./bin/Debug/net8.0/FSharp.Core.dll .

You can also produce a bug with let f (x:int) : System.IComparable = $"{x}" (which produces two errors) or with let x = 3 in let o : obj = $"{x}".

Expected behavior

Clean verified IL like the following:

$ dotnet .tools/.store/dotnet-ilverify/10.0.11/dotnet-ilverify/10.0.11/tools/net10.0/any/ILVerify.dll ./bin/Debug/net8.0/repro4.dll -r "/nix/store/ngvwp9ba5xg3a9wilmy3nasgjzkylrpb-dotnet-sdk-10.0.203/share/dotnet/shared/Microsoft.NETCore.App/10.0.7/*.dll" -r ./bin/Debug/net8.0/FSharp.Core.dll

All Classes and Methods in /Users/patrick/Documents/GitHub/repro4/bin/Debug/net8.0/repro4.dll Verified.

(The paths look a bit exotic here, but I'm confident this is not a problem caused by Nix.)

Actual behavior

$ dotnet .tools/.store/dotnet-ilverify/10.0.11/dotnet-ilverify/10.0.11/tools/net10.0/any/ILVerify.dll ./bin/Debug/net8.0/repro4.dll -r "/nix/store/ngvwp9ba5xg3a9wilmy3nasgjzkylrpb-dotnet-sdk-10.0.203/share/dotnet/shared/Microsoft.NETCore.App/10.0.7/*.dll" -r ./bin/Debug/net8.0/FSharp.Core.dll
[IL]: Error [StackUnexpected]: [/Users/patrick/Documents/GitHub/repro4/bin/Debug/net8.0/repro4.dll : Program::f(int32)][offset 0x0000001E][found ref '[FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<object,object,object,object,int32>'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<object,Microsoft.FSharp.Core.Unit,string,string>'] Unexpected type on the stack.
1 Error(s) Verifying /Users/patrick/Documents/GitHub/repro4/bin/Debug/net8.0/repro4.dll

The compiler has allocated PrintfFormat<obj, obj, obj, obj, int>, not the correct PrintfFormat<obj, unit, string, string, int>.

The silent upcast which appears to cause this problem will become visible if you turn on warnings 3559 and 3388.

The IComparable thing I mentioned earlier has this additional verification error:

  [IL]: Error [StackUnexpected]: [repro4.dll : Program::cmp(int32)][offset 0x00000023]
    [found ref 'object'][expected ref '[S.P.CoreLib]System.IComparable'] Unexpected type on the stack.

Known workarounds

Boxing the output works: let f (x : int) : obj = $"{x}" :> obj (or a call to box).

Related information

I've reproduced this in some combinations of the net8 and net10 SDKs and a target framework of net8.0 and net10.0, on darwin-aarch64 and linux-x64 (though not the entire Cartesian product).

$ dotnet --info
.NET SDK:
 Version:           10.0.203
 Commit:            c23858a6d8
 Workload version:  10.0.200-manifests.ef86bccd
 MSBuild version:   18.3.3+c23858a6d

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  26.6
 OS Platform: Darwin
 RID:         osx-arm64
 Base Path:   /nix/store/ngvwp9ba5xg3a9wilmy3nasgjzkylrpb-dotnet-sdk-10.0.203/share/dotnet/sdk/10.0.203/

.NET workloads installed:
There are no installed workloads to display.
Configured to use workload sets when installing new manifests.
No workload sets are installed. Run "dotnet workload restore" to install a workload set.

Host:
  Version:      10.0.7
  Architecture: arm64
  Commit:       b16286c228

.NET SDKs installed:
  10.0.203 [/nix/store/ngvwp9ba5xg3a9wilmy3nasgjzkylrpb-dotnet-sdk-10.0.203/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 10.0.7 [/nix/store/ngvwp9ba5xg3a9wilmy3nasgjzkylrpb-dotnet-sdk-10.0.203/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 10.0.7 [/nix/store/ngvwp9ba5xg3a9wilmy3nasgjzkylrpb-dotnet-sdk-10.0.203/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  DOTNET_CLI_TELEMETRY_OPTOUT              [1]
  DOTNET_LINUX_FRAMEWORK_DIR               [/nix/store/fmkdxq8nv51yha9bqkc2695mcqxgrdq1-dotnet-linux-x64-framework-10.0.7]
  DOTNET_NOLOGO                            [1]
  DOTNET_RUNTIME_SRC                       [/nix/store/a34mfixzm2kyz64h9kqv31cdbiw4n83c-runtime-7706f54]
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE        [1]
  DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK     [1]
  DOTNET_USE_POLLING_FILE_WATCHER          [1]

global.json file:
  Not found

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Status
    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions