From 1fac128a033d1306cfa65938619f0cb98ee828da Mon Sep 17 00:00:00 2001 From: Copilot Date: Mon, 8 Jun 2026 17:31:14 +0200 Subject: [PATCH 1/3] test(#6694): RED tests for recursive module sibling collision Add 7 tests in ErrorMessages/ModuleTests.fs locking the desired behavior: duplicate sibling modules in a 'module rec' / 'namespace rec' must produce FS0037 at type-check time, not FS2014 from IL emit. Filtered run (--filter-class ErrorMessages.Modules): total: 20, failed: 3, succeeded: 17 RED (positive, fail today): - Recursive module with duplicate sibling modules emits FS0037 - Recursive namespace with duplicate sibling modules emits FS0037 - Recursive module nested siblings with duplicate name emit FS0037 GREEN (negative regression guards, pass today): - Recursive module nested cousins with same name compile - Non-recursive module with duplicate sibling modules still emits FS0037 - Single file with two namespace fragments and different modules compiles - Recursive module siblings differing only in case compile Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ErrorMessages/ModuleTests.fs | 98 ++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs index 4ebb36eac5c..e5c76ccd542 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs @@ -191,4 +191,100 @@ AutoOpen>] L1 = do () |> shouldFail |> withDiagnostics [ Error 10, Line 3, Col 1, Line 3, Col 9, "Unexpected start of structured construct in attribute list" - ] \ No newline at end of file + ] + + [] + let ``Recursive module with duplicate sibling modules emits FS0037`` () = + FSharp """ +module rec A + +module Foobar = let x = 1 +module Foobar = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Foobar" + |> ignore + + [] + let ``Recursive namespace with duplicate sibling modules emits FS0037`` () = + FSharp """ +namespace rec N + +module Foobar = let x = 1 +module Foobar = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Foobar" + |> ignore + + [] + let ``Recursive module nested cousins with same name compile`` () = + FSharp """ +module rec Outer + +module A = + module Inner = let x = 1 + +module B = + module Inner = let y = 2 +""" + |> typecheck + |> shouldSucceed + + [] + let ``Recursive module nested siblings with duplicate name emit FS0037`` () = + FSharp """ +module rec Outer + +module Inner = + module Dup = let x = 1 + module Dup = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Dup" + |> ignore + + [] + let ``Non-recursive module with duplicate sibling modules still emits FS0037`` () = + FSharp """ +module A + +module Foobar = let x = 1 +module Foobar = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Foobar" + |> ignore + + [] + let ``Single file with two namespace fragments and different modules compiles`` () = + FSharp """ +namespace N + +module M = let x = 1 + +namespace N + +module M2 = let y = 2 +""" + |> typecheck + |> shouldSucceed + + [] + let ``Recursive module siblings differing only in case compile`` () = + FSharp """ +module rec A + +module Foobar = let x = 1 +module FooBar = let y = 2 +""" + |> typecheck + |> shouldSucceed \ No newline at end of file From 4db291128029b5ae793c8320a45f96f06a032b4d Mon Sep 17 00:00:00 2001 From: Copilot Date: Mon, 8 Jun 2026 17:58:32 +0200 Subject: [PATCH 2/3] fix(#6694): detect duplicate sibling modules in recursive scopes at publish time In `module rec` / `namespace rec` groups all siblings share the same envInitial during Phase1A, so the existing CheckForDuplicateModule call in TcTyconDefnCore_Phase1A_BuildInitialModule cannot see them. Move the check to the Phase1AB publish point where envAbove's accumulator already contains earlier-published siblings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Checking/CheckDeclarations.fs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 4a4361e9808..1811bda062d 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4095,6 +4095,11 @@ module EstablishTypeDefinitionCores = let envTmp, withEnvs = (envInitial, withEntities) ||> MutRecShapes.computeEnvs (fun envAbove (MutRecDefnsPhase2DataForModule (moduleTyAcc, moduleEntity)) -> + // In recursive scopes all siblings build their entities in Phase1A against the same + // envInitial, so duplicate sibling-module names are not yet visible there. Detect + // them here at the publish point, where envAbove's accumulator already contains + // any earlier-published siblings. See https://github.com/dotnet/fsharp/issues/6694. + CheckForDuplicateModule envAbove moduleEntity.DemangledModuleOrNamespaceName moduleEntity.Range PublishModuleDefn cenv envAbove moduleEntity MakeInnerEnvWithAcc true envAbove moduleEntity.Id moduleTyAcc moduleEntity.ModuleOrNamespaceType.ModuleOrNamespaceKind) (fun envAbove _ -> envAbove) From 9e3b1a8ec820bad95f31821004a12311b42064aa Mon Sep 17 00:00:00 2001 From: Copilot Date: Mon, 8 Jun 2026 19:00:33 +0200 Subject: [PATCH 3/3] docs(#6694): release notes entry for recursive-module duplicate diagnostic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 965957dbc0c..313a0cf7deb 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,5 +1,6 @@ ### Fixed +* Report `FS0037 Duplicate definition of type or module` at type-check time when two sibling modules in a `module rec` / `namespace rec` group share a name, instead of letting the duplicate slip through to IL emit where it surfaced as the cryptic `FS2014: duplicate entry . in type index table`. ([Issue #6694](https://github.com/dotnet/fsharp/issues/6694), [PR #19913](https://github.com/dotnet/fsharp/pull/19913)) * 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))