Open AndrewDRX opened 2 months ago
@EgorBo it seems an exception is being thrown from https://github.com/dotnet/runtime/blob/e733c2f22710b6382c9aadb8139833fbf395c332/src/coreclr/vm/prestub.cpp#L939
Perhaps this is a JIT issue?
Mono does something funny here too. If you run with the JIT it prints:
Unhandled Exception:
System.TypeLoadException: Recursive type definition detected .IExample
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: Recursive type definition detected .IExample
If you run with the interpreter, it works.
using System;
public interface IExample
{
public static Example DefaultExample { get; } = new();
}
public struct Example : IExample { }
public class Program
{
public static void Main()
{
var example = IExample.DefaultExample;
Console.WriteLine(example.GetType());
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseMonoRuntime>true</UseMonoRuntime>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>
% dotnet run
Unhandled Exception:
System.TypeLoadException: Recursive type definition detected .IExample
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: Recursive type definition detected .IExample
% MONO_ENV_OPTIONS=--interp dotnet run
Example
Actually if you change it to
public interface IExample
{
public static Example DefaultExample { get; } = default(Example);
}
That's a TLE in CoreCLR (and Mono JIT) too - and in that case we don't even have a cctor in IExample
likely related to #100077, #88030, and #100950
It does look similar. I didn't find those beforehand when searching for existing issues to comment on because my search was focused on interface
-derived types, whereas those are for a self-referencing struct
.
I will leave this issue open for the time being until there is further confirmation that this is indeed a duplicate.
The good news is that we know the problem is a recursive type loading issue and that if we defer the type being fully loaded to a second pass, then it'll likely start working. The bad news is that it's too late in the cycle to take such a change as it has the potential to create other type loading issues. That's pretty risky and so we'll try to fix this early on in the .NET 10 cycle.
@AndrewDRX my advice is to continue to use the workaround that you posted. Thanks for raising this issue 👍
Just wanted to flag one of the cases raised in one of the duplicate issues, in case its helpful. This isn't unique to interfaces:
using System;
using System.Collections.Immutable;
Console.WriteLine(new MyStruct());
public struct MyStruct
{
static ImmutableArray<MyStruct> One;
}
For this case, the workaround isn't really suitable (as there could be a fair bit of data that we don't want to recreate every time). Specifically, ImmutableArray<MyStruct>
causes the issue. Replacing that with other collection types (e.g MyStruct[]
, HashSet<MyStruct>
) allows the code to work (which is the workaround to use for this situation).
@JakeYallop thanks! We won't lose sight of what's in the duplicate issues as test cases to help validate the fixes.
Description
A
System.TypeLoadException
exception is thrown when aninterface
contains either a static field or property with an initial value for a derivedstruct
type. This works for derivedrecord
andclass
types.Reproduction Steps
Expected behavior
Behavior should be consistent for where an
interface
can be statically aware of derived types.Actual behavior
Behavior is inconsistent for where an
interface
can be statically aware of derived types.Regression?
No response
Known Workarounds
See "Other Information" section.
Configuration
No response
Other information
If the
struct
type is changed to arecord
orclass
type then the error is not seen.E.g.
Or
If the static property or field is changed from having an initial value to instead be a property with a getter or an expression-bodied property then the error is not seen.
E.g.
Or