From bf83f71d72218110f3633bf5543659ec69e2d697 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 28 Mar 2025 22:50:40 +0000 Subject: [PATCH 1/9] Allows patterns to work properly with private types when the pattern is used within the same module scope. --- src/Compiler/Checking/PostInferenceChecks.fs | 12 ++++++++++-- .../Conformance/PatternMatching/Tuple/Tuple.fs | 9 +++++++++ .../Conformance/PatternMatching/Tuple/tuples02.fs | 7 +++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 4df0185a2e5..928b0e6428e 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -532,8 +532,16 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty = | ValueSome tcref -> let thisCompPath = compPathOfCcu cenv.viewCcu let tyconAcc = tcref.Accessibility |> AccessInternalsVisibleToAsInternal thisCompPath cenv.internalsVisibleToPaths - if isLessAccessible tyconAcc valAcc then - errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, (objName())), m)) + + // Skip accessibility checks for compiler-generated pattern inputs, which all have names + // starting with "val patternInput". This allows pattern matching to work properly with private + // types when the pattern is used within the same module scope. + let isCompilerGeneratedPatternInput = + let name: string = objName() + name.StartsWith("val patternInput") + + if not isCompilerGeneratedPatternInput && isLessAccessible tyconAcc valAcc then + errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, objName()), m)) CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs index d7286f04e3b..7cb19a42535 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs @@ -25,6 +25,15 @@ module Tuple = |> typecheck |> shouldSucceed + // This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Tuple) + [] + let ``Tuple - tuples02_fs - --test:ErrorRanges`` compilation = + compilation + |> asFs + |> withOptions ["--test:ErrorRanges"] + |> typecheck + |> shouldSucceed + // This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Tuple) [] let ``Tuple - W_IncompleteMatches01_fs - --test:ErrorRanges`` compilation = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs new file mode 100644 index 00000000000..1f98c623669 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs @@ -0,0 +1,7 @@ +module private PM = + type PT = + abstract A : int + let a = { new PT with member __.A = 1 } + let b, c = + { new PT with member __.A = 1 } + , { new PT with member __.A = 1 } \ No newline at end of file From 1c0b02d6ae797cef14eab0dbd011b5d44275bd4f Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 28 Mar 2025 22:50:40 +0000 Subject: [PATCH 2/9] Allows patterns to work properly with private types when the pattern is used within the same module scope. --- src/Compiler/Checking/PostInferenceChecks.fs | 6 +++--- .../Conformance/PatternMatching/Tuple/Tuple.fs | 9 +++++++++ .../Conformance/PatternMatching/Tuple/tuples02.fs | 7 +++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 4df0185a2e5..d9290ec1576 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -532,8 +532,9 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty = | ValueSome tcref -> let thisCompPath = compPathOfCcu cenv.viewCcu let tyconAcc = tcref.Accessibility |> AccessInternalsVisibleToAsInternal thisCompPath cenv.internalsVisibleToPaths + if isLessAccessible tyconAcc valAcc then - errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, (objName())), m)) + errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, objName()), m)) CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty @@ -2057,8 +2058,7 @@ and CheckBinding cenv env alwaysCheckNoReraise ctxt (TBind(v, bindRhs, _) as bin // Check accessibility if (v.IsMemberOrModuleBinding || v.IsMember) && not v.IsIncrClassGeneratedMember then - let access = AdjustAccess (IsHiddenVal env.sigToImplRemapInfo v) (fun () -> v.DeclaringEntity.CompilationPath) v.Accessibility - CheckTypeForAccess cenv env (fun () -> NicePrint.stringOfQualifiedValOrMember cenv.denv cenv.infoReader vref) access v.Range v.Type + CheckTypeForAccess cenv env (fun () -> NicePrint.stringOfQualifiedValOrMember cenv.denv cenv.infoReader vref) vref.Accessibility v.Range v.Type if cenv.reportErrors then diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs index d7286f04e3b..7cb19a42535 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs @@ -25,6 +25,15 @@ module Tuple = |> typecheck |> shouldSucceed + // This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Tuple) + [] + let ``Tuple - tuples02_fs - --test:ErrorRanges`` compilation = + compilation + |> asFs + |> withOptions ["--test:ErrorRanges"] + |> typecheck + |> shouldSucceed + // This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Tuple) [] let ``Tuple - W_IncompleteMatches01_fs - --test:ErrorRanges`` compilation = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs new file mode 100644 index 00000000000..0b0375a7953 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs @@ -0,0 +1,7 @@ +module PM = + type PT = + abstract A : int + let a = { new PT with member __.A = 1 } + let b, c = + { new PT with member __.A = 1 } + , { new PT with member __.A = 1 } \ No newline at end of file From 1bdcf1a91c201277e10dc4e1a4204b9ceb6c32a3 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Sat, 29 Mar 2025 16:04:16 +0000 Subject: [PATCH 3/9] isCompileGenerated --- src/Compiler/Checking/PostInferenceChecks.fs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index d9290ec1576..1bfc30dec36 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -521,7 +521,7 @@ let AccessInternalsVisibleToAsInternal thisCompPath internalsVisibleToPaths acce accessSubstPaths (thisCompPath, internalsVisibleToPath) access) -let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty = +let CheckTypeForAccess (cenv: cenv) env objName valAcc isCompilerGenerated m ty = if cenv.reportErrors then let visitType ty = @@ -532,8 +532,14 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty = | ValueSome tcref -> let thisCompPath = compPathOfCcu cenv.viewCcu let tyconAcc = tcref.Accessibility |> AccessInternalsVisibleToAsInternal thisCompPath cenv.internalsVisibleToPaths - - if isLessAccessible tyconAcc valAcc then + + // Skip accessibility checks for compiler-generated pattern inputs, which all have names + // starting with "val patternInput". This allows pattern matching to work properly with private + // types when the pattern is used within the same module scope. + // let isCompilerGeneratedPatternInput = + // let name: string = objName() + // name.StartsWith("val patternInput") + if not isCompilerGenerated && isLessAccessible tyconAcc valAcc then errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, objName()), m)) CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty @@ -2058,7 +2064,8 @@ and CheckBinding cenv env alwaysCheckNoReraise ctxt (TBind(v, bindRhs, _) as bin // Check accessibility if (v.IsMemberOrModuleBinding || v.IsMember) && not v.IsIncrClassGeneratedMember then - CheckTypeForAccess cenv env (fun () -> NicePrint.stringOfQualifiedValOrMember cenv.denv cenv.infoReader vref) vref.Accessibility v.Range v.Type + let access = AdjustAccess (IsHiddenVal env.sigToImplRemapInfo v) (fun () -> v.DeclaringEntity.CompilationPath) v.Accessibility + CheckTypeForAccess cenv env (fun () -> NicePrint.stringOfQualifiedValOrMember cenv.denv cenv.infoReader vref) access vref.IsCompilerGenerated v.Range v.Type if cenv.reportErrors then @@ -2283,7 +2290,7 @@ let CheckRecdField isUnion cenv env (tycon: Tycon) (rfield: RecdField) = IsHiddenTyconRepr env.sigToImplRemapInfo tycon || (not isUnion && IsHiddenRecdField env.sigToImplRemapInfo (tcref.MakeNestedRecdFieldRef rfield)) let access = AdjustAccess isHidden (fun () -> tycon.CompilationPath) rfield.Accessibility - CheckTypeForAccess cenv env (fun () -> rfield.LogicalName) access m fieldTy + CheckTypeForAccess cenv env (fun () -> rfield.LogicalName) access false m fieldTy if isByrefLikeTyconRef g m tcref then // Permit Span fields in IsByRefLike types @@ -2551,7 +2558,7 @@ let CheckEntityDefn cenv env (tycon: Entity) = // Access checks let access = AdjustAccess (IsHiddenTycon env.sigToImplRemapInfo tycon) (fun () -> tycon.CompilationPath) tycon.Accessibility - let visitType ty = CheckTypeForAccess cenv env (fun () -> tycon.DisplayNameWithStaticParametersAndUnderscoreTypars) access tycon.Range ty + let visitType ty = CheckTypeForAccess cenv env (fun () -> tycon.DisplayNameWithStaticParametersAndUnderscoreTypars) access false tycon.Range ty abstractSlotValsOfTycons [tycon] |> List.iter (typeOfVal >> visitType) From 07a88e26eaff94476f71b29b5e19e496adef6c2f Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 31 Mar 2025 16:13:25 +0100 Subject: [PATCH 4/9] update test --- .../Conformance/PatternMatching/Tuple/tuples02.fs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs index 0b0375a7953..751f57bb97d 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs @@ -1,4 +1,12 @@ module PM = + type PT = + abstract A : int + let a = { new PT with member __.A = 1 } + let b, c = + { new PT with member __.A = 1 } + , { new PT with member __.A = 1 } + +module private PM2 = type PT = abstract A : int let a = { new PT with member __.A = 1 } From 404ba83e80110ff5463aaf42d478ce2b72335152 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 31 Mar 2025 16:22:13 +0100 Subject: [PATCH 5/9] Update test --- src/Compiler/Checking/PostInferenceChecks.fs | 8 +------- .../Conformance/PatternMatching/Tuple/Tuple.fs | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 1bfc30dec36..5a70dbb1f46 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -532,13 +532,7 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc isCompilerGenerated m ty | ValueSome tcref -> let thisCompPath = compPathOfCcu cenv.viewCcu let tyconAcc = tcref.Accessibility |> AccessInternalsVisibleToAsInternal thisCompPath cenv.internalsVisibleToPaths - - // Skip accessibility checks for compiler-generated pattern inputs, which all have names - // starting with "val patternInput". This allows pattern matching to work properly with private - // types when the pattern is used within the same module scope. - // let isCompilerGeneratedPatternInput = - // let name: string = objName() - // name.StartsWith("val patternInput") + // Skip accessibility checks for compiler-generated pattern inputs if not isCompilerGenerated && isLessAccessible tyconAcc valAcc then errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, objName()), m)) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs index 7cb19a42535..58fded5a1b5 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs @@ -25,7 +25,6 @@ module Tuple = |> typecheck |> shouldSucceed - // This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Tuple) [] let ``Tuple - tuples02_fs - --test:ErrorRanges`` compilation = compilation From 88978993db409a44c7e0b1cf21ae34d79b95b056 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 31 Mar 2025 16:22:22 +0100 Subject: [PATCH 6/9] release notes --- docs/release-notes/.FSharp.Compiler.Service/9.0.300.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index f40ab0428ed..dd6905c7a7b 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -20,6 +20,7 @@ * Fix duplicate parse error reporting for GetBackgroundCheckResultsForFileInProject ([Issue #18379](https://github.com/dotnet/fsharp/issues/18379) [PR #18380](https://github.com/dotnet/fsharp/pull/18380)) * Fix MethodDefNotFound when compiling code invoking delegate with option parameter ([Issue #5171](https://github.com/dotnet/fsharp/issues/5171), [PR #18385](https://github.com/dotnet/fsharp/pull/18385)) * Fix #r nuget ..." downloads unneeded packages ([Issue #18231](https://github.com/dotnet/fsharp/issues/18231), [PR #18393](https://github.com/dotnet/fsharp/pull/18393)) +* Skip accessibility checks for compiler-generated pattern inputs when used within the same module scope. ([Issue #4161](https://github.com/dotnet/fsharp/issues/4161), [PR #18426](https://github.com/dotnet/fsharp/pull/18426)) ### Added * Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241)) From 990a802e3b34f4d8c0dc3ec61e459c21cb4392e8 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 31 Mar 2025 19:10:37 +0100 Subject: [PATCH 7/9] compileExeAndRun --- .../Conformance/PatternMatching/Tuple/Tuple.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs index 58fded5a1b5..1800cb0d352 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs @@ -30,7 +30,7 @@ module Tuple = compilation |> asFs |> withOptions ["--test:ErrorRanges"] - |> typecheck + |> compileExeAndRun |> shouldSucceed // This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Tuple) From 5016a360499bc860361e7fba7220924f83c41dae Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 1 Apr 2025 09:34:32 +0100 Subject: [PATCH 8/9] better naming --- src/Compiler/Checking/PostInferenceChecks.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 5a70dbb1f46..464a8168a40 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -521,7 +521,7 @@ let AccessInternalsVisibleToAsInternal thisCompPath internalsVisibleToPaths acce accessSubstPaths (thisCompPath, internalsVisibleToPath) access) -let CheckTypeForAccess (cenv: cenv) env objName valAcc isCompilerGenerated m ty = +let CheckTypeForAccess (cenv: cenv) env objName valAcc accessedValIsCompilerGenerated m ty = if cenv.reportErrors then let visitType ty = @@ -533,7 +533,7 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc isCompilerGenerated m ty let thisCompPath = compPathOfCcu cenv.viewCcu let tyconAcc = tcref.Accessibility |> AccessInternalsVisibleToAsInternal thisCompPath cenv.internalsVisibleToPaths // Skip accessibility checks for compiler-generated pattern inputs - if not isCompilerGenerated && isLessAccessible tyconAcc valAcc then + if not accessedValIsCompilerGenerated && isLessAccessible tyconAcc valAcc then errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, objName()), m)) CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty From b66660a7111ebe361c40079750cd47c699e310bf Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Sat, 5 Apr 2025 10:26:14 -0400 Subject: [PATCH 9/9] Add a couple more tests --- .../PatternMatching/Tuple/Tuple.fs | 26 ++++++++++++++++++- .../PatternMatching/Tuple/tuples03.fs | 6 +++++ .../PatternMatching/Tuple/tuples04.fs | 6 +++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples03.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples04.fs diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs index 1800cb0d352..79ad5cfbd95 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs @@ -32,7 +32,31 @@ module Tuple = |> withOptions ["--test:ErrorRanges"] |> compileExeAndRun |> shouldSucceed - + + [] + let ``Tuple - tuples03_fs - --test:ErrorRanges`` compilation = + compilation + |> asFs + |> withOptions ["--test:ErrorRanges"] + |> typecheck + |> shouldFail + |> withDiagnostics [ + Error 410, Line 6, Col 12, Line 6, Col 14, "The type 'T' is less accessible than the value, member or type 'val t': T' it is used in." + Error 410, Line 6, Col 9, Line 6, Col 10, "The type 'T' is less accessible than the value, member or type 'val t: T' it is used in." + ] + + [] + let ``Tuple - tuples04_fs - --test:ErrorRanges`` compilation = + compilation + |> asFs + |> withOptions ["--test:ErrorRanges"] + |> typecheck + |> shouldFail + |> withDiagnostics [ + Error 410, Line 6, Col 12, Line 6, Col 14, "The type 'T' is less accessible than the value, member or type 'val internal t': T' it is used in." + Error 410, Line 6, Col 9, Line 6, Col 10, "The type 'T' is less accessible than the value, member or type 'val internal t: T' it is used in." + ] + // This test was automatically generated (moved from FSharpQA suite - Conformance/PatternMatching/Tuple) [] let ``Tuple - W_IncompleteMatches01_fs - --test:ErrorRanges`` compilation = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples03.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples03.fs new file mode 100644 index 00000000000..402101fbc18 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples03.fs @@ -0,0 +1,6 @@ +namespace N + +type internal T = T + +module public M = + let t, t' = T, T diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples04.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples04.fs new file mode 100644 index 00000000000..d01d6b98592 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples04.fs @@ -0,0 +1,6 @@ +namespace N + +type private T = T + +module internal M = + let t, t' = T, T