diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md index a3949c5b823..3f356fcc03a 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md @@ -11,6 +11,7 @@ * Fix find all references for F# exceptions ([PR #18565](https://github.com/dotnet/fsharp/pull/18565)) * Shorthand lambda: fix completion for chained calls and analysis for unfinished expression ([PR #18560](https://github.com/dotnet/fsharp/pull/18560)) * Completion: fix previous namespace considered opened [PR #18609](https://github.com/dotnet/fsharp/pull/18609) +* Fix active pattern typechecking regression. ([Issue #18638](https://github.com/dotnet/fsharp/issues/18638), [PR #18642](https://github.com/dotnet/fsharp/pull/18642)) ### Added diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 4ce2fb1700b..e1410e8c595 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -5259,6 +5259,8 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags | _, _ -> FSComp.SR.tcActivePatternArgsCountNotMatchArgsAndPat(paramCount, caseName, fmtExprArgs paramCount) error(Error(msg, m)) + let isUnsolvedTyparTy g ty = tryDestTyparTy g ty |> ValueOption.exists (fun typar -> not typar.IsSolved) + // partial active pattern (returning bool) doesn't have output arg if (not apinfo.IsTotal && isBoolTy g retTy) then checkLanguageFeatureError g.langVersion LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern m @@ -5281,7 +5283,8 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags showErrMsg 1 // active pattern in function param (e.g. let f (|P|_|) = ...) - elif tryDestTyparTy g vExprTy |> ValueOption.exists (fun typar -> not typar.IsSolved) then + // or in mutual recursion with a lambda: `and (|P|) x = fun y -> …` + elif isUnsolvedTyparTy g vExprTy || tryDestFunTy g vExprTy |> ValueOption.exists (fun (_, rangeTy) -> isUnsolvedTyparTy g rangeTy) then List.frontAndBack args // args count should equal to AP function params count diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs index 8c2133721d2..09de043f4fd 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs @@ -761,6 +761,26 @@ match 1 with P expr2 pat -> () |> typecheck |> shouldSucceed + module ``Recursive active pattern definition with and`` = + /// See https://github.com/dotnet/fsharp/issues/18638 + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let rec parse p = + function + | IsSomething p v -> Some v + | _ -> None + +and (|IsSomething|_|) p = + function + | "nested" -> parse p "42" + | "42" -> Some 42 + | _ -> None + """ + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + module ``int → int → int voption`` = // Normal usage; pat is int. []