From 003e7076a644c31b9ea3a57cdbfc81e35909e19e Mon Sep 17 00:00:00 2001 From: KirtiRamchandani Date: Sun, 14 Jun 2026 08:26:47 +0000 Subject: [PATCH] Fix spurious FS0410 for tuple patternInput bindings (#4161) Skip accessibility checks on compiler-generated patternInput module bindings. These temps are module-init scaffolding whose visibility does not reflect the enclosing let-binding scope, which caused false FS0410 errors when tuple deconstruction referenced private types in the same module. Fixes #4161 --- .../.FSharp.Compiler.Service/11.0.100.md | 1 + src/Compiler/Checking/PostInferenceChecks.fs | 13 +++++--- .../PatternMatching/Tuple/Tuple.fs | 32 +++++++++++++++++++ .../PatternMatching/Tuple/tuples02.fs | 15 +++++++++ .../PatternMatching/Tuple/tuples03.fs | 6 ++++ .../PatternMatching/Tuple/tuples04.fs | 6 ++++ 6 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs 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/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index ab65df29a6e..b6ba1cea739 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -23,6 +23,7 @@ * Preserve source range for type errors on empty-bodied computation expressions (e.g. `foo {}`) in pipelines, function arguments, and type-annotated contexts, instead of reporting `unknown(1,1)`. ([Issue #19550](https://github.com/dotnet/fsharp/issues/19550), [PR #19849](https://github.com/dotnet/fsharp/pull/19849)) * Fix multiline nested type arguments failing to parse when the closing `>` aligns with the opening type name's column. ([Issue #15171](https://github.com/dotnet/fsharp/issues/15171)) * 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 spurious FS0410 accessibility error when tuple-deconstructing bindings use private types in the same module scope. ([Issue #4161](https://github.com/dotnet/fsharp/issues/4161), [PR #19947](https://github.com/dotnet/fsharp/pull/19947)) * 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 internal compiler error FS1110 in `task { let! }` (and other computation expressions) when a generic IL extension method whose `this`-parameter is a method-level type variable is in scope (e.g. `open ReactiveUI`). Regression from PR #19536. ([Issue #19936](https://github.com/dotnet/fsharp/issues/19936)) diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 09266946c43..9849d7ed875 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -525,7 +525,7 @@ let isLessAccessibleWithVisibility (cenv: cenv) itemAccess refAccess = let thisCompPath = compPathOfCcu cenv.viewCcu isLessAccessible (itemAccess |> AccessInternalsVisibleToAsInternal thisCompPath cenv.internalsVisibleToPaths) refAccess -let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty = +let CheckTypeForAccess (cenv: cenv) env objName valAcc skipAccessibilityCheckForCompilerGeneratedVal m ty = if cenv.reportErrors then let visitType ty = @@ -534,7 +534,7 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty = match tryTcrefOfAppTy cenv.g ty with | ValueNone -> () | ValueSome tcref -> - if isLessAccessibleWithVisibility cenv tcref.Accessibility valAcc then + if not skipAccessibilityCheckForCompilerGeneratedVal && isLessAccessibleWithVisibility cenv tcref.Accessibility valAcc then errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, objName()), m)) CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty @@ -2135,7 +2135,10 @@ 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 + // Compiler-generated patternInput temps are module-init scaffolding; their promoted + // accessibility does not reflect the enclosing binding scope (dotnet/fsharp#4161). + let skipAccessibilityCheck = v.IsCompilerGenerated && v.LogicalName.StartsWith("patternInput") + CheckTypeForAccess cenv env (fun () -> NicePrint.stringOfQualifiedValOrMember cenv.denv cenv.infoReader vref) access skipAccessibilityCheck v.Range v.Type CheckInlineValueIsSufficientlyAccessible cenv env v bindRhs @@ -2362,7 +2365,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 @@ -2642,7 +2645,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) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs index d7286f04e3b..10d286cd3e3 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/Tuple.fs @@ -24,6 +24,38 @@ module Tuple = |> withOptions ["--test:ErrorRanges"] |> typecheck |> shouldSucceed + + [] + let ``Tuple - tuples02_fs - --test:ErrorRanges`` compilation = + compilation + |> asFs + |> 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) [] 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..b7cf08921a7 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs @@ -0,0 +1,15 @@ +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 } + let b, c = + { new PT with member __.A = 1 } + , { new PT with member __.A = 1 } 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