Skip to content
Open
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
### Fixed
* 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))
* Fix missing TailCall warning in TOp.IntegerForLoop ([PR #18399](https://github.com/dotnet/fsharp/pull/18399))
* Fix classification of `nameof` in `nameof<'T>`, `match … with nameof ident -> …`. ([Issue #10026](https://github.com/dotnet/fsharp/issues/10026), [PR #18300](https://github.com/dotnet/fsharp/pull/18300))
* Fix Realsig+ generates nested closures with incorrect Generic ([Issue #17797](https://github.com/dotnet/fsharp/issues/17797), [PR #17877](https://github.com/dotnet/fsharp/pull/17877))
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10572,6 +10572,7 @@ and TcMethodApplication
TcAdhocChecksOnLibraryMethods cenv env isInstance finalCalledMeth finalCalledMethInfo objArgs mMethExpr mItem

if not finalCalledMeth.IsIndexParamArraySetter &&
not finalCalledMeth.IsIndexerSetter &&
(finalCalledMeth.ArgSets |> List.existsi (fun i argSet -> argSet.UnnamedCalledArgs |> List.existsi (fun j ca -> ca.Position <> (i, j)))) then
errorR(Deprecated(FSComp.SR.tcUnnamedArgumentsDoNotFormPrefix(), mMethExpr))

Expand Down
11 changes: 9 additions & 2 deletions src/Compiler/Checking/MethodCalls.fs
Original file line number Diff line number Diff line change
Expand Up @@ -658,19 +658,24 @@ type CalledMeth<'T>

let nUnnamedCallerArgs = unnamedCallerArgs.Length
let nUnnamedCalledArgs = unnamedCalledArgs.Length
// For an indexer setter the ParamArray slot is the second-to-last unnamed called arg
// (the last one is the setter 'value'). When a named argument matched one of the indexer
// args, fewer unnamed called args remain and the setter degenerates to the non-ParamArray
// shape - fall back to the regular indexing in that case.
let useIndexerSetterShape = isIndexerSetter && nUnnamedCalledArgs >= 2
let supportsParamArgs =
allowParamArgs &&
nUnnamedCalledArgs >= 1 &&
nUnnamedCallerArgs >= nUnnamedCalledArgs-1 &&
let possibleParamArg =
if isIndexerSetter then
if useIndexerSetterShape then
unnamedCalledArgs[nUnnamedCalledArgs-2]
else
unnamedCalledArgs[nUnnamedCalledArgs-1]
possibleParamArg.IsParamArray && isArray1DTy g possibleParamArg.CalledArgumentType

if supportsParamArgs then
if isIndexerSetter then
if useIndexerSetterShape then
// Note, for an indexer setter nUnnamedCalledArgs will be at least two, and normally exactly 2
let unnamedCalledArgs2 =
unnamedCalledArgs[0..unnamedCalledArgs.Length-3] @
Expand Down Expand Up @@ -808,6 +813,8 @@ type CalledMeth<'T>

member x.IsIndexParamArraySetter = isIndexerSetter && x.UsesParamArrayConversion

member x.IsIndexerSetter = isIndexerSetter

member x.ParamArrayCalledArgOpt = x.ArgSets |> List.tryPick (fun argSet -> argSet.ParamArrayCalledArgOpt)

member x.ParamArrayCallerArgs = x.ArgSets |> List.tryPick (fun argSet -> if Option.isSome argSet.ParamArrayCalledArgOpt then Some argSet.ParamArrayCallerArgs else None )
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Checking/MethodCalls.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ type CalledMeth<'T> =

member IsIndexParamArraySetter: bool

member IsIndexerSetter: bool

/// The method we're attempting to call
member Method: MethInfo

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
module FSharp.Compiler.ComponentTests.ErrorMessages.IndexedSetterNamedArgTests

open Xunit
open FSharp.Test.Compiler

[<Fact>]
let ``Indexed property setter with named argument does not ICE - intrinsic``() =
FSharp """
module Test
type T() =
member x.Item
with get (a1: obj) = 1
and set (a1: obj) (value: int) = ()

let t = T()
t.Item(a1 = "x") <- 1
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Indexed property setter with named argument does not ICE - named property``() =
FSharp """
module Test
type T() =
member x.indexed1
with get (a1: obj) = 1
and set (a1: obj) (value: int) = ()

let t = T()
t.indexed1(a1 = "x") <- 1
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Indexed property setter with named argument does not ICE - extension``() =
FSharp """
module Test
type T() =
member x.indexed1
with get (a1: obj) = 1
and set (a1: obj) (value: int) = ()

module Extensions =
type T with
member x.indexed1
with get (aa1: obj) = 1
and set (aa1: obj) (value: int) = ()

open Extensions
let t = T()
t.indexed1(aa1 = "x") <- 1
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Indexed property getter with named argument still works``() =
FSharp """
module Test
type T() =
member x.indexed1
with get (a1: obj) = 1
and set (a1: obj) (value: int) = ()

let t = T()
let _ = t.indexed1(a1 = "x")
"""
|> compile
|> shouldSucceed

[<Theory>]
[<InlineData("t.indexed1(a1 = \"x\") <- 1")>]
[<InlineData("t.indexed1(\"x\") <- 1")>]
[<InlineData("t.set_indexed1(\"x\", 1)")>]
let ``Indexed setter call shapes compile`` (call: string) =
FSharp $"""
module Test
type T() =
member x.indexed1
with get (a1: obj) = 1
and set (a1: obj) (value: int) = ()

let t = T()
{call}
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Multi-arg indexer setter with named args compiles``() =
FSharp """
module Test
type M() =
member x.Item
with get (a: int, b: string) = 1
and set (a: int, b: string) (v: int) = ()

let m = M()
m.[a = 1, b = "x"] <- 5
m.[b = "x", a = 1] <- 5
m.[1, b = "x"] <- 5
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Default Item indexer setter with named arg compiles``() =
FSharp """
module Test
type D() =
member x.Item
with get (k: string) = 1
and set (k: string) (v: int) = ()

let d = D()
d.[k = "x"] <- 1
"""
|> compile
|> shouldSucceed
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
<!--<Compile Include="EmittedIL\StructDefensiveCopy\StructDefensiveCopy.fs" />-->
<Compile Include="ErrorMessages\UnsupportedAttributes.fs" />
<Compile Include="ErrorMessages\TailCallAttribute.fs" />
<Compile Include="ErrorMessages\IndexedSetterNamedArgTests.fs" />
<Compile Include="ErrorMessages\IndexingSyntax.fs" />
<Compile Include="ErrorMessages\TypeEqualsMissingTests.fs" />
<Compile Include="ErrorMessages\AccessOfTypeAbbreviationTests.fs" />
Expand Down
Loading