Skip to content

TypeLoadException when a ref struct using explicit layout has fields of type ref struct #111260

Closed

Description

@lithiumtoast

Description

Context: Found in .NET 9 when experimenting with ref struct in P/Invoke scenarios. I want to make it clear that I'm not using "ref fields" but rather only "ref structs"; the fields of the struct are not ref (pointer) but the type is of ref struct (stack-only).

Reproduction Steps

using System;
using System.Runtime.InteropServices;
					
public class Program
{
    [StructLayout(LayoutKind.Explicit, Size = 8, Pack = 8)]
    public ref struct Foo
    {
        [FieldOffset(0)]
        public Bar A;
        [FieldOffset(4)]
        public Bar B;
    }

    public ref struct Bar
    {
        public uint Value;
    }
	
    public static void Main()
    {
	var e = default(Foo);
	e.A.Value = 1;
	e.B.Value = 2;
	Console.WriteLine("1 + 2 = " + (e.A.Value + e.B.Value));
    }
}

Expected behavior

A ref struct of explicit layout can have fields that are positioned using FieldOffset attribute where the type of the field is a ref struct without creating a runtime exception.

Actual behavior

When a ref struct is using explicit struct layout, a System.TypeLoadException can occur regarding the field offset of the struct's field which the type is another ref struct.

Unhandled exception. System.TypeLoadException: Could not load type 'Foo' from assembly 'r2oeekw4.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field.
   at Program.Main()

Regression?

No response

Known Workarounds

  • Removing the ref from the nested struct.

Configuration

  • .NET 9
  • macOS Ventura 13.6.5
  • Apple M1 (osx-arm64)
  • Not specific to any configuration.

Other information

Hypothesis: The word ref in the context of a struct (stack-only) is confused to be in the context of a field (pointer); the field is mistaken for an object type with rules about field alignment when it's actually a struct.

Activity

  1. jkotas commented on Jan 10, 2025

    @jkotas
    Member

    The offending imprecise check was introduced in dotnet/coreclr#25200 .

  2. AaronRobinsonMSFT commented on Jan 19, 2025

    @AaronRobinsonMSFT
    Member

    @jkotas or @davidwrighton My interpretation of this scenario is that a ByRefLike value type should just be treated as any other value type. The ByRefLike nature of it doesn't impact the overlap or alignment rules.

    My plan here would be to remove the alignment check for the ByRefLike type and let the "value type layout" check work for the ByRefLike type "as is". Is there a flaw with that approach?

    CoreCLR:

    if (pByValueMT->IsByRefLike() || pByValueMT->ContainsGCPointers())
    {
    if ((pFD->GetOffset() & ((ULONG)TARGET_POINTER_SIZE - 1)) != 0)
    {
    // If we got here, then a ByRefLike valuetype or a valuetype containing an OREF was misaligned.
    badOffset = pFD->GetOffset();
    fieldTrust.SetTrust(ExplicitFieldTrust::kNone);
    break;
    }

    native AOT:

    bool needsToBeAligned =
    !computedOffset.IsIndeterminate
    &&
    (
    fieldType.IsGCPointer
    || fieldType.IsByRefLike
    || (fieldType.IsValueType && ((DefType)fieldType).ContainsGCPointers)
    );

  3. davidwrighton commented on Jan 19, 2025

    @davidwrighton
    Member

    We need an alignment check on the actual byref fields... If we remove the check on byref like types as a whole, we need to preserve that detail for them.

  4. AaronRobinsonMSFT commented on Jan 19, 2025

    @AaronRobinsonMSFT
    Member

    We need an alignment check on the actual byref fields...

    Agreed. Does IsByRefLike return true for both ByRefLike types and byref fields?

  5. AaronRobinsonMSFT commented on Jan 19, 2025

    @AaronRobinsonMSFT
    Member

    We need an alignment check on the actual byref fields...

    Oh, perhaps you meant that since ref fields only exists on ByRefLike types the this logic was implied. It does seem we validate this now too though. I wrote some of it previously when ref fields were added. I also see this in native AOT.

  6. AaronRobinsonMSFT commented on Jan 19, 2025

    @AaronRobinsonMSFT
    Member

    @davidwrighton It looks like we are missing some check here.

    we need to preserve that detail for them.

    I think I see what you meant by this now. Thanks.

  7. added
    in-prThere is an active PR which will close this issue when it is merged
    on Jan 19, 2025
  8. added this to the 10.0.0 milestone on Jan 19, 2025
  9. locked and limited conversation to collaborators on Mar 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-TypeSystem-coreclrin-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions