Skip to content

Commit aa00b17

Browse files
authored
Fix ICE on named-arg indexer setter (#16034) (#19851)
1 parent 9ceee64 commit aa00b17

6 files changed

Lines changed: 136 additions & 2 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### Fixed
22

3+
* Fix internal error (FS0193) when calling an indexed property setter with a named argument that matches an indexer parameter. ([Issue #16034](https://github.com/dotnet/fsharp/issues/16034), [PR #19851](https://github.com/dotnet/fsharp/pull/19851))
34
* Fix missing FS1182 ("unused binding") warning for unused `let` function bindings inside class types. ([Issue #13849](https://github.com/dotnet/fsharp/issues/13849), [PR #19805](https://github.com/dotnet/fsharp/pull/19805))
45
* Fix inner mutually-recursive `let rec ... and ...` functions under `--realsig+` not being lifted to top-level static methods (TLR), causing `FSharpFunc` closure allocations and loss of `tail.` opcodes — the large struct-mutual-recursion perf regression reported in [Issue #17607](https://github.com/dotnet/fsharp/issues/17607). ([PR #19882](https://github.com/dotnet/fsharp/pull/19882))
56
* Fix `TypeLoadException` ("Specialize tried to implicitly override a method with weaker type parameter constraints") and the related CLR crash with constrained inline calls by stripping constraints from closure-class typars in `EraseClosures.convIlxClosureDef`. ([Issue #14492](https://github.com/dotnet/fsharp/issues/14492), [Issue #19075](https://github.com/dotnet/fsharp/issues/19075), [PR #19882](https://github.com/dotnet/fsharp/pull/19882))

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10578,7 +10578,11 @@ and TcMethodApplication
1057810578

1057910579
TcAdhocChecksOnLibraryMethods cenv env isInstance finalCalledMeth finalCalledMethInfo objArgs mMethExpr mItem
1058010580

10581+
// Indexer setters: when index args are named, the remaining unnamed args'
10582+
// position values won't form a prefix (the 'value' arg has a non-zero j).
10583+
// Without named args the check passes naturally, so blanket skip is safe.
1058110584
if not finalCalledMeth.IsIndexParamArraySetter &&
10585+
not finalCalledMeth.IsIndexerSetter &&
1058210586
(finalCalledMeth.ArgSets |> List.existsi (fun i argSet -> argSet.UnnamedCalledArgs |> List.existsi (fun j ca -> ca.Position <> (i, j)))) then
1058310587
errorR(Deprecated(FSComp.SR.tcUnnamedArgumentsDoNotFormPrefix(), mMethExpr))
1058410588

src/Compiler/Checking/MethodCalls.fs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,19 +658,21 @@ type CalledMeth<'T>
658658

659659
let nUnnamedCallerArgs = unnamedCallerArgs.Length
660660
let nUnnamedCalledArgs = unnamedCalledArgs.Length
661+
// x.Item(i, value = v) — named arg removes 'i' from unnamed, leaving < 2 unnamed called args.
662+
let useIndexerSetterShape = isIndexerSetter && nUnnamedCalledArgs >= 2
661663
let supportsParamArgs =
662664
allowParamArgs &&
663665
nUnnamedCalledArgs >= 1 &&
664666
nUnnamedCallerArgs >= nUnnamedCalledArgs-1 &&
665667
let possibleParamArg =
666-
if isIndexerSetter then
668+
if useIndexerSetterShape then
667669
unnamedCalledArgs[nUnnamedCalledArgs-2]
668670
else
669671
unnamedCalledArgs[nUnnamedCalledArgs-1]
670672
possibleParamArg.IsParamArray && isArray1DTy g possibleParamArg.CalledArgumentType
671673

672674
if supportsParamArgs then
673-
if isIndexerSetter then
675+
if useIndexerSetterShape then
674676
// Note, for an indexer setter nUnnamedCalledArgs will be at least two, and normally exactly 2
675677
let unnamedCalledArgs2 =
676678
unnamedCalledArgs[0..unnamedCalledArgs.Length-3] @
@@ -808,6 +810,8 @@ type CalledMeth<'T>
808810

809811
member x.IsIndexParamArraySetter = isIndexerSetter && x.UsesParamArrayConversion
810812

813+
member x.IsIndexerSetter = isIndexerSetter
814+
811815
member x.ParamArrayCalledArgOpt = x.ArgSets |> List.tryPick (fun argSet -> argSet.ParamArrayCalledArgOpt)
812816

813817
member x.ParamArrayCallerArgs = x.ArgSets |> List.tryPick (fun argSet -> if Option.isSome argSet.ParamArrayCalledArgOpt then Some argSet.ParamArrayCallerArgs else None )

src/Compiler/Checking/MethodCalls.fsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ type CalledMeth<'T> =
271271

272272
member IsIndexParamArraySetter: bool
273273

274+
/// True when this method call is for a property indexer setter (set_Item or named indexer set).
275+
member IsIndexerSetter: bool
276+
274277
/// The method we're attempting to call
275278
member Method: MethInfo
276279

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
module FSharp.Compiler.ComponentTests.ErrorMessages.IndexedSetterNamedArgTests
2+
3+
open Xunit
4+
open FSharp.Test.Compiler
5+
6+
[<Fact>]
7+
let ``Indexed property setter with named argument does not ICE - intrinsic``() =
8+
FSharp """
9+
module Test
10+
type T() =
11+
member x.Item
12+
with get (a1: obj) = 1
13+
and set (a1: obj) (value: int) = ()
14+
15+
let t = T()
16+
t.Item(a1 = "x") <- 1
17+
"""
18+
|> compile
19+
|> shouldSucceed
20+
21+
[<Fact>]
22+
let ``Indexed property setter with named argument does not ICE - named property``() =
23+
FSharp """
24+
module Test
25+
type T() =
26+
member x.indexed1
27+
with get (a1: obj) = 1
28+
and set (a1: obj) (value: int) = ()
29+
30+
let t = T()
31+
t.indexed1(a1 = "x") <- 1
32+
"""
33+
|> compile
34+
|> shouldSucceed
35+
36+
[<Fact>]
37+
let ``Indexed property setter with named argument does not ICE - extension``() =
38+
FSharp """
39+
module Test
40+
type T() =
41+
member x.indexed1
42+
with get (a1: obj) = 1
43+
and set (a1: obj) (value: int) = ()
44+
45+
module Extensions =
46+
type T with
47+
member x.indexed1
48+
with get (aa1: obj) = 1
49+
and set (aa1: obj) (value: int) = ()
50+
51+
open Extensions
52+
let t = T()
53+
t.indexed1(aa1 = "x") <- 1
54+
"""
55+
|> compile
56+
|> shouldSucceed
57+
58+
[<Fact>]
59+
let ``Indexed property getter with named argument still works``() =
60+
FSharp """
61+
module Test
62+
type T() =
63+
member x.indexed1
64+
with get (a1: obj) = 1
65+
and set (a1: obj) (value: int) = ()
66+
67+
let t = T()
68+
let _ = t.indexed1(a1 = "x")
69+
"""
70+
|> compile
71+
|> shouldSucceed
72+
73+
[<Theory>]
74+
[<InlineData("t.indexed1(a1 = \"x\") <- 1")>]
75+
[<InlineData("t.indexed1(\"x\") <- 1")>]
76+
[<InlineData("t.set_indexed1(\"x\", 1)")>]
77+
let ``Indexed setter call shapes compile`` (call: string) =
78+
FSharp $"""
79+
module Test
80+
type T() =
81+
member x.indexed1
82+
with get (a1: obj) = 1
83+
and set (a1: obj) (value: int) = ()
84+
85+
let t = T()
86+
{call}
87+
"""
88+
|> compile
89+
|> shouldSucceed
90+
91+
[<Fact>]
92+
let ``Multi-arg indexer setter with named args compiles``() =
93+
FSharp """
94+
module Test
95+
type M() =
96+
member x.Item
97+
with get (a: int, b: string) = 1
98+
and set (a: int, b: string) (v: int) = ()
99+
100+
let m = M()
101+
m.[a = 1, b = "x"] <- 5
102+
m.[b = "x", a = 1] <- 5
103+
m.[1, b = "x"] <- 5
104+
"""
105+
|> compile
106+
|> shouldSucceed
107+
108+
[<Fact>]
109+
let ``Default Item indexer setter with named arg compiles``() =
110+
FSharp """
111+
module Test
112+
type D() =
113+
member x.Item
114+
with get (k: string) = 1
115+
and set (k: string) (v: int) = ()
116+
117+
let d = D()
118+
d.[k = "x"] <- 1
119+
"""
120+
|> compile
121+
|> shouldSucceed

tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@
301301
<!--<Compile Include="EmittedIL\StructDefensiveCopy\StructDefensiveCopy.fs" />-->
302302
<Compile Include="ErrorMessages\UnsupportedAttributes.fs" />
303303
<Compile Include="ErrorMessages\TailCallAttribute.fs" />
304+
<Compile Include="ErrorMessages\IndexedSetterNamedArgTests.fs" />
304305
<Compile Include="ErrorMessages\IndexingSyntax.fs" />
305306
<Compile Include="ErrorMessages\TypeEqualsMissingTests.fs" />
306307
<Compile Include="ErrorMessages\AccessOfTypeAbbreviationTests.fs" />

0 commit comments

Comments
 (0)