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/11.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* Fix `[<CompiledName>]` silently producing duplicate IL entries (FS0192/FS2014) when applied to a multi-value let-binding (e.g. `let a, b = 1, 2`); now emits FS0755 at type-check time. ([Issue #6131](https://github.com/dotnet/fsharp/issues/6131))
* Suppress hover/symbol resolution for wildcard `_` patterns inside `member _.…` bodies that incorrectly showed `val _: T` tooltip. ([PR #19760](https://github.com/dotnet/fsharp/pull/19760))
* Deduplicate format specifier locations in computation expressions so editor tooling no longer reports duplicate entries for the same `%` specifier. ([Issue #16419](https://github.com/dotnet/fsharp/issues/16419), [PR #19791](https://github.com/dotnet/fsharp/pull/19791))
* Reject non-function bindings for single-case and partial active pattern names with FS1209, matching the existing multi-case behavior. ([PR #19763](https://github.com/dotnet/fsharp/pull/19763))
Expand Down
17 changes: 17 additions & 0 deletions src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,23 @@ let MakeAndPublishVal (cenv: cenv) env (altActualParent, inSig, declKind, valRec
vspec

let MakeAndPublishVals (cenv: cenv) env (altActualParent, inSig, declKind, valRecInfo, valSchemes, attrs, xmlDoc, literalValue) =
let g = cenv.g

// [<CompiledName>] on a binding that publishes more than one val (e.g. tuple,
// record, list, cons, `let x as y = ...` destructures, multi-case active patterns)
// would silently propagate the same compiled name to every introduced value and
// produce duplicate IL entries (FS0192/FS2014) during codegen. Catch it here where
// the post-generalization val count is authoritative. See dotnet/fsharp#6131.
if Map.count valSchemes > 1 then
match attrs with
| ValAttribString g WellKnownValAttributes.CompiledNameAttribute _ ->
let m =
(Map.toList valSchemes, range0)
||> List.foldBack (fun (_, ValScheme(id = id)) acc ->
if Range.equals acc range0 then id.idRange else unionRanges id.idRange acc)
errorR(Error(FSComp.SR.tcCompiledNameAttributeMisused(), m))
| _ -> ()

Map.foldBack
(fun name (valscheme: ValScheme) values ->
Map.add name (MakeAndPublishVal cenv env (altActualParent, inSig, declKind, valRecInfo, valscheme, attrs, xmlDoc, literalValue, false), valscheme.GeneralizedType) values)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace FSharp.Compiler.ComponentTests.Attributes

open Xunit
open FSharp.Test.Compiler

module CompiledNameMultipleValues =

[<Theory>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet a, b = 1, 2")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet (a, b) = 1, 2")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet ((a, b), c) = (1, 2), 3")>]
[<InlineData("module M\ntype R = { F: int; G: int }\n[<CompiledName(\"X\")>]\nlet { F = a; G = b } = { F = 1; G = 2 }")>]
[<InlineData("module M\ntype T = Pair of int * int\n[<CompiledName(\"X\")>]\nlet (Pair(a, b)) = Pair(1, 2)")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet (Some (a, b)) = Some (1, 2)")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet [| a; b |] = [| 1; 2 |]")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet a :: b = [1; 2]")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet (x as y) = 1")>]
let ``CompiledName on multi-value let-binding produces FS0755`` (source: string) =
FSharp source
|> typecheck
|> shouldFail
|> withErrorCode 755
|> ignore

[<Fact>]
let ``CompiledName on tuple binding fires FS0755 exactly once`` () =
let source = "module M\n[<CompiledName(\"X\")>]\nlet a, b, c = 1, 2, 3"
let result =
FSharp source
|> typecheck
|> shouldFail

let diag755Count =
result.Output.Diagnostics
|> List.filter (fun d -> d.Error = Error 755)
|> List.length
Assert.Equal(1, diag755Count)

[<Fact>]
let ``CompiledName on single-value let-binding still compiles`` () =
FSharp "module M\n[<CompiledName(\"X\")>]\nlet a = 1"
|> compile
|> shouldSucceed
|> withWarnings []
|> ignore

[<Theory>]
[<InlineData("module M\nlet a, b = 1, 2")>]
[<InlineData("module M\nlet (a, b) = 1, 2")>]
[<InlineData("module M\ntype R = { F: int; G: int }\nlet { F = a; G = b } = { F = 1; G = 2 }")>]
let ``Multi-value let-binding without CompiledName still compiles`` (source: string) =
FSharp source
|> compile
|> shouldSucceed
|> withWarnings []
|> ignore

[<Theory>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet (Some x) = Some 1")>]
[<InlineData("module M\ntype Box = Box of int\n[<CompiledName(\"X\")>]\nlet (Box x) = Box 1")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet a, _ = 1, 2")>]
[<InlineData("module M\ntype R = { F: int; G: int }\n[<CompiledName(\"X\")>]\nlet { F = a } = { F = 1; G = 2 }")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet [| a |] = [| 1 |]")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet a :: _ = [1; 2]")>]
[<InlineData("module M\n[<CompiledName(\"X\")>]\nlet (|Even|Odd|) n = if n % 2 = 0 then Even else Odd")>]
let ``CompiledName on single-value destructure does not fire FS0755`` (source: string) =
let result = FSharp source |> typecheck
let diags =
match result with
| CompilationResult.Success r -> r.Diagnostics
| CompilationResult.Failure r -> r.Diagnostics
let fs755 = diags |> List.filter (fun d -> d.Error = Error 755)
Assert.Empty(fs755)
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@
<Compile Include="FSharpChecker\FindReferences.fs" />
<Compile Include="FSharpChecker\SemanticClassificationRegressions.fs" />
<Compile Include="Attributes\AttributeCtorSetPropAccess.fs" />
<Compile Include="Attributes\CompiledNameMultipleValues.fs" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading