Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.TypeLoadException When Interface Type Contains Static Field/Property for Derived Struct Type #104511

Open
AndrewDRX opened this issue Jul 6, 2024 · 9 comments · May be fixed by #111984
Open
Assignees
Labels
area-TypeSystem-coreclr in-pr There is an active PR which will close this issue when it is merged
Milestone

Comments

@AndrewDRX
Copy link

AndrewDRX commented Jul 6, 2024

Description

A System.TypeLoadException exception is thrown when an interface contains either a static field or property with an initial value for a derived struct type. This works for derived record and class types.

Reproduction Steps

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
</Project>
public interface IExample
{
  public static Example DefaultExample { get; } = new();
}
public struct Example : IExample { }
var example = IExample.DefaultExample;
Unhandled exception. System.TypeLoadException: Could not load type 'Example' from assembly 'test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at Program.<Main>$(String[] args)

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 a record or class type then the error is not seen.

E.g.

public record Example : IExample { }

Or

public class Example : IExample { }

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.

public static Example DefaultExample { get => new(); }

Or

public static Example DefaultExample => new();
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jul 6, 2024
@steveisok
Copy link
Member

@EgorBo it seems an exception is being thrown from

pCode = UnsafeJitFunction(pConfig, pilHeader, &jitFlags, pSizeOfCode);

Perhaps this is a JIT issue?

@steveisok
Copy link
Member

likely related to #100077, #88030, and #100950

@lambdageek
Copy link
Member

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

@lambdageek
Copy link
Member

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

@AndrewDRX
Copy link
Author

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.

@steveisok steveisok removed the untriaged New issue has not been triaged by the area owner label Jul 18, 2024
@steveisok steveisok added this to the 10.0.0 milestone Jul 18, 2024
@steveisok
Copy link
Member

steveisok commented Jul 18, 2024

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 👍

@JakeYallop
Copy link
Contributor

JakeYallop commented Jul 18, 2024

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:

sharplab

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).

@steveisok
Copy link
Member

@JakeYallop thanks! We won't lose sight of what's in the duplicate issues as test cases to help validate the fixes.

@2A5F
Copy link

2A5F commented Dec 20, 2024

struct Bar<T>;

struct Foo
{
    public Inner inner;
    
    public struct Inner
    {
        public Bar<Foo> a;
    } 
}

sharplab.io

@dotnet-policy-service dotnet-policy-service bot added the in-pr There is an active PR which will close this issue when it is merged label Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-TypeSystem-coreclr in-pr There is an active PR which will close this issue when it is merged
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants