Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make typechecking indexed setters with tuples on the right more consistent #17017

Merged
merged 12 commits into from
Apr 19, 2024
3 changes: 2 additions & 1 deletion docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
@@ -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))
15 changes: 12 additions & 3 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
@@ -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 ->
Original file line number Diff line number Diff line change
@@ -233,6 +233,7 @@
<Compile Include="Language\CopyAndUpdateTests.fs" />
<Compile Include="Language\ConstraintIntersectionTests.fs" />
<Compile Include="Language\BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs" />
<Compile Include="Language\IndexedSetTests.fs" />
<Compile Include="ConstraintSolver\PrimitiveConstraints.fs" />
<Compile Include="ConstraintSolver\MemberConstraints.fs" />
<Compile Include="ConstraintSolver\ObjInference.fs" />
182 changes: 182 additions & 0 deletions tests/FSharp.Compiler.ComponentTests/Language/IndexedSetTests.fs
Original file line number Diff line number Diff line change
@@ -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 =
[<Fact>]
let ``Dotless indexed set of parenthesized tuple compiles`` () =
FSharp "
let xs = Array.zeroCreate<int * int * int> 1
xs[0] <- (1, 2, 3)
"
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Dotless indexed set of unparenthesized tuple compiles`` () =
FSharp "
let xs = Array.zeroCreate<int * int * int> 1
xs[0] <- 1, 2, 3
"
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Dotless indexed set of double-parenthesized tuple compiles`` () =
FSharp "
let xs = Array.zeroCreate<int * int * int> 1
xs[0] <- ((1, 2, 3))
"
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Dot-indexed set of parenthesized tuple compiles`` () =
FSharp "
let xs = Array.zeroCreate<int * int * int> 1
xs.[0] <- (1, 2, 3)
"
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Dot-indexed set of unparenthesized tuple compiles`` () =
FSharp "
let xs = Array.zeroCreate<int * int * int> 1
xs.[0] <- 1, 2, 3
"
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Dot-indexed set of double-parenthesized tuple compiles`` () =
FSharp "
let xs = Array.zeroCreate<int * int * int> 1
xs.[0] <- ((1, 2, 3))
"
|> typecheck
|> shouldSucceed

module Dictionary =
// Parsed as SynExpr.Set.
[<Fact>]
let ``Dotless indexed set of parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs[0] <- (1, 2, 3)
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.Set.
[<Fact>]
let ``Dotless indexed set of unparenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs[0] <- 1, 2, 3
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.Set.
[<Fact>]
let ``Dotless indexed set of double-parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs[0] <- ((1, 2, 3))
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.DotIndexedSet.
[<Fact>]
let ``Dot-indexed set of parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs.[0] <- (1, 2, 3)
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.DotIndexedSet.
[<Fact>]
let ``Dot-indexed set of unparenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs.[0] <- 1, 2, 3
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.DotIndexedSet.
[<Fact>]
let ``Dot-indexed set of double-parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs.[0] <- ((1, 2, 3))
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.NamedIndexedPropertySet.
[<Fact>]
let ``Named indexed property set of parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs.Item 0 <- (1, 2, 3)
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.NamedIndexedPropertySet.
[<Fact>]
let ``Named indexed property set of unparenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs.Item 0 <- 1, 2, 3
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.NamedIndexedPropertySet.
[<Fact>]
let ``Named indexed property set of double-parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
xs.Item 0 <- ((1, 2, 3))
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.DotNamedIndexedPropertySet.
[<Fact>]
let ``Dot-named indexed property set of parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
(xs).Item 0 <- (1, 2, 3)
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.DotNamedIndexedPropertySet.
[<Fact>]
let ``Dot-named indexed property set of unparenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
(xs).Item 0 <- 1, 2, 3
"
|> typecheck
|> shouldSucceed

// Parsed as SynExpr.DotNamedIndexedPropertySet.
[<Fact>]
let ``Dot-named indexed property set of double-parenthesized tuple compiles`` () =
FSharp "
let xs = System.Collections.Generic.Dictionary<int, int * int * int> ()
(xs).Item 0 <- ((1, 2, 3))
"
|> typecheck
|> shouldSucceed