Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

### Fixed

* Fix F# compiler to prevent tail call emission when pinned locals are present ([PR #XXXX](https://github.com/dotnet/fsharp/pull/XXXX))
* Fix duplicate .cctor issue for discriminated unions with generic statics ([Issue #18767](https://github.com/dotnet/fsharp/issues/18767))
* Fix F# compiler to prevent tail call emission when pinned locals are present ([PR #18893](https://github.com/dotnet/fsharp/pull/18893))
* Fix SignatureHash to include constant values in hash computation ([Issue #18758](https://github.com/dotnet/fsharp/issues/18758))
* Fix parsing errors using anonymous records and units of measures ([PR #18543](https://github.com/dotnet/fsharp/pull/18543))
* Fix parsing errors using anonymous records and code quotations ([PR #18603](https://github.com/dotnet/fsharp/pull/18603))
Expand Down
39 changes: 35 additions & 4 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4112,18 +4112,49 @@ let prependInstrsToMethod newCode md =
let cdef_cctorCode2CodeOrCreate tag imports f (cd: ILTypeDef) =
let mdefs = cd.Methods

let cctor =
let cctor, renamedCctors =
match mdefs.FindByName ".cctor" with
| [ mdef ] -> mdef
| [ mdef ] -> mdef, []
| [] ->
let body = mkMethodBody (false, [], 1, nonBranchingInstrsToCode [], tag, imports)
mkILClassCtor body
| _ -> failwith "bad method table: more than one .cctor found"
mkILClassCtor body, []
| multipleCctors ->
// Handle multiple .cctor methods by renaming them and creating a new .cctor that calls them
// This resolves the "duplicate entry '.cctor' in method table" error (FS2014)
let renamedCctors =
multipleCctors
|> List.mapi (fun i mdef ->
let newName = sprintf "cctor_renamed_%d" i
mdef.With(name = newName))

// Create call instructions for each renamed .cctor
// Use a simple self-referencing type
let currentTypeRef = mkILTyRef (ILScopeRef.Local, cd.Name)
let currentType = mkILNonGenericBoxedTy currentTypeRef

let callInstrs =
renamedCctors
|> List.map (fun mdef ->
let mspec =
mkILNonGenericStaticMethSpecInTy (currentType, mdef.Name, [], ILType.Void)

mkNormalCall mspec)

// Create new .cctor that calls all renamed methods
let newCctorInstrs = callInstrs @ [ I_ret ]

let newCctorBody =
mkMethodBody (false, [], 8, nonBranchingInstrsToCode newCctorInstrs, tag, imports)

let newCctor = mkILClassCtor newCctorBody

newCctor, renamedCctors

let methods =
ILMethodDefs(fun () ->
[|
yield f cctor
yield! renamedCctors
for md in mdefs do
if md.Name <> ".cctor" then
yield md
Expand Down
30 changes: 30 additions & 0 deletions tests/FSharp.Compiler.ComponentTests/Misc.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,33 @@ IL_0005: ret"""
"""
IL_0000: call !!0[] [runtime]System.Array::Empty<!!0>()
IL_0005: ret""" ]

[<Fact>]
let ``Discriminated union with generic statics generates single cctor calling renamed methods``() =
FSharp """
module DuplicateCctorFix

type TestUnion<'T when 'T: comparison> =
| A of 'T
| B of string
| C // nullary case that triggers union erasure .cctor for constant field initialization

// Static member that triggers incremental class .cctor generation
static member val StaticProperty = "test" with get, set

// Another static member to ensure .cctor has meaningful initialization
static member CompareStuff x y = compare x y
"""
|> compile
|> shouldSucceed
|> verifyIL [""".method private specialname rtspecialname static
void .cctor() cil managed
{
// Code size
IL_0000: call void DuplicateCctorFix/TestUnion`1::cctor_renamed_0()
IL_0005: call void DuplicateCctorFix/TestUnion`1::cctor_renamed_1()
IL_000a: ret
} // end of method TestUnion`1::.cctor

.method private static void cctor_renamed_0() cil managed
.method private static void cctor_renamed_1() cil managed"""]
Loading