From 73af24cbea4cdc1de15d8fd0351be939e12fb020 Mon Sep 17 00:00:00 2001 From: Travis Date: Thu, 12 Feb 2026 13:44:09 -0600 Subject: [PATCH 1/3] Show the actual type of RHS on error when LHS is a pattern with a type annotation --- .../Checking/Expressions/CheckExpressions.fs | 13 +++++++++++++ .../ErrorMessages/TypeMismatchTests.fs | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index c0d29359266..bb7bcdadf00 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -11152,6 +11152,19 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt // If binding a ctor then set the ugly counter that permits us to write ctor expressions on the r.h.s. let isCtor = (match memberFlagsOpt with Some memberFlags -> memberFlags.MemberKind = SynMemberKind.Constructor | _ -> false) + // For bindings with a type annotation, the parser wraps the RHS in SynExpr.Typed. + // Unwrap it and unify the annotation with the binding type separately so that + // type errors on the RHS report the actual expression type, not the annotation type. + let rhsExpr = + match rtyOpt, rhsExpr with + | Some (SynBindingReturnInfo(typeName = retInfoTy; range = mRetTy)), SynExpr.Typed(innerExpr, _, _) when spatsL.IsEmpty -> + let retTy, _ = TcTypeAndRecover cenv NewTyparsOK CheckCxs ItemOccurrence.UseInType WarnOnIWSAM.Yes envinner tpenv retInfoTy + try UnifyTypes cenv envinner mRetTy overallExprTy retTy + with RecoverableException exn -> errorRecovery exn mRetTy + innerExpr + | _ -> + rhsExpr + // Now check the right of the binding. // // At each module binding, dive into the expression to check for syntax errors and suppress them if they show. diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs index d645f4d5a92..738d53b0dc5 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs @@ -370,3 +370,17 @@ let main args = (Error 1, Line 8, Col 25, Line 8, Col 37, "The tuples have differing lengths of 3 and 2") ] + [] + let ``Binding with type annotation and tuple pattern reports correct type``() = + FSharp """ +let a, b: int = () + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 2, Col 11, Line 2, Col 14, + "This expression was expected to have type\n ''a * 'b' \nbut here has type\n 'int' ") + (Error 1, Line 2, Col 17, Line 2, Col 19, + "This expression was expected to have type\n ''a * 'b' \nbut here has type\n 'unit' ") + ] + From 04add67bc2b93f41f027eeb6f31d5d7bd2c51c81 Mon Sep 17 00:00:00 2001 From: Travis Date: Thu, 12 Feb 2026 14:26:43 -0600 Subject: [PATCH 2/3] Add release note for type reporting on pattern binding with type annotation --- docs/release-notes/.FSharp.Compiler.Service/9.0.200.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md index 5fd7be2081e..aa2551e0245 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md @@ -23,6 +23,7 @@ * Shim/file system: fix leaks of the shim [PR #18144](https://github.com/dotnet/fsharp/pull/18144) * fsi: fix auto-loading of script file inside NuGet package ([PR #18177](https://github.com/dotnet/fsharp/pull/18177)) * Fix for `Obsolete` attribute warning/error not taken into account when used with a unit of measure [PR #18182](https://github.com/dotnet/fsharp/pull/18182) +* Fix incorrect type reported for RHS of pattern binding when there's a type annotation on the pattern [PR #19284](https://github.com/dotnet/fsharp/pull/19284) ### Added From 25a463661c4cf897ac4d3b7f573c8f4b0e6e779c Mon Sep 17 00:00:00 2001 From: Travis Date: Thu, 12 Feb 2026 16:22:51 -0600 Subject: [PATCH 3/3] Highlight pattern range instead of annotation range --- src/Compiler/Checking/Expressions/CheckExpressions.fs | 2 +- .../ErrorMessages/TypeMismatchTests.fs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index bb7bcdadf00..e6605655cfc 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -11159,7 +11159,7 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt match rtyOpt, rhsExpr with | Some (SynBindingReturnInfo(typeName = retInfoTy; range = mRetTy)), SynExpr.Typed(innerExpr, _, _) when spatsL.IsEmpty -> let retTy, _ = TcTypeAndRecover cenv NewTyparsOK CheckCxs ItemOccurrence.UseInType WarnOnIWSAM.Yes envinner tpenv retInfoTy - try UnifyTypes cenv envinner mRetTy overallExprTy retTy + try UnifyTypes cenv envinner pat.Range retTy overallExprTy with RecoverableException exn -> errorRecovery exn mRetTy innerExpr | _ -> diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs index 738d53b0dc5..9f2abf6d52f 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs @@ -378,8 +378,8 @@ let a, b: int = () |> typecheck |> shouldFail |> withDiagnostics [ - (Error 1, Line 2, Col 11, Line 2, Col 14, - "This expression was expected to have type\n ''a * 'b' \nbut here has type\n 'int' ") + (Error 1, Line 2, Col 5, Line 2, Col 9, + "This expression was expected to have type\n 'int' \nbut here has type\n ''a * 'b' ") (Error 1, Line 2, Col 17, Line 2, Col 19, "This expression was expected to have type\n ''a * 'b' \nbut here has type\n 'unit' ") ]