Skip to content

Commit 3299cec

Browse files
committed
Fix16431 - Modify DU static initialization #16661
1 parent 3ac064e commit 3299cec

File tree

5 files changed

+114
-37
lines changed

5 files changed

+114
-37
lines changed

docs/release-notes/.FSharp.Compiler.Service/8.0.300.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Graph Based Checking doesn't throw on invalid parsed input so it can be used for IDE scenarios ([PR #16575](https://github.com/dotnet/fsharp/pull/16575), [PR #16588](https://github.com/dotnet/fsharp/pull/16588), [PR #16643](https://github.com/dotnet/fsharp/pull/16643))
66
* Keep parens for problematic exprs (`if`, `match`, etc.) in `$"{(…):N0}"`, `$"{(…),-3}"`, etc. ([PR #16578](https://github.com/dotnet/fsharp/pull/16578))
77
* Fix crash in DOTNET_SYSTEM_GLOBALIZATION_INVARIANT mode [#PR 16471](https://github.com/dotnet/fsharp/pull/16471))
8-
8+
* Alter initialization code for DUs.[#PR 16661](https://github.com/dotnet/fsharp/pull/16661)
99

1010
### Added
1111

src/Compiler/AbstractIL/il.fs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3952,6 +3952,29 @@ let mdef_code2code f (md: ILMethodDef) =
39523952
let b = MethodBody.IL(notlazy ilCode)
39533953
md.With(body = notlazy b)
39543954

3955+
let appendInstrsToCode (instrs: ILInstr list) (c2: ILCode) =
3956+
let instrs = Array.ofList instrs
3957+
3958+
match
3959+
c2.Instrs
3960+
|> Array.tryFindIndexBack (fun instr ->
3961+
match instr with
3962+
| I_ret -> true
3963+
| _ -> false)
3964+
with
3965+
| Some 0 ->
3966+
{ c2 with
3967+
Instrs = Array.concat [| instrs; c2.Instrs |]
3968+
}
3969+
| Some index ->
3970+
{ c2 with
3971+
Instrs = Array.concat [| c2.Instrs[.. index - 1]; instrs; c2.Instrs[index..] |]
3972+
}
3973+
| None ->
3974+
{ c2 with
3975+
Instrs = Array.append c2.Instrs instrs
3976+
}
3977+
39553978
let prependInstrsToCode (instrs: ILInstr list) (c2: ILCode) =
39563979
let instrs = Array.ofList instrs
39573980
let n = instrs.Length
@@ -3985,6 +4008,9 @@ let prependInstrsToCode (instrs: ILInstr list) (c2: ILCode) =
39854008
Instrs = Array.append instrs c2.Instrs
39864009
}
39874010

4011+
let appendInstrsToMethod newCode md =
4012+
mdef_code2code (appendInstrsToCode newCode) md
4013+
39884014
let prependInstrsToMethod newCode md =
39894015
mdef_code2code (prependInstrsToCode newCode) md
39904016

src/Compiler/AbstractIL/il.fsi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2162,8 +2162,9 @@ val internal mkRawDataValueTypeDef: ILType -> string * size: int32 * pack: uint1
21622162
/// the code, and the first instruction will be the new entry
21632163
/// of the method. The instructions should be non-branching.
21642164

2165+
val internal appendInstrsToCode: ILInstr list -> ILCode -> ILCode
2166+
val internal appendInstrsToMethod: ILInstr list -> ILMethodDef -> ILMethodDef
21652167
val internal prependInstrsToCode: ILInstr list -> ILCode -> ILCode
2166-
21672168
val internal prependInstrsToMethod: ILInstr list -> ILMethodDef -> ILMethodDef
21682169

21692170
/// Injecting initialization code into a class.

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,7 +1908,16 @@ type TypeDefBuilder(tdef: ILTypeDef, tdefDiscards) =
19081908
if not discard then
19091909
AddPropertyDefToHash m gproperties pdef
19101910

1911-
member _.PrependInstructionsToSpecificMethodDef(cond, instrs, tag, imports) =
1911+
member _.AppendInstructionsToSpecificMethodDef(cond, instrs, tag, imports) =
1912+
match ResizeArray.tryFindIndex cond gmethods with
1913+
| Some idx -> gmethods[idx] <- appendInstrsToMethod instrs gmethods[idx]
1914+
| None ->
1915+
let body =
1916+
mkMethodBody (false, [], 1, nonBranchingInstrsToCode instrs, tag, imports)
1917+
1918+
gmethods.Add(mkILClassCtor body)
1919+
1920+
member this.PrependInstructionsToSpecificMethodDef(cond, instrs, tag, imports) =
19121921
match ResizeArray.tryFindIndex cond gmethods with
19131922
| Some idx -> gmethods[idx] <- prependInstrsToMethod instrs gmethods[idx]
19141923
| None ->
@@ -1917,6 +1926,8 @@ type TypeDefBuilder(tdef: ILTypeDef, tdefDiscards) =
19171926

19181927
gmethods.Add(mkILClassCtor body)
19191928

1929+
this
1930+
19201931
and TypeDefsBuilder() =
19211932

19221933
let tdefs =
@@ -2264,6 +2275,22 @@ and AssemblyBuilder(cenv: cenv, anonTypeTable: AnonTypeGenerationTable) as mgbuf
22642275
/// static init fields on script modules.
22652276
let scriptInitFspecs = ConcurrentStack<ILFieldSpec * range>()
22662277

2278+
let initialInstrs seqpt feefee =
2279+
[
2280+
yield!
2281+
(if isEnvVarSet "NO_ADD_FEEFEE_TO_CCTORS" then []
2282+
elif isEnvVarSet "ADD_SEQPT_TO_CCTORS" then seqpt
2283+
else feefee) // mark start of hidden code
2284+
]
2285+
2286+
let finalInstrs fspec =
2287+
[
2288+
yield mkLdcInt32 0
2289+
yield mkNormalStsfld fspec
2290+
yield mkNormalLdsfld fspec
2291+
yield AI_pop
2292+
]
2293+
22672294
member _.AddScriptInitFieldSpec(fieldSpec, range) =
22682295
scriptInitFspecs.Push((fieldSpec, range))
22692296

@@ -2276,15 +2303,7 @@ and AssemblyBuilder(cenv: cenv, anonTypeTable: AnonTypeGenerationTable) as mgbuf
22762303
let InitializeCompiledScript (fspec, m) =
22772304
let ilDebugRange = GenPossibleILDebugRange cenv m
22782305

2279-
mgbuf.AddExplicitInitToSpecificMethodDef(
2280-
(fun (md: ILMethodDef) -> md.IsEntryPoint),
2281-
tref,
2282-
fspec,
2283-
ilDebugRange,
2284-
imports,
2285-
[],
2286-
[]
2287-
)
2306+
mgbuf.AddExplicitInitToEntryPoint(tref, fspec, ilDebugRange, imports, [], [])
22882307

22892308
scriptInitFspecs |> Seq.iter InitializeCompiledScript
22902309
| None -> ()
@@ -2325,24 +2344,23 @@ and AssemblyBuilder(cenv: cenv, anonTypeTable: AnonTypeGenerationTable) as mgbuf
23252344
if ilMethodDef.IsEntryPoint then
23262345
explicitEntryPointInfo <- Some tref
23272346

2328-
member _.AddExplicitInitToSpecificMethodDef(cond, tref, fspec, sourceOpt, imports, feefee, seqpt) =
2329-
// Authoring a .cctor with effects forces the cctor for the 'initialization' module by doing a dummy store & load of a field
2330-
// Doing both a store and load keeps FxCop happier because it thinks the field is useful
2331-
let instrs =
2332-
[
2333-
yield!
2334-
(if isEnvVarSet "NO_ADD_FEEFEE_TO_CCTORS" then []
2335-
elif isEnvVarSet "ADD_SEQPT_TO_CCTORS" then seqpt
2336-
else feefee) // mark start of hidden code
2337-
yield mkLdcInt32 0
2338-
yield mkNormalStsfld fspec
2339-
yield mkNormalLdsfld fspec
2340-
yield AI_pop
2341-
]
2347+
member _.AddExplicitInitToEntryPoint(tref, fspec, sourceOpt, imports, feefee, seqpt) =
2348+
2349+
let cond = (fun (md: ILMethodDef) -> md.IsEntryPoint)
23422350

23432351
gtdefs
23442352
.FindNestedTypeDefBuilder(tref)
2345-
.PrependInstructionsToSpecificMethodDef(cond, instrs, sourceOpt, imports)
2353+
.PrependInstructionsToSpecificMethodDef(cond, (initialInstrs seqpt feefee) @ (finalInstrs fspec), sourceOpt, imports)
2354+
|> ignore
2355+
2356+
member _.AddExplicitInitToCctor(tref, fspec, sourceOpt, imports, feefee, seqpt) =
2357+
2358+
let cond = (fun (md: ILMethodDef) -> md.Name = ".cctor")
2359+
2360+
gtdefs
2361+
.FindNestedTypeDefBuilder(tref)
2362+
.PrependInstructionsToSpecificMethodDef(cond, initialInstrs seqpt feefee, sourceOpt, imports)
2363+
.AppendInstructionsToSpecificMethodDef(cond, finalInstrs fspec, sourceOpt, imports)
23462364

23472365
member _.AddEventDef(tref, edef) =
23482366
gtdefs.FindNestedTypeDefBuilder(tref).AddEventDef(edef)
@@ -10194,15 +10212,7 @@ and GenImplFile cenv (mgbuf: AssemblyBuilder) mainInfoOpt eenv (implFile: Checke
1019410212
// This adds the explicit init of the .cctor to the explicit entry point main method
1019510213
let ilDebugRange = GenPossibleILDebugRange cenv m
1019610214

10197-
mgbuf.AddExplicitInitToSpecificMethodDef(
10198-
(fun md -> md.IsEntryPoint),
10199-
tref,
10200-
fspec,
10201-
ilDebugRange,
10202-
eenv.imports,
10203-
feefee,
10204-
seqpt
10205-
))
10215+
mgbuf.AddExplicitInitToEntryPoint(tref, fspec, ilDebugRange, eenv.imports, feefee, seqpt))
1020610216

1020710217
let cctorMethDef =
1020810218
mkILClassCtor (MethodBody.IL(InterruptibleLazy.FromValue topCode))
@@ -10289,7 +10299,7 @@ and GenForceWholeFileInitializationAsPartOfCCtor cenv (mgbuf: AssemblyBuilder) (
1028910299
// Doing both a store and load keeps FxCop happier because it thinks the field is useful
1029010300
lazyInitInfo.Add(fun fspec feefee seqpt ->
1029110301
let ilDebugRange = GenPossibleILDebugRange cenv m
10292-
mgbuf.AddExplicitInitToSpecificMethodDef((fun md -> md.Name = ".cctor"), tref, fspec, ilDebugRange, imports, feefee, seqpt))
10302+
mgbuf.AddExplicitInitToCctor(tref, fspec, ilDebugRange, imports, feefee, seqpt))
1029310303

1029410304
/// Generate an Equals method.
1029510305
and GenEqualsOverrideCallingIComparable cenv (tcref: TyconRef, ilThisTy, _ilThatTy) =

tests/FSharp.Compiler.ComponentTests/Conformance/Types/UnionTypes/UnionTypes.fs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,3 +609,43 @@ module UnionTypes =
609609
|> withDiagnostics [
610610
(Warning 42, Line 11, Col 12, Line 11, Col 24, "This construct is deprecated: it is only for use in the F# library")
611611
]
612+
613+
614+
//SOURCE=W_UnionCaseProduction01.fsx SCFLAGS="-a --test:ErrorRanges" # W_UnionCaseProduction01.fsx
615+
[<Fact>]
616+
let ``UnionCaseInitialization_repro16431`` () =
617+
618+
let testFs =
619+
SourceCodeFileKind.Create(
620+
"testFs.fs",
621+
$"""
622+
module Test
623+
624+
type ABC =
625+
| A
626+
| B
627+
| C of int
628+
629+
static let c75' = ABC.C 75
630+
static member c75 = c75'
631+
632+
static let ab' = [ A; B ]
633+
static member ab = ab'
634+
""")
635+
636+
let programFs =
637+
SourceCodeFileKind.Create(
638+
"programFs.fs",
639+
$"""
640+
open Test
641+
642+
if (sprintf "%%A" ABC.c75) <> "C 75" then failwith (sprintf "Failed: printing 'ABC.c75': Expected output: 'C 75' Actual output: '%%A'" ABC.c75)
643+
if (sprintf "%%A" ABC.ab) <> "[A; B]" then failwith (sprintf "Failed: printing 'ABC.ab: Expected: '[A; B]' Actual: '%%A'" ABC.ab)
644+
""")
645+
646+
(fsFromString testFs)
647+
|> FS
648+
|> withAdditionalSourceFiles [programFs]
649+
|> asExe
650+
|> compileAndRun
651+
|> shouldSucceed

0 commit comments

Comments
 (0)