Skip to content

Add ExtendedLayoutAttribute support for F# compiler - #19194

Draft
T-Gro with Copilot wants to merge 22 commits into
mainfrom
copilot/add-extended-layout-attribute-support
Draft

Add ExtendedLayoutAttribute support for F# compiler#19194
T-Gro with Copilot wants to merge 22 commits into
mainfrom
copilot/add-extended-layout-attribute-support

Conversation

Copilot AI commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

ExtendedLayoutAttribute Support Implementation - COMPLETE ✅

Phase 1: IL Layer Changes ✅

  • Add Extended case to ILTypeDefLayout type in il.fsi and il.fs
  • Update convertLayout function in il.fs to handle ExtendedTypeAttributes 0x18
  • Update typeLayoutOfFlags in ilread.fs to read 0x18 flags
  • Update GenTypeDefPass3 in ilwrite.fs to handle Extended layout
  • Update typeAttributesOfTypeLayout in ilreflect.fs for Extended
  • Update splitTypeLayout in ilprint.fs for Extended

Phase 2: Compiler Infrastructure ✅

  • Add attrib_ExtendedLayoutAttribute_opt to TcGlobals.fs and TcGlobals.fsi
  • Add error messages to FSComp.txt (errors 3879-3882) for new validations

Phase 3: Type Checking & Validation ✅

  • Add attribute detection in CheckDeclarations.fs
  • Add validation: ExtendedLayout only on structs (error 3881)
  • Add validation: ExtendedLayout + StructLayout conflict check (error 3879)
  • Add validation: Runtime support check (error 3880)
  • Add validation: Reject LayoutKind.Extended (value 1) via StructLayoutAttribute (error 3882)
  • Extend FieldOffset validation for ExtendedLayout types

Phase 4: IL Generation ✅

  • Update IlxGen.fs to detect ExtendedLayoutAttribute and set ILTypeDefLayout.Extended
  • ExtendedLayoutAttribute automatically preserved (not filtered like StructLayout)
  • Build and core tests passed successfully
  • Formatting check passed

