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 5d4bd0fe6ac..383e50e1add 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,11 +1,11 @@ ### 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)) * Tooltip "Full name" now shows demangled companion module names (e.g. `MyType.func` instead of `MyTypeModule.func`). ([Issue #17335](https://github.com/dotnet/fsharp/issues/17335), [PR #19867](https://github.com/dotnet/fsharp/pull/19867)) * 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), [PR #19851](https://github.com/dotnet/fsharp/pull/19851)) * Fix missing FS1182 ("unused binding") warning for unused `let` function bindings inside class types. ([Issue #13849](https://github.com/dotnet/fsharp/issues/13849), [PR #19805](https://github.com/dotnet/fsharp/pull/19805)) * Fix inner mutually-recursive `let rec ... and ...` functions under `--realsig+` not being lifted to top-level static methods (TLR), causing `FSharpFunc` closure allocations and loss of `tail.` opcodes — the large struct-mutual-recursion perf regression reported in [Issue #17607](https://github.com/dotnet/fsharp/issues/17607). ([PR #19882](https://github.com/dotnet/fsharp/pull/19882)) * Fix `TypeLoadException` ("Specialize tried to implicitly override a method with weaker type parameter constraints") and the related CLR crash with constrained inline calls by stripping constraints from closure-class typars in `EraseClosures.convIlxClosureDef`. ([Issue #14492](https://github.com/dotnet/fsharp/issues/14492), [Issue #19075](https://github.com/dotnet/fsharp/issues/19075), [PR #19882](https://github.com/dotnet/fsharp/pull/19882)) - * 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)) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index aa8eb266d07..37aa9f02184 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) 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