diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
index b24a17b85fb..124baf0dd6e 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
@@ -1,7 +1,8 @@
### Fixed
+* Make typechecking of indexed setters with tuples on the right more consistent. ([Issue #16987](https://github.com/dotnet/fsharp/issues/16987), [PR #17017](https://github.com/dotnet/fsharp/pull/17017))
* Static abstract method on classes no longer yields internal error. ([Issue #17044](https://github.com/dotnet/fsharp/issues/17044), [PR #17055](https://github.com/dotnet/fsharp/pull/17055))
* Disallow calling abstract methods directly on interfaces. ([Issue #14012](https://github.com/dotnet/fsharp/issues/14012), [Issue #16299](https://github.com/dotnet/fsharp/issues/16299), [PR #17021](https://github.com/dotnet/fsharp/pull/17021))
-* Various parenthesization API fixes. ([PR #16977](https://github.com/dotnet/fsharp/pull/16977))
+* Various parenthesization API fixes. ([PR #16977](https://github.com/dotnet/fsharp/pull/16977))
* Fix bug in optimization of for-loops over integral ranges with steps and units of measure. ([Issue #17025](https://github.com/dotnet/fsharp/issues/17025), [PR #17040](https://github.com/dotnet/fsharp/pull/17040), [PR #17048](https://github.com/dotnet/fsharp/pull/17048))
* Fix calling an overridden virtual static method via the interface ([PR #17013](https://github.com/dotnet/fsharp/pull/17013))
diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs
index 4f7b56ebf11..3466b162c3a 100644
--- a/src/Compiler/Checking/CheckExpressions.fs
+++ b/src/Compiler/Checking/CheckExpressions.fs
@@ -5412,7 +5412,10 @@ and TcExprThen (cenv: cenv) overallTy env tpenv isArg synExpr delayed =
TcNonControlFlowExpr env <| fun env ->
if g.langVersion.SupportsFeature LanguageFeature.IndexerNotationWithoutDot then
warning(Error(FSComp.SR.tcIndexNotationDeprecated(), mDot))
- TcIndexerThen cenv env overallTy mWholeExpr mDot tpenv (Some (expr3, mOfLeftOfSet)) expr1 indexArgs delayed
+ // Wrap in extra parens: like MakeDelayedSet,
+ // but we don't actually want to delay it here.
+ let setInfo = SynExpr.Paren (expr3, range0, None, expr3.Range), mOfLeftOfSet
+ TcIndexerThen cenv env overallTy mWholeExpr mDot tpenv (Some setInfo) expr1 indexArgs delayed
// Part of 'T.Ident
| SynExpr.Typar (typar, m) ->
@@ -5827,7 +5830,10 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE
// synExpr1.longId(synExpr2) <- expr3, very rarely used named property setters
| SynExpr.DotNamedIndexedPropertySet (synExpr1, synLongId, synExpr2, expr3, mStmt) ->
TcNonControlFlowExpr env <| fun env ->
- TcExprDotNamedIndexedPropertySet cenv overallTy env tpenv (synExpr1, synLongId, synExpr2, expr3, mStmt)
+ // Wrap in extra parens: like MakeDelayedSet,
+ // but we don't actually want to delay it here.
+ let setInfo = SynExpr.Paren (expr3, range0, None, expr3.Range)
+ TcExprDotNamedIndexedPropertySet cenv overallTy env tpenv (synExpr1, synLongId, synExpr2, setInfo, mStmt)
| SynExpr.LongIdentSet (synLongId, synExpr2, m) ->
TcNonControlFlowExpr env <| fun env ->
@@ -5836,7 +5842,10 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE
// Type.Items(synExpr1) <- synExpr2
| SynExpr.NamedIndexedPropertySet (synLongId, synExpr1, synExpr2, mStmt) ->
TcNonControlFlowExpr env <| fun env ->
- TcExprNamedIndexPropertySet cenv overallTy env tpenv (synLongId, synExpr1, synExpr2, mStmt)
+ // Wrap in extra parens: like MakeDelayedSet,
+ // but we don't actually want to delay it here.
+ let setInfo = SynExpr.Paren (synExpr2, range0, None, synExpr2.Range)
+ TcExprNamedIndexPropertySet cenv overallTy env tpenv (synLongId, synExpr1, setInfo, mStmt)
| SynExpr.TraitCall (TypesForTypar tps, synMemberSig, arg, m) ->
TcNonControlFlowExpr env <| fun env ->
diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
index 4f04946a466..ddafa873e23 100644
--- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
+++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
@@ -233,6 +233,7 @@
+
diff --git a/tests/FSharp.Compiler.ComponentTests/Language/IndexedSetTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/IndexedSetTests.fs
new file mode 100644
index 00000000000..573685aabd9
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Language/IndexedSetTests.fs
@@ -0,0 +1,182 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+module Language.IndexedSetTests
+
+open FSharp.Test.Compiler
+open Xunit
+
+module Array =
+ []
+ let ``Dotless indexed set of parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = Array.zeroCreate 1
+ xs[0] <- (1, 2, 3)
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ []
+ let ``Dotless indexed set of unparenthesized tuple compiles`` () =
+ FSharp "
+ let xs = Array.zeroCreate 1
+ xs[0] <- 1, 2, 3
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ []
+ let ``Dotless indexed set of double-parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = Array.zeroCreate 1
+ xs[0] <- ((1, 2, 3))
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ []
+ let ``Dot-indexed set of parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = Array.zeroCreate 1
+ xs.[0] <- (1, 2, 3)
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ []
+ let ``Dot-indexed set of unparenthesized tuple compiles`` () =
+ FSharp "
+ let xs = Array.zeroCreate 1
+ xs.[0] <- 1, 2, 3
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ []
+ let ``Dot-indexed set of double-parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = Array.zeroCreate 1
+ xs.[0] <- ((1, 2, 3))
+ "
+ |> typecheck
+ |> shouldSucceed
+
+module Dictionary =
+ // Parsed as SynExpr.Set.
+ []
+ let ``Dotless indexed set of parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs[0] <- (1, 2, 3)
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.Set.
+ []
+ let ``Dotless indexed set of unparenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs[0] <- 1, 2, 3
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.Set.
+ []
+ let ``Dotless indexed set of double-parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs[0] <- ((1, 2, 3))
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.DotIndexedSet.
+ []
+ let ``Dot-indexed set of parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs.[0] <- (1, 2, 3)
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.DotIndexedSet.
+ []
+ let ``Dot-indexed set of unparenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs.[0] <- 1, 2, 3
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.DotIndexedSet.
+ []
+ let ``Dot-indexed set of double-parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs.[0] <- ((1, 2, 3))
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.NamedIndexedPropertySet.
+ []
+ let ``Named indexed property set of parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs.Item 0 <- (1, 2, 3)
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.NamedIndexedPropertySet.
+ []
+ let ``Named indexed property set of unparenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs.Item 0 <- 1, 2, 3
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.NamedIndexedPropertySet.
+ []
+ let ``Named indexed property set of double-parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ xs.Item 0 <- ((1, 2, 3))
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.DotNamedIndexedPropertySet.
+ []
+ let ``Dot-named indexed property set of parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ (xs).Item 0 <- (1, 2, 3)
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.DotNamedIndexedPropertySet.
+ []
+ let ``Dot-named indexed property set of unparenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ (xs).Item 0 <- 1, 2, 3
+ "
+ |> typecheck
+ |> shouldSucceed
+
+ // Parsed as SynExpr.DotNamedIndexedPropertySet.
+ []
+ let ``Dot-named indexed property set of double-parenthesized tuple compiles`` () =
+ FSharp "
+ let xs = System.Collections.Generic.Dictionary ()
+ (xs).Item 0 <- ((1, 2, 3))
+ "
+ |> typecheck
+ |> shouldSucceed