Phase 5: Testing ✅

  • Create type checking tests (ExtendedLayout on struct, class, interface, delegate)
  • Create conflict tests (ExtendedLayout + StructLayout)
  • Create FieldOffset validation test
  • Create LayoutKind.Extended rejection test
  • Create IL verification test with 'extended' layout keyword
  • Fix tests with mock attribute (runtime doesn't have ExtendedLayoutAttribute yet)
  • All test files created and integrated into Basic.fs

Quality Assurance ✅

  • Code review completed - all feedback addressed
  • CodeQL security check passed - no vulnerabilities detected
  • Formatting verified
  • Build successful
  • Added runtime issue reference for extendedLayoutKind value clarification

Implementation Complete

All tasks have been successfully completed. The F# compiler now supports ExtendedLayoutAttribute with:

  • Proper IL generation (TypeAttributes 0x18)
  • Comprehensive validation
  • Full test coverage
  • Security review passed
  • Code comments clarified per review feedback
Original prompt

ExtendedLayoutAttribute Support for F# Compiler

Summary

Add support for System.Runtime.InteropServices.ExtendedLayoutAttribute, a new .NET runtime feature that allows specifying extended type layouts (like C structs/unions) for interop scenarios. When a user applies [<ExtendedLayout(ExtendedLayoutKind.CStruct)>] to a struct, the compiler must:

  1. Recognize the attribute and set the correct TypeAttributes.ExtendedLayout bit (0x18) in metadata
  2. Validate attribute usage (no combining with StructLayoutAttribute, no FieldOffset on fields, runtime support check)
  3. Keep the attribute on the type (it's a real user-written attribute, not a pseudo-attribute)

The compiler intentionally does not validate the specific ExtendedLayoutKind values or field types - those semantics are owned by the runtime and may evolve.

Reference: #19190


Architecture Overview

The layout information flows through the system as follows:

User writes [<ExtendedLayout(...)>] on struct
        ↓
CheckDeclarations.fs: Detect attribute, validate, keep on tycon.Attribs
        ↓
IlxGen.fs: Read attributes, determine ILTypeDefLayout.Extended
        ↓
il.fs (convertLayout): ILTypeDefLayout.Extended → TypeAttributes 0x18
        ↓
ILTypeDef created with: attributes containing 0x18, layout = Extended
        ↓
ilwrite.fs: Write TypeAttributes (with 0x18) to TypeDef table Flags column

Implementation Tasks

Task 1: Add Attribute Reference to TcGlobals

Files:

  • src/Compiler/TypedTree/TcGlobals.fs
  • src/Compiler/TypedTree/TcGlobals.fsi

Location in TcGlobals.fs: Near line 1489, where attrib_StructLayoutAttribute is defined.

Add:

member val attrib_ExtendedLayoutAttribute_opt = tryFindSysAttrib "System.Runtime.InteropServices.ExtendedLayoutAttribute"

Location in TcGlobals.fsi: Near line 477, where attrib_StructLayoutAttribute is declared.

Add:

member attrib_ExtendedLayoutAttribute_opt: BuiltinAttribInfo option

Task 2: Add ILTypeDefLayout.Extended Case

Files:

  • src/Compiler/AbstractIL/il.fs
  • src/Compiler/AbstractIL/il.fsi

Location in il.fsi: Around line 1456, the ILTypeDefLayout type definition.

Change from:

type ILTypeDefLayout =
    | Auto
    | Sequential of ILTypeDefLayoutInfo
    | Explicit of ILTypeDefLayoutInfo

To:

type ILTypeDefLayout =
    | Auto
    | Sequential of ILTypeDefLayoutInfo
    | Explicit of ILTypeDefLayoutInfo
    | Extended

Location in il.fs: Around line 2517, the ILTypeDefLayout type definition. Same change.

Location in il.fs: Around line 2630, the convertLayout function.

Change from:

let convertLayout layout =
    match layout with
    | ILTypeDefLayout.Auto -> TypeAttributes.AutoLayout
    | ILTypeDefLayout.Sequential _ -> TypeAttributes.SequentialLayout
    | ILTypeDefLayout.Explicit _ -> TypeAttributes.ExplicitLayout

To:

let convertLayout layout =
    match layout with
    | ILTypeDefLayout.Auto -> TypeAttributes.AutoLayout
    | ILTypeDefLayout.Sequential _ -> TypeAttributes.SequentialLayout
    | ILTypeDefLayout.Explicit _ -> TypeAttributes.ExplicitLayout
    | ILTypeDefLayout.Extended -> enum<TypeAttributes>(0x18)

Task 3: Update IL Reading

File: src/Compiler/AbstractIL/ilread.fs

Location: Around line 2040, the typeLayoutOfFlags function.

Change from:

and typeLayoutOfFlags (ctxt: ILMetadataReader) mdv flags tidx =
    let f = (flags &&& 0x00000018)
    if f = 0x00000008 then
        ILTypeDefLayout.Sequential(seekReadClassLayout ctxt mdv tidx)
    elif f = 0x00000010 then
        ILTypeDefLayout.Explicit(seekReadClassLayout ctxt mdv tidx)
    else
        ILTypeDefLayout.Auto

To:

and typeLayoutOfFlags (ctxt: ILMetadataReader) mdv flags tidx =
    let f = (flags &&& 0x00000018)
    if f = 0x00000008 then
        ILTypeDefLayout.Sequential(seekReadClassLayout ctxt mdv tidx)
    elif f = 0x00000010 then
        ILTypeDefLayout.Explicit(seekReadClassLayout ctxt mdv tidx)
    elif f = 0x00000018 then
        ILTypeDefLayout.Extended
    else
        ILTypeDefLayout.Auto

Task 4: Update IL Writing

File: src/Compiler/AbstractIL/ilwrite.fs

Location: Around line 2884, in GenTypeDefPass3, the ClassLayout handling.

Change from:

match tdef.Layout with
| ILTypeDefLayout.Auto -> ()
| ILTypeDefLayout.Sequential layout | ILTypeDefLayout.Explicit layout ->
    if Option.isSome layout.Pack || Option.isSome layout.Size then
        AddUnsharedRow cenv TableNames.ClassLayout
            (UnsharedRow
                [| UShort (defaultArg layout.Pack (uint16 0x0))
                   ULong (defaultArg layout.Size 0x0)
                   SimpleIndex (TableNames.TypeDef, tidx) |]) |> ignore

To:

match tdef.Layout with
| ILTypeDefLayout.Auto -> ()
| ILTypeDefLayo...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

Copilot AI changed the title [WIP] Add ExtendedLayoutAttribute support for F# compiler Add ExtendedLayoutAttribute support for F# compiler Jan 6, 2026
Copilot AI requested a review from T-Gro January 6, 2026 15:11
Comment thread src/Compiler/Checking/CheckDeclarations.fs Outdated
@T-Gro

T-Gro commented Jan 9, 2026

Copy link
Copy Markdown
Member

Note to self: This waits for NET11 alpha to have the attribute available.

@github-actions

github-actions Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

@T-Gro
T-Gro force-pushed the copilot/add-extended-layout-attribute-support branch 6 times, most recently from 0625d3d to 1d053ed Compare April 21, 2026 06:44
Copilot AI and others added 8 commits April 21, 2026 10:10
…alidation

Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
… IL test

Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
- Resolve merge conflicts in TcGlobals, CheckDeclarations, IlxGen
- Adapt to new WellKnownEntityAttributes pattern (HasFSharpAttributeOpt removed)
- Remove mock-based ExtendedLayout tests (need .NET 11 runtime)
- Fix E_StructLayout_Extended test line/col expectations
- Update surface area baseline for ILTypeDefLayout.Extended
- Add release notes entry

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
T-Gro and others added 6 commits April 21, 2026 10:10
- Error codes 3879-3882 were already allocated on main. Renumber
  ExtendedLayout diagnostics to 3885-3888 and move them to end of
  FSComp.txt for proper sorting.
- Move release notes from 9.0.300.md to 11.0.100.md (correct file).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- il.fs: Replace incorrect 'ECMA-335 spec' reference with runtime issue link
  (0x18 Extended layout is a .NET runtime extension, not in ECMA-335)
- ilread.fs: Add comment explaining why hasLayout only matches Explicit
- CheckDeclarations.fs: Document unreachable None branch in runtime check

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, and other type kinds

- Add noExtendedLayoutAttributeCheck() to hiddenReprChecks, Union, Record,
  Enum, LibraryOnlyILAssembly, and TyconCoreAbbrevThatIsReallyAUnion paths
- Without this, ExtendedLayoutAttribute on non-struct types (when .NET 11
  ships the attribute) would be silently accepted instead of raising FS3887
- Remove dead code: unreachable None branch in extendedLayoutAttributeCheck
  (hasExtendedLayoutAttr is false when attrib_ExtendedLayoutAttribute_opt
  is None, making the inner match redundant)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove dead error tcRuntimeDoesNotSupportExtendedLayoutTypes (3886) that was
  defined in FSComp.txt and all xlf files but never referenced in source code.
  The runtime support check is implicitly handled by tryFindSysAttrib returning
  None when the attribute doesn't exist in the target runtime.
- Update release notes to mention ILTypeDefLayout.Extended API surface change
  and link to the tracking issue.
- Fix release notes formatting (remove stray blank line).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…reviations, update comments

- Add noExtendedLayoutAttributeCheck() to TypeAbbrev path (was missing)
- Update stale comment: LayoutKind.Extended is now in .NET 11+ (not future)
- Clarify FieldOffset rejection comment with rationale

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@T-Gro
T-Gro force-pushed the copilot/add-extended-layout-attribute-support branch from 8b17336 to cee99f5 Compare April 21, 2026 08:48
T-Gro and others added 8 commits May 12, 2026 16:07
Resolve conflict in FSComp.txt by keeping main's 3887 (ilCustomAttrInvalidArrayElemType) and renumbering feature branch entries to 3888-3890.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…layout-attribute-support

# Conflicts:
#	src/Compiler/Checking/CheckDeclarations.fs
#	src/Compiler/FSComp.txt
… .executor-pid)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Now that the repo targets net11.0, System.Runtime.InteropServices.ExtendedLayoutAttribute
is available in the referenced BCL, so the feature can be exercised end-to-end:

- positive: struct with [<ExtendedLayout(ExtendedLayoutKind.CStruct)>] emits the
  0x18 type layout flag and preserves the attribute on the emitted type (verified
  by reading metadata via System.Reflection.Metadata)
- ExtendedLayout + StructLayout combined -> FS3910
- ExtendedLayout on class/interface/record/union/enum/delegate -> FS3911
- FieldOffset on an ExtendedLayout struct -> FS1211

Tests requiring the net11 attribute are gated behind NETCOREAPP.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Review of the ExtendedLayoutAttribute feature found that [<Struct>] records
(which are value types) were rejected with FS3911 'Only structs may be given
the ExtendedLayoutAttribute' - a message that contradicts itself, since a
[<Struct>] record IS a struct. This was inconsistent with plain structs and
struct...end syntax (both allowed) and with StructLayoutAttribute, which is
allowed on struct records.

The Record simple-representation case now routes ExtendedLayout through the
struct-aware check: [<Struct>] records accept it (emitting the 0x18 layout flag
and preserving the attribute, subject to the existing StructLayout-conflict
error), while reference-typed records still reject it.

Discriminated unions remain rejected on purpose: an F# struct DU carries a tag
plus non-overlapping per-case fields, so ExtendedLayoutKind.CUnion would overlap
the tag with data and produce a broken type. This also matches the existing
policy that StructLayoutAttribute is disallowed on unions.

Adds regression tests: struct record emits 0x18 + preserves the attribute,
struct record + StructLayout -> FS3910, struct union -> FS3911; the existing
record-rejection test is clarified to reference records.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follow-up to the [<Struct>] record fix: a [<Struct>] discriminated union was
still rejected with FS3911 'Only structs may be given the ExtendedLayoutAttribute'
- the same self-contradiction, since a [<Struct>] union IS a struct.

Adds FS3913 'The ExtendedLayoutAttribute cannot be applied to discriminated
unions' and routes both union declaration forms (SynTypeDefnSimpleRepr.Union
and the single-case core-abbreviation union) through it. The message is accurate
for struct and reference unions alike: a union carries a case tag plus per-case
fields, which is incompatible with the CStruct/CUnion field layout, so extended
layout is never valid on a union regardless of value-type-ness.

Updates the two union regression tests to assert FS3913; adds the xlf entries.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Every test in CustomAttributes_ExtendedLayout is gated behind #if NETCOREAPP
because it needs System.Runtime.InteropServices.ExtendedLayoutAttribute (net11+).
On other target frameworks the CI builds (e.g. net472) the module body was
entirely excluded, leaving 'module CustomAttributes_ExtendedLayout =' with no
members, which fails to compile (FS0010/FS0058).

Add a placeholder binding under #if !NETCOREAPP so the module is a valid,
non-empty declaration on every target framework while the .NET Core build is
unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The WindowsNoRealsig_testCoreclr leg was canceled after ~1h43m on the prior
run (pipeline timeout hanging in the full FSharp.Core.UnitTests run). All sibling
Windows test legs, including the realsig+ coreclr_release counterpart, passed on
the same commit, so this is a flaky infrastructure timeout unrelated to the
change. Empty commit to re-trigger the matrix.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

3 participants