From b40740aea13f975f19d1807026630ec53f706cc4 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 27 Dec 2022 12:18:21 +0100 Subject: [PATCH 01/18] Broken code for trywith in seq --- .../Checking/CheckComputationExpressions.fs | 23 +++++++++++++-- .../FSharp.Compiler.ComponentTests.fsproj | 1 + .../Language/SequenceExpressionTests.fs | 29 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index db8d307381f..b0f2fbce17c 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -2075,8 +2075,27 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = Some(mkLet spMatch inputExprMark matchv inputExpr matchExpr, tpenv) - | SynExpr.TryWith (trivia={ TryToWithRange = mTryToWith }) -> - error(Error(FSComp.SR.tcTryIllegalInSequenceExpression(), mTryToWith)) + | SynExpr.TryWith (innerTry,withList,mTryToWith,spTry,spWith,trivia) -> + let env = { env with eIsControlFlow = true } + let innerExpr, tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry + let withExpr, tpenv = TcExpr cenv (MustEqual cenv.g.unit_ty) env tpenv unwindExpr + + // We attach the debug points to the lambda expressions so we can fetch it out again in LowerComputedListOrArraySeqExpr + let mTry = + match spTry with + | DebugPointAtTry.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Try) + | _ -> trivia.TryKeyword + + let mWith = + match spWith with + | DebugPointAtWith.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Finally) + | _ -> trivia.WithKeyword + + let innerExpr = mkSeqDelayedExpr mTry innerExpr + let unwindExpr = mkUnitDelayLambda cenv.g mFinally unwindExpr + mkTryWith cenv.g + Some(mkSeqFinally cenv env mTryToLast genOuterTy innerExpr unwindExpr, tpenv) + //error(Error(FSComp.SR.tcTryIllegalInSequenceExpression(), mTryToWith)) | SynExpr.YieldOrReturnFrom ((isYield, _), synYieldExpr, m) -> let env = { env with eIsControlFlow = false } diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 1262a9dc28a..22bc54d8cab 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -177,6 +177,7 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs new file mode 100644 index 00000000000..575a236c9c9 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Compiler.ComponentTests.Language.SequenceExpressionTests + +open Xunit +open FSharp.Test.Compiler + + +[] +let ``A sequence expression can yield from with clause``() = + FSharp """ +module SequenceTryWithTest +let sum = + seq { + for x in [0;1] do + try + yield (10 / x) + with _ -> + yield 100 + } + |> Seq.sum +if sum <> 110 then + failwith $"Sum was {sum} instead" + """ + |> compileAndRun + |> shouldSucceed + |> ignore + + \ No newline at end of file From 3fd3ccb4ad1c23f8a59d51c1871b6d720c6102fb Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 3 Jan 2023 14:47:10 +0100 Subject: [PATCH 02/18] try/with for sequence expressions - failing, but building --- .../Checking/CheckComputationExpressions.fs | 41 +++++++------ .../Language/SequenceExpressionTests.fs | 61 +++++++++++++++++++ 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index c1147cf1006..589eb90351f 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -2081,25 +2081,28 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = | SynExpr.TryWith (innerTry,withList,mTryToWith,spTry,spWith,trivia) -> let env = { env with eIsControlFlow = true } - let innerExpr, tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry - let withExpr, tpenv = TcExpr cenv (MustEqual cenv.g.unit_ty) env tpenv unwindExpr - - // We attach the debug points to the lambda expressions so we can fetch it out again in LowerComputedListOrArraySeqExpr - let mTry = - match spTry with - | DebugPointAtTry.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Try) - | _ -> trivia.TryKeyword - - let mWith = - match spWith with - | DebugPointAtWith.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Finally) - | _ -> trivia.WithKeyword - - let innerExpr = mkSeqDelayedExpr mTry innerExpr - let unwindExpr = mkUnitDelayLambda cenv.g mFinally unwindExpr - mkTryWith cenv.g - Some(mkSeqFinally cenv env mTryToLast genOuterTy innerExpr unwindExpr, tpenv) - //error(Error(FSComp.SR.tcTryIllegalInSequenceExpression(), mTryToWith)) + let tryExpr, tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry + + // Compile the pattern twice, once as a List.filter with all succeeding targets returning "1", and once as a proper catch block. + let clauses, tpenv = + (tpenv, withList) ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, m, sp, trivia)) -> + let patR, condR, vspecs, envinner, tpenv = TcMatchPattern cenv g.exn_ty env tpenv pat cond + let envinner = + match sp with + | DebugPointAtTarget.Yes -> { envinner with eIsControlFlow = true } + | DebugPointAtTarget.No -> envinner + let matchBody, tpenv = tcSequenceExprBody envinner genOuterTy tpenv innerComp + let handlerClause = MatchClause(patR, condR, TTarget(vspecs, matchBody, None), patR.Range) + let filterClause = MatchClause(patR, condR, TTarget([], Expr.Const(Const.Int32 1,m,g.int_ty), None), patR.Range) + (handlerClause,filterClause), tpenv) + + let handlers, filterClauses = List.unzip clauses + let withRange = trivia.WithToEndRange + let v1, filterExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses + let v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true Rethrow None g.exn_ty genOuterTy handlers + let finalExpr = mkTryWith g (tryExpr, v1, filterExpr, v2, handlerExpr, mTryToWith, genOuterTy, spTry, spWith) + + Some(finalExpr,tpenv) | SynExpr.YieldOrReturnFrom ((isYield, _), synYieldExpr, m) -> let env = { env with eIsControlFlow = false } diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 575a236c9c9..73feb5b47fe 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -26,4 +26,65 @@ if sum <> 110 then |> shouldSucceed |> ignore +[] +let ``A sequence expression can yield from try and have empty with``() = + FSharp """ +module SequenceTryWithTest +let sum = + seq { + for x in [1;0] do + try + yield (10 / x) + with _ -> + printfn "Crash" + } + |> Seq.sum +if sum <> 10 then + failwith $"Sum was {sum} instead" + """ + |> compileAndRun + |> shouldSucceed + |> ignore + +[] +let ``A sequence expression can yield from with and have empty try``() = + FSharp """ +module SequenceTryWithTest +let sum = + seq { + for x in [1;0] do + try + let result = (10 / x) + printfn "%A" result + with _ -> + yield 100 + } + |> Seq.sum +if sum <> 100 then + failwith $"Sum was {sum} instead" + """ + |> compileAndRun + |> shouldSucceed + |> ignore + + +[] +let ``A sequence expression can have implicit yields in try-with``() = + FSharp """ +module SequenceTryWithTest +let sum = + seq { + for x in [0;1] do + try + (10 / x) + with _ -> + 100 + } + |> Seq.sum +if sum <> 110 then + failwith $"Sum was {sum} instead" + """ + |> compileAndRun + |> shouldSucceed + |> ignore \ No newline at end of file From 8f4586457f5b6bef71014ae4c128cc851bd2b379 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 3 Jan 2023 16:34:58 +0100 Subject: [PATCH 03/18] Adding more tests --- .../Checking/CheckComputationExpressions.fs | 2 +- .../Language/SequenceExpressionTests.fs | 44 ++++++++++++++----- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index 589eb90351f..ac20f351609 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -2085,7 +2085,7 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = // Compile the pattern twice, once as a List.filter with all succeeding targets returning "1", and once as a proper catch block. let clauses, tpenv = - (tpenv, withList) ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, m, sp, trivia)) -> + (tpenv, withList) ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, m, sp, _)) -> let patR, condR, vspecs, envinner, tpenv = TcMatchPattern cenv g.exn_ty env tpenv pat cond let envinner = match sp with diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 73feb5b47fe..06ccb9e6462 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -8,8 +8,7 @@ open FSharp.Test.Compiler [] let ``A sequence expression can yield from with clause``() = - FSharp """ -module SequenceTryWithTest + Fsx """ let sum = seq { for x in [0;1] do @@ -22,14 +21,13 @@ let sum = if sum <> 110 then failwith $"Sum was {sum} instead" """ + |> asExe |> compileAndRun |> shouldSucceed - |> ignore [] let ``A sequence expression can yield from try and have empty with``() = - FSharp """ -module SequenceTryWithTest + Fsx """ let sum = seq { for x in [1;0] do @@ -42,14 +40,13 @@ let sum = if sum <> 10 then failwith $"Sum was {sum} instead" """ + |> asExe |> compileAndRun |> shouldSucceed - |> ignore [] let ``A sequence expression can yield from with and have empty try``() = - FSharp """ -module SequenceTryWithTest + Fsx """ let sum = seq { for x in [1;0] do @@ -63,15 +60,14 @@ let sum = if sum <> 100 then failwith $"Sum was {sum} instead" """ + |> asExe |> compileAndRun |> shouldSucceed - |> ignore [] let ``A sequence expression can have implicit yields in try-with``() = - FSharp """ -module SequenceTryWithTest + Fsx """ let sum = seq { for x in [0;1] do @@ -84,7 +80,31 @@ let sum = if sum <> 110 then failwith $"Sum was {sum} instead" """ + |> asExe |> compileAndRun |> shouldSucceed - |> ignore + +[] +[] +[] +[] +[] +[] +[] +[] +[] +let ``Typecheck mismatch between try and match return types``(valInTry,valInWith1,valInWith2) = + Fsx $""" +let typedSeq = + seq {{ + for x in [0;1] do + try + %s{valInTry} + with + |_ when x = 0 -> %s{valInWith1} + |_ when x = 0 -> %s{valInWith2} + }} + """ + |> typecheck + |> shouldSucceed \ No newline at end of file From 2cff81fc59c97171a17047c59bf941a29a0d339a Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 3 Jan 2023 16:55:48 +0100 Subject: [PATCH 04/18] Failing test added - error at enumeration time not being caught! --- .../Language/SequenceExpressionTests.fs | 91 ++++++++++++++++++- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 06ccb9e6462..253562db834 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -25,6 +25,46 @@ if sum <> 110 then |> compileAndRun |> shouldSucceed +[] +let ``A sequence expression can yield! from with clause``() = + Fsx """ +let sum = + seq { + for x in [0;1] do + try + yield (10 / x) + with _ -> + yield! seq{1;2;3} + } + |> Seq.sum +if sum <> 16 then + failwith $"Sum was {sum} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + +[] +let ``A sequence expression can do multiple yields from try/with clause``() = + Fsx """ +let sum = + seq { + for x in [0;1] do + try + yield 1 // Should work both times before failure + yield! seq{ (10 / x);2} // Will crash for 0 + with _ -> + yield 100 + yield 100 + } + |> Seq.sum +if sum <> (1+100+100+1+10+2) then + failwith $"Sum was {sum} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + [] let ``A sequence expression can yield from try and have empty with``() = Fsx """ @@ -85,15 +125,11 @@ if sum <> 110 then |> shouldSucceed [] -[] -[] -[] [] [] [] [] -[] -let ``Typecheck mismatch between try and match return types``(valInTry,valInWith1,valInWith2) = +let ``Propper type matching in seq{try/with}``(valInTry,valInWith1,valInWith2) = Fsx $""" let typedSeq = seq {{ @@ -107,4 +143,49 @@ let typedSeq = """ |> typecheck |> shouldSucceed + +[] +[] +[] +[] +let ``Type mismatch error in seq{try/with}``(valInTry,valInWith1,valInWith2) = + Fsx $""" +let typedSeq = + seq {{ + for x in [0;1] do + try + %s{valInTry} + with + |_ when x = 0 -> %s{valInWith1} + |_ when x = 0 -> %s{valInWith2} + }} + """ + |> typecheck + |> shouldFail + |> withErrorCode 193 + |> withDiagnosticMessageMatches "Type constraint mismatch" + + +[] +let printCode = """ printfn "Hello there" """ + +[] +[] +[] +let ``Missing result type in seq{try/with}``(valInTry,valInWith1,valInWith2) = + Fsx $""" +let typedSeq = + seq {{ + for x in [0;1] do + try + %s{valInTry} + with + |_ when x = 0 -> %s{valInWith1} + |_ when x = 0 -> %s{valInWith2} + }} + """ + |> typecheck + |> shouldFail + |> withErrorCode 30 + |> withDiagnosticMessageMatches "Value restriction. The value 'typedSeq' has been inferred to have generic type" \ No newline at end of file From e3dc32dbbf2d4cbf614cd90ca74067f19521453e Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 3 Jan 2023 17:15:39 +0100 Subject: [PATCH 05/18] More tailored failing tests added --- .../Language/SequenceExpressionTests.fs | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 253562db834..4e36045d6a7 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -44,6 +44,24 @@ if sum <> 16 then |> compileAndRun |> shouldSucceed +[] +let ``A sequence expression can fail later in try/with and still get caught``() = + Fsx """ +let sum = + seq { + try + yield 1 + yield (10 / 0) + with _ -> () + } + |> Seq.sum +if sum <> (1) then + failwith $"Sum was {sum} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + [] let ``A sequence expression can do multiple yields from try/with clause``() = Fsx """ @@ -52,7 +70,8 @@ let sum = for x in [0;1] do try yield 1 // Should work both times before failure - yield! seq{ (10 / x);2} // Will crash for 0 + yield (10/x) // Will crash for 0 + yield 2 // Will only get there for 1 with _ -> yield 100 yield 100 @@ -129,7 +148,7 @@ if sum <> 110 then [] [] [] -let ``Propper type matching in seq{try/with}``(valInTry,valInWith1,valInWith2) = +let ``Propper type matching in seq{try/with} with implicit yield``(valInTry,valInWith1,valInWith2) = Fsx $""" let typedSeq = seq {{ @@ -144,6 +163,45 @@ let typedSeq = |> typecheck |> shouldSucceed +[] +[] +[] +let ``seq{try/with} using yield or implicit must be consistent``(valInTry,valInWith1,valInWith2) = + Fsx $""" +let typedSeq = + seq {{ + for x in [0;1] do + try + %s{valInTry} + with + |_ when x = 0 -> %s{valInWith1} + |_ when x = 0 -> %s{valInWith2} + }} + """ + |> typecheck + |> shouldSucceed + +[] +[] +[] +let ``seq{try/with} mismatch implicit vs. yield``(valInTry,valInWith1,valInWith2) = + Fsx $""" +let typedSeq = + seq {{ + for x in [0;1] do + try + %s{valInTry} + with + |_ when x = 0 -> %s{valInWith1} + |_ when x = 0 -> %s{valInWith2} + }} + """ + |> typecheck + |> shouldFail + |> withErrorCode 3221 + |> withDiagnosticMessageMatches "This expression returns a value of type 'int' but is implicitly discarded." + |> withDiagnosticMessageMatches "If you intended to use the expression as a value in the sequence then use an explicit 'yield'." + [] [] [] From 81695552929e4e098acb6a16c8b314f7caad3187 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 3 Jan 2023 19:02:44 +0100 Subject: [PATCH 06/18] pushing 'with' clause into the generated state machine for chained expressions --- .../Checking/CheckComputationExpressions.fs | 12 +++- .../Language/SequenceExpressionTests.fs | 65 +++++++++++++++++-- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index ac20f351609..3216980b430 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -2081,7 +2081,17 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = | SynExpr.TryWith (innerTry,withList,mTryToWith,spTry,spWith,trivia) -> let env = { env with eIsControlFlow = true } - let tryExpr, tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry + let tryExpr, tpenv = + // If we have multiple yields/implicit below each other, we want to keep the results before failure + // Therefore, each 'Sequential' (expr1;expr2) can either fail the first one (outer with caters for it) + // -OR-, the first expr can suceed and yield something, and only the second fails - we add an artificial 'with' + let liftedInnerSynExpr = + match innerTry with + | SynExpr.Sequential(sp, isTrueSeq, innerComp1, innerComp2, m) -> + let wrappedComp2 = SynExpr.TryWith(innerComp2,withList,m,spTry,spWith,trivia) + SynExpr.Sequential(sp, isTrueSeq, innerComp1, wrappedComp2, m) + | other -> other + tcSequenceExprBody env genOuterTy tpenv liftedInnerSynExpr // Compile the pattern twice, once as a List.filter with all succeeding targets returning "1", and once as a proper catch block. let clauses, tpenv = diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 4e36045d6a7..1c2425a256c 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -6,6 +6,36 @@ open Xunit open FSharp.Test.Compiler +[] +let ``A seq{try/with} happy path with multiple language elements``() = + Fsx """ +let rec mySeq inputEnumerable = + seq { + for x in inputEnumerable do + try + match x with + | 0 -> yield 1 // - Single value + | 1 -> yield! (mySeq [0;3;6]) // - Recursion + | 2 -> () // - Empty + | 3 -> failwith "This should get caught!" // - Generic exn throw + | 4 -> yield (4/0) // - Specific exn throw + | 5 -> + yield 5 // - Two yields, will be a state machine + yield 5 + | _ -> failwith "This should get caught!" + with + | :? System.DivideByZeroException -> yield 4 // - Specific exn + | anyOther when x = 3 -> yield 3 // - Generic exn using 'x', no yield + | anyOther when x = 6 -> () // - Empty yield from 'with' clause + } + +if (mySeq [0..5] |> Seq.sum) <> (1+(1+3)+3+4+5+5) then + failwith $"Sum was {(mySeq [0..5] |> Seq.sum)} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + [] let ``A sequence expression can yield from with clause``() = Fsx """ @@ -49,13 +79,35 @@ let ``A sequence expression can fail later in try/with and still get caught``() Fsx """ let sum = seq { - try + try yield 1 - yield (10 / 0) - with _ -> () + yield 2 + yield 3 + yield (10/0) // This will crash + yield 4 // This will never be reached + with _ -> () + } + |> Seq.sum +if sum <> (1+2+3) then + failwith $"Sum was {sum} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + +[] +let ``A sequence expression can have inner seq{try/with} in an outer try/with``() = + Fsx """ +let sum = + seq { + try + yield 1 + yield! seq{ try (10 / 0) with _ -> 1} + yield 1 + with _ -> yield 100000 // will not get hit, covered by inner 'with' } |> Seq.sum -if sum <> (1) then +if sum <> (1+1+1) then failwith $"Sum was {sum} instead" """ |> asExe @@ -184,7 +236,7 @@ let typedSeq = [] [] [] -let ``seq{try/with} mismatch implicit vs. yield``(valInTry,valInWith1,valInWith2) = +let ``seq{try/with} mismatch implicit vs yield``(valInTry,valInWith1,valInWith2) = Fsx $""" let typedSeq = seq {{ @@ -197,8 +249,7 @@ let typedSeq = }} """ |> typecheck - |> shouldFail - |> withErrorCode 3221 + |> shouldFail |> withDiagnosticMessageMatches "This expression returns a value of type 'int' but is implicitly discarded." |> withDiagnosticMessageMatches "If you intended to use the expression as a value in the sequence then use an explicit 'yield'." From e1326567174c10bd717cac10497a0a21db638da9 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 4 Jan 2023 15:20:54 +0100 Subject: [PATCH 07/18] More test cases - recursion from with, inner try-finally inside of with --- .../Language/SequenceExpressionTests.fs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 1c2425a256c..16cc7168222 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -36,6 +36,53 @@ if (mySeq [0..5] |> Seq.sum) <> (1+(1+3)+3+4+5+5) then |> compileAndRun |> shouldSucceed +[] +let ``Inner try-finally's Dispose is executed before yielding from outer try-with``() = + Fsx """ +let mutable l = [] +let s() = seq { + try + try + l <- "Before try" :: l + yield (1/0) + l <- "After crash should never happen" :: l + finally + l <- "Inside finally" :: l + with ex when (l <- "Inside with pattern" :: l;true) -> + l <- "Inside with body" :: l + yield 1 + l <- "End of with body" :: l +} +let totalSum = s() |> Seq.sum +if totalSum <> 1 then + failwith $"Sum was {{totalSum}} instead" + +failwith $"List is %A{l}" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + +[] +[] +[] +let ``A sequence expression can recurse itself from with clause``(recLevel:int) = + Fsx $""" +let rec f () = seq {{ + try + yield 1 + yield (1/0) + with pat -> + yield! f() +}} +let topNsum = f() |> Seq.take {recLevel} |> Seq.sum +if topNsum <> {recLevel} then + failwith $"Sum was {{topNsum}} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + [] let ``A sequence expression can yield from with clause``() = Fsx """ @@ -55,6 +102,46 @@ if sum <> 110 then |> compileAndRun |> shouldSucceed +[] +let ``A sequence expression can have try-with around foreach``() = + Fsx """ +let mySeq (providedInput: seq) = + seq { + try + for x in providedInput do + yield (6 / x) + with _ -> + yield 100 + } +let mySum = (mySeq [3;2;1;0]) |> Seq.sum +if mySum <> 100 then + failwith $"Sum was {mySum} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + +[] +let ``A sequence expression can have try-with around while``() = + Fsx """ +let mySeq () = + seq { + let mutable x = 3 + try + while true do + yield (6/x) + x <- x-1 + with _ -> + yield 100 + } +let mySum = (mySeq () |> Seq.take 10) |> Seq.sum +if mySum <> (6/3 + 6/2 + 6/1 + 100) then + failwith $"Sum was {mySum} instead" + """ + |> asExe + |> compileAndRun + |> shouldSucceed + [] let ``A sequence expression can yield! from with clause``() = Fsx """ From 0b1a8549e1cf66d234dd6b11cb171754dfddf51f Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 4 Jan 2023 19:06:22 +0100 Subject: [PATCH 08/18] combinator for trywith added, not yet hooked up --- src/Compiler/TypedTree/TcGlobals.fs | 6 +- src/FSharp.Core/seqcore.fs | 64 +++++++++++++++++++ src/FSharp.Core/seqcore.fsi | 11 ++++ .../Language/SequenceExpressionTests.fs | 46 ++++++------- tests/FSharp.Test.Utilities/Compiler.fs | 17 +++-- 5 files changed, 110 insertions(+), 34 deletions(-) diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index dd2f6e70f9e..349d988fd42 100755 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -764,6 +764,7 @@ type TcGlobals( let v_seq_using_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "EnumerateUsing" , None , None , [vara;varb;varc], ([[varaTy];[(varaTy --> varbTy)]], mkSeqTy varcTy)) let v_seq_generated_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "EnumerateWhile" , None , None , [varb], ([[v_unit_ty --> v_bool_ty]; [mkSeqTy varbTy]], mkSeqTy varbTy)) let v_seq_finally_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "EnumerateThenFinally" , None , None , [varb], ([[mkSeqTy varbTy]; [v_unit_ty --> v_unit_ty]], mkSeqTy varbTy)) + let v_seq_trywith_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "EnumerateTryWith" , None , None , [varb], ([[mkSeqTy varbTy]; [mkNonGenericTy v_exn_tcr --> v_int32_ty]; [mkNonGenericTy v_exn_tcr --> mkSeqTy varbTy]], mkSeqTy varbTy)) let v_seq_of_functions_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "EnumerateFromFunctions" , None , None , [vara;varb], ([[v_unit_ty --> varaTy]; [varaTy --> v_bool_ty]; [varaTy --> varbTy]], mkSeqTy varbTy)) let v_create_event_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "CreateEvent" , None , None , [vara;varb], ([[varaTy --> v_unit_ty]; [varaTy --> v_unit_ty]; [(v_obj_ty --> (varbTy --> v_unit_ty)) --> varaTy]], mkIEvent2Ty varaTy varbTy)) let v_cgh__useResumableCode_info = makeIntrinsicValRef(fslib_MFStateMachineHelpers_nleref, "__useResumableCode" , None , None , [vara], ([[]], v_bool_ty)) @@ -1624,8 +1625,8 @@ type TcGlobals( member val query_select_vref = ValRefForIntrinsic v_query_select_value_info member val query_where_vref = ValRefForIntrinsic v_query_where_value_info member val query_zero_vref = ValRefForIntrinsic v_query_zero_value_info - member val seq_to_list_vref = ValRefForIntrinsic v_seq_to_list_info - member val seq_to_array_vref = ValRefForIntrinsic v_seq_to_array_info + member val seq_to_list_vref = ValRefForIntrinsic v_seq_to_list_info + member val seq_to_array_vref = ValRefForIntrinsic v_seq_to_array_info member _.seq_collect_info = v_seq_collect_info member _.seq_using_info = v_seq_using_info @@ -1633,6 +1634,7 @@ type TcGlobals( member _.seq_append_info = v_seq_append_info member _.seq_generated_info = v_seq_generated_info member _.seq_finally_info = v_seq_finally_info + member _.seq_trywith_info = v_seq_trywith_info member _.seq_of_functions_info = v_seq_of_functions_info member _.seq_map_info = v_seq_map_info member _.seq_singleton_info = v_seq_singleton_info diff --git a/src/FSharp.Core/seqcore.fs b/src/FSharp.Core/seqcore.fs index 2b2b3b9e55b..e3c0a24103f 100644 --- a/src/FSharp.Core/seqcore.fs +++ b/src/FSharp.Core/seqcore.fs @@ -390,6 +390,70 @@ module RuntimeHelpers = let EnumerateThenFinally (source: seq<'T>) (compensation: unit -> unit) = (FinallyEnumerable(compensation, (fun () -> source)) :> seq<_>) + + let EnumerateTryWith (source : seq<'T>) (exceptionFilter:exn -> int) (exceptionHandler:exn -> seq<'T>) = + let originalSource = lazy(source.GetEnumerator()) + let mutable shouldDisposeOriginalAtTheEnd = true + let mutable exceptionalSource : option> = None + + let current() = + match exceptionalSource with + | Some es -> es.Current + | None -> originalSource.Value.Current + + let disposeOriginal() = + if shouldDisposeOriginalAtTheEnd = true then + shouldDisposeOriginalAtTheEnd <- false + originalSource.Value.Dispose() + + let moveExceptionHandler(exn) = + exceptionalSource <- Some ((exceptionHandler exn).GetEnumerator()) + exceptionalSource.Value.MoveNext() + + let tryIfDisposalLeadsToExceptionHandlerSeq() = + try + disposeOriginal() + false + with + | e when exceptionFilter e = 1 -> moveExceptionHandler(e) + + (mkSeq (fun () -> + { new IEnumerator<_> with + member x.Current = current() + + interface IEnumerator with + member x.Current = box (current()) + + [] + member x.MoveNext() = + match exceptionalSource with + | Some es -> es.MoveNext() + | None -> + try + let hasNext = originalSource.Value.MoveNext() + if not hasNext then + // What if Moving does not fail, but Disposing does? + // In that case, the 'when' guards could actually produce new elements to by yielded + // Let's try it. If the Dispose() call below fails and gets caught by the guards, enumeration might continue + disposeOriginal() + hasNext + with + // Try .Dispose() original. If that fails && also matches with guards, let's use the exn from. Dispose() call for next enumeration + | _ when tryIfDisposalLeadsToExceptionHandlerSeq() -> true + // We go here when either original's disposal not fail, or failed but with an unmatched exception + | e when exceptionFilter e = 1 -> moveExceptionHandler(e) + + member x.Reset() = IEnumerator.noReset() + + interface System.IDisposable with + member x.Dispose() = + match exceptionalSource with + | Some es -> es.Dispose() + // We are no longer at a phase where anyone should be calling .MoveNext() + // This will only happen if someone is doing MoveNext()+Dispose() calls manually and decides to .Dispose() before + // Enumeration has finished. In this case, we do NOT invoke the exception handlers for the .Dispose() call + | None -> disposeOriginal()})) + let CreateEvent (addHandler : 'Delegate -> unit) (removeHandler : 'Delegate -> unit) (createHandler : (obj -> 'Args -> unit) -> 'Delegate ) :IEvent<'Delegate,'Args> = { new obj() with member x.ToString() = "" diff --git a/src/FSharp.Core/seqcore.fsi b/src/FSharp.Core/seqcore.fsi index 701a92fc705..81d4d250d76 100644 --- a/src/FSharp.Core/seqcore.fsi +++ b/src/FSharp.Core/seqcore.fsi @@ -84,6 +84,17 @@ module RuntimeHelpers = /// The result sequence. val EnumerateThenFinally: source: seq<'T> -> compensation: (unit -> unit) -> seq<'T> + + /// The F# compiler emits calls to this function to + /// implement the try/with operator for F# sequence expressions. + /// + /// The input sequence. + /// Pattern matches after 'when' converted to return 1 + /// Pattern matches after 'when' with their actual execution code + /// + /// The result sequence. + val EnumerateTryWith: source : seq<'T> -> exceptionFilter: (exn -> int) -> exceptionHandler: (exn -> seq<'T>) -> seq<'T> + /// The F# compiler emits calls to this function to implement the compiler-intrinsic /// conversions from untyped IEnumerable sequences to typed sequences. /// diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 16cc7168222..b7df6995693 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -6,6 +6,11 @@ open Xunit open FSharp.Test.Compiler + +let fsiSession = getSessionForEval() + +let runCode = evalInSharedSession fsiSession + [] let ``A seq{try/with} happy path with multiple language elements``() = Fsx """ @@ -32,8 +37,7 @@ let rec mySeq inputEnumerable = if (mySeq [0..5] |> Seq.sum) <> (1+(1+3)+3+4+5+5) then failwith $"Sum was {(mySeq [0..5] |> Seq.sum)} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -59,13 +63,12 @@ if totalSum <> 1 then failwith $"List is %A{l}" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] [] -[] +[] let ``A sequence expression can recurse itself from with clause``(recLevel:int) = Fsx $""" let rec f () = seq {{ @@ -79,8 +82,7 @@ let topNsum = f() |> Seq.take {recLevel} |> Seq.sum if topNsum <> {recLevel} then failwith $"Sum was {{topNsum}} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -98,8 +100,7 @@ let sum = if sum <> 110 then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -117,8 +118,7 @@ let mySum = (mySeq [3;2;1;0]) |> Seq.sum if mySum <> 100 then failwith $"Sum was {mySum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -138,8 +138,7 @@ let mySum = (mySeq () |> Seq.take 10) |> Seq.sum if mySum <> (6/3 + 6/2 + 6/1 + 100) then failwith $"Sum was {mySum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -157,8 +156,7 @@ let sum = if sum <> 16 then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -178,8 +176,7 @@ let sum = if sum <> (1+2+3) then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -197,8 +194,7 @@ let sum = if sum <> (1+1+1) then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -219,8 +215,7 @@ let sum = if sum <> (1+100+100+1+10+2) then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -238,8 +233,7 @@ let sum = if sum <> 10 then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] @@ -258,8 +252,7 @@ let sum = if sum <> 100 then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed @@ -278,8 +271,7 @@ let sum = if sum <> 110 then failwith $"Sum was {sum} instead" """ - |> asExe - |> compileAndRun + |> runCode |> shouldSucceed [] diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 8ea26ea602b..f7836031b05 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -787,11 +787,8 @@ module rec Compiler = let compileExeAndRun = asExe >> compileAndRun - let private evalFSharp (fs: FSharpCompilationSource) : CompilationResult = + let private evalFSharp (fs: FSharpCompilationSource) (script:FSharpScript) : CompilationResult = let source = fs.Source.GetSourceText |> Option.defaultValue "" - let options = fs.Options |> Array.ofList - - use script = new FSharpScript(additionalArgs=options) let (evalResult: Result), (err: FSharpDiagnostic[]) = script.Eval(source) let diagnostics = err |> fromFSharpDiagnostic let result = @@ -811,7 +808,17 @@ module rec Compiler = let eval (cUnit: CompilationUnit) : CompilationResult = match cUnit with - | FS fs -> evalFSharp fs + | FS fs -> + let options = fs.Options |> Array.ofList + use script = new FSharpScript(additionalArgs=options) + evalFSharp fs script + | _ -> failwith "Script evaluation is only supported for F#." + + let getSessionForEval () = new FSharpScript() + + let evalInSharedSession (script:FSharpScript) (cUnit: CompilationUnit) : CompilationResult = + match cUnit with + | FS fs -> evalFSharp fs script | _ -> failwith "Script evaluation is only supported for F#." let runFsi (cUnit: CompilationUnit) : CompilationResult = From e40e6f89f33e2fb91679e6f358becb8313af61ca Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 4 Jan 2023 20:08:27 +0100 Subject: [PATCH 09/18] Calling combinator from seqexpression translation (failing now, incorrect CLR program) --- .../Checking/CheckComputationExpressions.fs | 27 +++++++++++++------ src/Compiler/TypedTree/TypedTreeOps.fs | 3 +++ src/Compiler/TypedTree/TypedTreeOps.fsi | 2 ++ .../Language/SequenceExpressionTests.fs | 3 ++- tests/FSharp.Test.Utilities/Compiler.fs | 4 +++ 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index 3216980b430..06f630d5242 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -1854,6 +1854,13 @@ let mkSeqFinally (cenv: cenv) env m genTy e1 e2 = let e1 = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e1) e1 mkCallSeqFinally cenv.g m genResultTy e1 e2 +let mkSeqTryWith (cenv: cenv) env m genTy origSeq exnFilter exnHandler = + let g = cenv.g + let genResultTy = NewInferenceType g + UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) + let origSeq = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g origSeq) origSeq + mkCallSeqTryWith cenv.g m genResultTy origSeq exnFilter exnHandler + let mkSeqExprMatchClauses (pat, vspecs) innerExpr = [MatchClause(pat, None, TTarget(vspecs, innerExpr, None), pat.Range) ] @@ -2079,7 +2086,7 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = Some(mkLet spMatch inputExprMark matchv inputExpr matchExpr, tpenv) - | SynExpr.TryWith (innerTry,withList,mTryToWith,spTry,spWith,trivia) -> + | SynExpr.TryWith (innerTry,withList,mTryToWith,_spTry,_spWith,trivia) -> let env = { env with eIsControlFlow = true } let tryExpr, tpenv = // If we have multiple yields/implicit below each other, we want to keep the results before failure @@ -2087,9 +2094,9 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = // -OR-, the first expr can suceed and yield something, and only the second fails - we add an artificial 'with' let liftedInnerSynExpr = match innerTry with - | SynExpr.Sequential(sp, isTrueSeq, innerComp1, innerComp2, m) -> - let wrappedComp2 = SynExpr.TryWith(innerComp2,withList,m,spTry,spWith,trivia) - SynExpr.Sequential(sp, isTrueSeq, innerComp1, wrappedComp2, m) + //| SynExpr.Sequential(sp, isTrueSeq, innerComp1, innerComp2, m) -> + // let wrappedComp2 = SynExpr.TryWith(innerComp2,withList,m,spTry,spWith,trivia) + // SynExpr.Sequential(sp, isTrueSeq, innerComp1, wrappedComp2, m) | other -> other tcSequenceExprBody env genOuterTy tpenv liftedInnerSynExpr @@ -2108,11 +2115,15 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let handlers, filterClauses = List.unzip clauses let withRange = trivia.WithToEndRange - let v1, filterExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses - let v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true Rethrow None g.exn_ty genOuterTy handlers - let finalExpr = mkTryWith g (tryExpr, v1, filterExpr, v2, handlerExpr, mTryToWith, genOuterTy, spTry, spWith) + let _v1, filterExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses + //let _v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true Rethrow None g.exn_ty genOuterTy handlers + let _v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty genOuterTy handlers + let combinatorExpr = mkSeqTryWith cenv env mTryToWith genOuterTy tryExpr filterExpr handlerExpr + Some (combinatorExpr,tpenv) + + //let finalExpr = mkTryWith g (tryExpr, v1, filterExpr, v2, handlerExpr, mTryToWith, genOuterTy, spTry, spWith) - Some(finalExpr,tpenv) + //Some(finalExpr,tpenv) | SynExpr.YieldOrReturnFrom ((isYield, _), synYieldExpr, m) -> let env = { env with eIsControlFlow = false } diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 1ff908d445a..7a79e107462 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -7809,6 +7809,9 @@ let mkCallSeqGenerated g m elemTy arg1 arg2 = let mkCallSeqFinally g m elemTy arg1 arg2 = mkApps g (typedExprForIntrinsic g m g.seq_finally_info, [[elemTy]], [ arg1; arg2 ], m) + +let mkCallSeqTryWith g m elemTy origSeq exnFilter exnHandler = + mkApps g (typedExprForIntrinsic g m g.seq_trywith_info, [[elemTy]], [ origSeq; exnFilter; exnHandler ], m) let mkCallSeqOfFunctions g m ty1 ty2 arg1 arg2 arg3 = mkApps g (typedExprForIntrinsic g m g.seq_of_functions_info, [[ty1;ty2]], [ arg1; arg2; arg3 ], m) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index ae58019683a..eaf9e08392d 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2132,6 +2132,8 @@ val mkCallSeqAppend: TcGlobals -> range -> TType -> Expr -> Expr -> Expr val mkCallSeqFinally: TcGlobals -> range -> TType -> Expr -> Expr -> Expr +val mkCallSeqTryWith: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Expr + val mkCallSeqGenerated: TcGlobals -> range -> TType -> Expr -> Expr -> Expr val mkCallSeqOfFunctions: TcGlobals -> range -> TType -> TType -> Expr -> Expr -> Expr -> Expr diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index b7df6995693..e30869a1620 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -61,7 +61,8 @@ let totalSum = s() |> Seq.sum if totalSum <> 1 then failwith $"Sum was {{totalSum}} instead" -failwith $"List is %A{l}" +if l<> [] then + failwith $" List is %A{l}" """ |> runCode |> shouldSucceed diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index f7836031b05..cbfd0b6095b 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -1182,6 +1182,10 @@ module rec Compiler = match r.Output with | Some (ExecutionOutput output) -> sprintf "----output-----\n%s\n----error-------\n%s\n----------" output.StdOut output.StdErr + | Some (EvalOutput (Result.Error exn) ) -> + sprintf "----script error-----\n%s\n----------" (exn.ToString()) + | Some (EvalOutput (Result.Ok fsiVal) ) -> + sprintf "----script output-----\n%A\n----------" (fsiVal) | _ -> () ] |> String.concat "\n" failwith message From db5ada6a4884ab725e76afaabfb92da1a8c8684c Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 4 Jan 2023 20:38:53 +0100 Subject: [PATCH 10/18] Fixing expressions to become lambdas --- .../Checking/CheckComputationExpressions.fs | 14 +++++--- .../Language/SequenceExpressionTests.fs | 33 +++++++++++++++++-- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index 06f630d5242..17f02e3f9f4 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -2098,7 +2098,8 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = // let wrappedComp2 = SynExpr.TryWith(innerComp2,withList,m,spTry,spWith,trivia) // SynExpr.Sequential(sp, isTrueSeq, innerComp1, wrappedComp2, m) | other -> other - tcSequenceExprBody env genOuterTy tpenv liftedInnerSynExpr + let inner,tpenv = tcSequenceExprBody env genOuterTy tpenv liftedInnerSynExpr + mkSeqDelayedExpr mTryToWith inner, tpenv // Compile the pattern twice, once as a List.filter with all succeeding targets returning "1", and once as a proper catch block. let clauses, tpenv = @@ -2115,10 +2116,15 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let handlers, filterClauses = List.unzip clauses let withRange = trivia.WithToEndRange - let _v1, filterExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses + let v1, filterExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses //let _v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true Rethrow None g.exn_ty genOuterTy handlers - let _v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty genOuterTy handlers - let combinatorExpr = mkSeqTryWith cenv env mTryToWith genOuterTy tryExpr filterExpr handlerExpr + let v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty genOuterTy handlers + + let filterLambda = mkLambda filterExpr.Range v1 (filterExpr, genOuterTy) + let handlerLambda = mkLambda handlerExpr.Range v2 (handlerExpr, genOuterTy) + + + let combinatorExpr = mkSeqTryWith cenv env mTryToWith genOuterTy tryExpr filterLambda handlerLambda Some (combinatorExpr,tpenv) //let finalExpr = mkTryWith g (tryExpr, v1, filterExpr, v2, handlerExpr, mTryToWith, genOuterTy, spTry, spWith) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index e30869a1620..6fd04fe06a6 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -11,6 +11,20 @@ let fsiSession = getSessionForEval() let runCode = evalInSharedSession fsiSession +[] +let ``Simplest call of them all to check IL``() = + Fsx """ +let mySeq = + seq { + try + yield (10 / 0) + with _ -> + yield 100 + } + """ + |> compile + |> verifyIL ["abcde"] + [] let ``A seq{try/with} happy path with multiple language elements``() = Fsx """ @@ -57,11 +71,23 @@ let s() = seq { yield 1 l <- "End of with body" :: l } +l <- "Before sum" :: l let totalSum = s() |> Seq.sum +l <- "After sum" :: l if totalSum <> 1 then failwith $"Sum was {{totalSum}} instead" -if l<> [] then +l <- List.rev l +let expectedList = + [ "Before sum" // Seq is lazy, so we do not expect anything until iteration starts + "Before try" + "Inside finally" + "Inside with pattern" + "Inside with pattern" // Yes indeed, the exn matching pattern is executed twice + "Inside with body" + "End of with body" + "After sum"] +if l<> expectedList then failwith $" List is %A{l}" """ |> runCode @@ -70,6 +96,7 @@ if l<> [] then [] [] [] +[] let ``A sequence expression can recurse itself from with clause``(recLevel:int) = Fsx $""" let rec f () = seq {{ @@ -116,7 +143,7 @@ let mySeq (providedInput: seq) = yield 100 } let mySum = (mySeq [3;2;1;0]) |> Seq.sum -if mySum <> 100 then +if mySum <> (6/3 + 6/2 + 6/1 + 100) then failwith $"Sum was {mySum} instead" """ |> runCode @@ -135,7 +162,7 @@ let mySeq () = with _ -> yield 100 } -let mySum = (mySeq () |> Seq.take 10) |> Seq.sum +let mySum = (mySeq () |> Seq.truncate 10) |> Seq.sum if mySum <> (6/3 + 6/2 + 6/1 + 100) then failwith $"Sum was {mySum} instead" """ From 1f493f5c0f93f8e9c8bd8acc2391550b46f05b73 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 4 Jan 2023 20:54:40 +0100 Subject: [PATCH 11/18] Demonstrate slow recursion case --- .../Language/SequenceExpressionTests.fs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 6fd04fe06a6..70ec7950251 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -14,13 +14,13 @@ let runCode = evalInSharedSession fsiSession [] let ``Simplest call of them all to check IL``() = Fsx """ -let mySeq = - seq { - try - yield (10 / 0) - with _ -> - yield 100 - } +let rec f () = seq { + try + yield 1 + yield (1/0) + with pat -> + yield! f() +} """ |> compile |> verifyIL ["abcde"] @@ -96,7 +96,7 @@ if l<> expectedList then [] [] [] -[] +[] let ``A sequence expression can recurse itself from with clause``(recLevel:int) = Fsx $""" let rec f () = seq {{ From ee7e5938dc4d04d58550d114d50e390ce7c08d3e Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 4 Jan 2023 20:57:26 +0100 Subject: [PATCH 12/18] Removing old version of the code --- .../Checking/CheckComputationExpressions.fs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index 17f02e3f9f4..0eceeb80473 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -2089,19 +2089,10 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = | SynExpr.TryWith (innerTry,withList,mTryToWith,_spTry,_spWith,trivia) -> let env = { env with eIsControlFlow = true } let tryExpr, tpenv = - // If we have multiple yields/implicit below each other, we want to keep the results before failure - // Therefore, each 'Sequential' (expr1;expr2) can either fail the first one (outer with caters for it) - // -OR-, the first expr can suceed and yield something, and only the second fails - we add an artificial 'with' - let liftedInnerSynExpr = - match innerTry with - //| SynExpr.Sequential(sp, isTrueSeq, innerComp1, innerComp2, m) -> - // let wrappedComp2 = SynExpr.TryWith(innerComp2,withList,m,spTry,spWith,trivia) - // SynExpr.Sequential(sp, isTrueSeq, innerComp1, wrappedComp2, m) - | other -> other - let inner,tpenv = tcSequenceExprBody env genOuterTy tpenv liftedInnerSynExpr + let inner,tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry mkSeqDelayedExpr mTryToWith inner, tpenv - // Compile the pattern twice, once as a List.filter with all succeeding targets returning "1", and once as a proper catch block. + // Compile the pattern twice, once as a filter with all succeeding targets returning "1", and once as a proper catch block. let clauses, tpenv = (tpenv, withList) ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, m, sp, _)) -> let patR, condR, vspecs, envinner, tpenv = TcMatchPattern cenv g.exn_ty env tpenv pat cond @@ -2117,20 +2108,14 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let handlers, filterClauses = List.unzip clauses let withRange = trivia.WithToEndRange let v1, filterExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses - //let _v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true Rethrow None g.exn_ty genOuterTy handlers let v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty genOuterTy handlers let filterLambda = mkLambda filterExpr.Range v1 (filterExpr, genOuterTy) let handlerLambda = mkLambda handlerExpr.Range v2 (handlerExpr, genOuterTy) - let combinatorExpr = mkSeqTryWith cenv env mTryToWith genOuterTy tryExpr filterLambda handlerLambda Some (combinatorExpr,tpenv) - //let finalExpr = mkTryWith g (tryExpr, v1, filterExpr, v2, handlerExpr, mTryToWith, genOuterTy, spTry, spWith) - - //Some(finalExpr,tpenv) - | SynExpr.YieldOrReturnFrom ((isYield, _), synYieldExpr, m) -> let env = { env with eIsControlFlow = false } let resultExpr, genExprTy, tpenv = TcExprOfUnknownType cenv env tpenv synYieldExpr From 3dfbb1a5d59854867cc0511d175e174a8fd34313 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 5 Jan 2023 10:34:42 +0100 Subject: [PATCH 13/18] fantomas --- src/FSharp.Core/seqcore.fsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Core/seqcore.fsi b/src/FSharp.Core/seqcore.fsi index 81d4d250d76..4753eebabd5 100644 --- a/src/FSharp.Core/seqcore.fsi +++ b/src/FSharp.Core/seqcore.fsi @@ -84,7 +84,6 @@ module RuntimeHelpers = /// The result sequence. val EnumerateThenFinally: source: seq<'T> -> compensation: (unit -> unit) -> seq<'T> - /// The F# compiler emits calls to this function to /// implement the try/with operator for F# sequence expressions. /// @@ -93,7 +92,8 @@ module RuntimeHelpers = /// Pattern matches after 'when' with their actual execution code /// /// The result sequence. - val EnumerateTryWith: source : seq<'T> -> exceptionFilter: (exn -> int) -> exceptionHandler: (exn -> seq<'T>) -> seq<'T> + val EnumerateTryWith: + source: seq<'T> -> exceptionFilter: (exn -> int) -> exceptionHandler: (exn -> seq<'T>) -> seq<'T> /// The F# compiler emits calls to this function to implement the compiler-intrinsic /// conversions from untyped IEnumerable sequences to typed sequences. From 221e1f8be0916703b57533316f267a8c0deb52b2 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 30 Jan 2023 15:06:43 +0100 Subject: [PATCH 14/18] Try-with IL baseline test for .tail instruction present --- .../Language/SequenceExpressionTests.fs | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 70ec7950251..3f28d655667 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -12,18 +12,49 @@ let fsiSession = getSessionForEval() let runCode = evalInSharedSession fsiSession [] -let ``Simplest call of them all to check IL``() = +let ``Basic recursive case uses tail. recursion``() = Fsx """ let rec f () = seq { - try - yield 1 - yield (1/0) + try + yield 123 + yield (456/0) with pat -> + yield 789 yield! f() } """ |> compile - |> verifyIL ["abcde"] + |> verifyIL [" + .class auto ansi serializable sealed nested assembly beforefieldinit 'f@3-1' + extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2> + { + .field static assembly initonly class Test/'f@3-1' @_instance + .method assembly specialname rtspecialname + instance void .ctor() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>::.ctor() + IL_0006: ret + } + + .method public strict virtual instance class [runtime]System.Collections.Generic.IEnumerable`1 + Invoke(class [FSharp.Core]Microsoft.FSharp.Core.Unit unitVar) cil managed + { + + .maxstack 8 + IL_0000: ldc.i4.s 123 + IL_0002: call class [runtime]System.Collections.Generic.IEnumerable`1 [FSharp.Core]Microsoft.FSharp.Collections.SeqModule::Singleton(!!0) + IL_0007: ldsfld class Test/'f@5-2' Test/'f@5-2'::@_instance + IL_000c: call class [runtime]System.Collections.Generic.IEnumerable`1 [FSharp.Core]Microsoft.FSharp.Collections.SeqModule::Delay(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>) + IL_0011: tail. + IL_0013: call class [runtime]System.Collections.Generic.IEnumerable`1 [FSharp.Core]Microsoft.FSharp.Collections.SeqModule::Append(class [runtime]System.Collections.Generic.IEnumerable`1, + class [runtime]System.Collections.Generic.IEnumerable`1) + IL_0018: ret + } "] [] let ``A seq{try/with} happy path with multiple language elements``() = @@ -94,9 +125,9 @@ if l<> expectedList then |> shouldSucceed [] -[] -[] -[] +[] +[] +[] let ``A sequence expression can recurse itself from with clause``(recLevel:int) = Fsx $""" let rec f () = seq {{ From 7b85ff7244471720c24d38178443a1f9e858a1a2 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 30 Jan 2023 15:25:48 +0100 Subject: [PATCH 15/18] Language switch for try/with in seq{} --- src/Compiler/Checking/CheckComputationExpressions.fs | 3 +++ src/Compiler/FSComp.txt | 1 + src/Compiler/Facilities/LanguageFeatures.fs | 3 +++ src/Compiler/Facilities/LanguageFeatures.fsi | 1 + src/Compiler/xlf/FSComp.txt.cs.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.de.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.es.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.fr.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.it.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.ja.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.ko.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.pl.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.ru.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.tr.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 +++++ 17 files changed, 73 insertions(+) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index 0eceeb80473..e31b9a1369d 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -2087,6 +2087,9 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = Some(mkLet spMatch inputExprMark matchv inputExpr matchExpr, tpenv) | SynExpr.TryWith (innerTry,withList,mTryToWith,_spTry,_spWith,trivia) -> + if not(g.langVersion.SupportsFeature(LanguageFeature.TryWithInSeqExpression)) then + error(Error(FSComp.SR.tcTryIllegalInSequenceExpression(), mTryToWith)) + let env = { env with eIsControlFlow = true } let tryExpr, tpenv = let inner,tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 46379fc0eb4..8c19c5e6ff9 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1561,6 +1561,7 @@ featureErrorForNonVirtualMembersOverrides,"Raises errors for non-virtual members featureWarningWhenInliningMethodImplNoInlineMarkedFunction,"Raises warnings when 'let inline ... =' is used together with [] attribute. Function is not getting inlined." featureArithmeticInLiterals,"Allow arithmetic and logical operations in literals" featureErrorReportingOnStaticClasses,"Error reporting on static classes" +featureTryWithInSeqExpressions,"Support for try-with in sequence expressions" 3353,fsiInvalidDirective,"Invalid directive '#%s %s'" 3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." 3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index e7654bea422..1cdf25a8213 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -61,6 +61,7 @@ type LanguageFeature = | EscapeDotnetFormattableStrings | ArithmeticInLiterals | ErrorReportingOnStaticClasses + | TryWithInSeqExpression /// LanguageVersion management type LanguageVersion(versionText) = @@ -138,6 +139,7 @@ type LanguageVersion(versionText) = LanguageFeature.EscapeDotnetFormattableStrings, previewVersion LanguageFeature.ArithmeticInLiterals, previewVersion LanguageFeature.ErrorReportingOnStaticClasses, previewVersion + LanguageFeature.TryWithInSeqExpression, previewVersion ] @@ -252,6 +254,7 @@ type LanguageVersion(versionText) = | LanguageFeature.EscapeDotnetFormattableStrings -> FSComp.SR.featureEscapeBracesInFormattableString () | LanguageFeature.ArithmeticInLiterals -> FSComp.SR.featureArithmeticInLiterals () | LanguageFeature.ErrorReportingOnStaticClasses -> FSComp.SR.featureErrorReportingOnStaticClasses () + | LanguageFeature.TryWithInSeqExpression -> FSComp.SR.featureTryWithInSeqExpressions () /// Get a version string associated with the given feature. static member GetFeatureVersionString feature = diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index d070a8b4e36..21d578418a2 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -51,6 +51,7 @@ type LanguageFeature = | EscapeDotnetFormattableStrings | ArithmeticInLiterals | ErrorReportingOnStaticClasses + | TryWithInSeqExpression /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index be4e61a8fe3..1c8a05e7cb7 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -382,6 +382,11 @@ reprezentace struktury aktivních vzorů + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Vyvolá upozornění, když se použije „let inline ... =“ společně s atributem [<MethodImpl(MethodImplOptions.NoInlining)>]. Funkce není vkládána. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 5d662b8bf27..edc0ee0e53c 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -382,6 +382,11 @@ Strukturdarstellung für aktive Muster + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Löst Warnungen aus, wenn „let inline ... =“ zusammen mit dem Attribut [<MethodImpl(MethodImplOptions.NoInlining)>] verwendet wird. Die Funktion wird nicht inline gesetzt. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 1ddc22b8475..9d2854c3f36 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -382,6 +382,11 @@ representación de struct para modelos activos + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Genera advertencias cuando se usa "let inline ... =" junto con el atributo [<MethodImpl(MethodImplOptions.NoInlining)>]. La función no se está insertando. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index a7b82e51172..fbfdb8cafc4 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -382,6 +382,11 @@ représentation de structure pour les modèles actifs + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Génère des avertissements lorsque « let inline ... = » est utilisé avec l’attribut [<MethodImpl(MethodImplOptions.NoInlining)>]. La fonction n’est pas inlined. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index fcd590c3b39..e5754fec04b 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -382,6 +382,11 @@ rappresentazione struct per criteri attivi + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Genera avvisi quando 'let inline ... =' viene usato insieme all'attributo [<MethodImpl(MethodImplOptions.NoInlining)>]. La funzione non viene resa inline. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index b064cc8d2b7..bf4713519ad 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -382,6 +382,11 @@ アクティブなパターンの構造体表現 + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. 'let inline ... =' が [<MethodImpl(MethodImplOptions.NoInlining)>] 属性と一緒に使用されるときに警告を生成します。関数はインライン化されていません。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 41fced8e047..a940489708b 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -382,6 +382,11 @@ 활성 패턴에 대한 구조체 표현 + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. 'let inline ... ='을(를) [<MethodImpl(MethodImplOptions.NoInlining)>] 특성과 함께 사용하는 경우 경고를 발생합니다. 함수가 인라인되지 않습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 764d818dcd4..a02efeb6374 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -382,6 +382,11 @@ reprezentacja struktury aktywnych wzorców + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Zgłasza ostrzeżenia, gdy element „let inline ... =” jest używany razem z atrybutem [<MethodImpl(MethodImplOptions.NoInlining)>]. Funkcja nie jest wstawiana. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index da8982c32ac..fa534de101b 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -382,6 +382,11 @@ representação estrutural para padrões ativos + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Gera avisos quando 'let inline ... =' é usado junto com o atributo [<MethodImpl(MethodImplOptions.NoInlining)>]. A função não está sendo embutida. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 29827955000..c90569f11ca 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -382,6 +382,11 @@ представление структуры для активных шаблонов + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. Выдает предупреждения, когда используется параметр "let inline ... =" вместе с атрибутом [<MethodImpl(MethodImplOptions.NoInlining)>]. Функция не встраивается. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 3f3bb5dee68..76cb705799f 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -382,6 +382,11 @@ etkin desenler için yapı gösterimi + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. [<MethodImpl(MethodImplOptions.NoInlining)>] özniteliği ile birlikte 'let inline ... =' kullanıldığında uyarı verir. İşlev satır içine alınmıyor. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 3601169ca3a..a124cc5b279 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -382,6 +382,11 @@ 活动模式的结构表示形式 + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. 当 "let inline ... =" 与 [<MethodImpl(MethodImplOptions.NoInlining)>] 属性一起使用时引发警告。函数未内联。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index b767cad8784..e30cddc816a 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -382,6 +382,11 @@ 現用模式的結構表示法 + + Support for try-with in sequence expressions + Support for try-with in sequence expressions + + Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined. 當 'let inline ... =' 與 [<MethodImpl(MethodImplOptions.NoInlining)>] 屬性一起使用時引發警告。函數未內嵌。 From 8d498ac74eb9592b315d9e593086601d336c9107 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 30 Jan 2023 16:49:46 +0100 Subject: [PATCH 16/18] Typecheck tests to use preview langversion --- .../Language/SequenceExpressionTests.fs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs index 3f28d655667..5542717a94a 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressionTests.fs @@ -23,6 +23,7 @@ let rec f () = seq { yield! f() } """ + |> withLangVersionPreview |> compile |> verifyIL [" .class auto ansi serializable sealed nested assembly beforefieldinit 'f@3-1' @@ -350,6 +351,7 @@ let typedSeq = |_ when x = 0 -> %s{valInWith2} }} """ + |> withLangVersionPreview |> typecheck |> shouldSucceed @@ -368,6 +370,7 @@ let typedSeq = |_ when x = 0 -> %s{valInWith2} }} """ + |> withLangVersionPreview |> typecheck |> shouldSucceed @@ -386,6 +389,7 @@ let typedSeq = |_ when x = 0 -> %s{valInWith2} }} """ + |> withLangVersionPreview |> typecheck |> shouldFail |> withDiagnosticMessageMatches "This expression returns a value of type 'int' but is implicitly discarded." @@ -407,6 +411,7 @@ let typedSeq = |_ when x = 0 -> %s{valInWith2} }} """ + |> withLangVersionPreview |> typecheck |> shouldFail |> withErrorCode 193 @@ -431,6 +436,7 @@ let typedSeq = |_ when x = 0 -> %s{valInWith2} }} """ + |> withLangVersionPreview |> typecheck |> shouldFail |> withErrorCode 30 From 331d11fbb84a1fb08590e6649e370026e7511960 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 31 Jan 2023 12:12:58 +0100 Subject: [PATCH 17/18] Fsharp.Core baseline updated --- .../FSharp.Core.SurfaceArea.netstandard20.debug.bsl | 1 + .../FSharp.Core.SurfaceArea.netstandard20.release.bsl | 1 + .../FSharp.Core.SurfaceArea.netstandard21.debug.bsl | 1 + .../FSharp.Core.SurfaceArea.netstandard21.release.bsl | 1 + 4 files changed, 4 insertions(+) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl index b0ec5cbb925..0a8deb36192 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl @@ -844,6 +844,7 @@ Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: Microsoft.FSharp.Control. Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateFromFunctions[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateUsing[T,TCollection,TResult](T, Microsoft.FSharp.Core.FSharpFunc`2[T,TCollection]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateThenFinally[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) +Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateTryWith[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Int32], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Collections.Generic.IEnumerable`1[T]]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateWhile[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Boolean], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: System.IAsyncResult BeginInvoke(Microsoft.FSharp.Core.CompilerServices.ResumableStateMachine`1[TData] ByRef, System.Runtime.CompilerServices.IAsyncStateMachine, System.AsyncCallback, System.Object) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: Void .ctor(System.Object, IntPtr) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl index bb1dd5db9d6..ed33af7f83c 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl @@ -844,6 +844,7 @@ Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: Microsoft.FSharp.Control. Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateFromFunctions[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateUsing[T,TCollection,TResult](T, Microsoft.FSharp.Core.FSharpFunc`2[T,TCollection]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateThenFinally[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) +Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateTryWith[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Int32], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Collections.Generic.IEnumerable`1[T]]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateWhile[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Boolean], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: System.IAsyncResult BeginInvoke(Microsoft.FSharp.Core.CompilerServices.ResumableStateMachine`1[TData] ByRef, System.Runtime.CompilerServices.IAsyncStateMachine, System.AsyncCallback, System.Object) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: Void .ctor(System.Object, IntPtr) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl index 028a4fee29b..b20fac9a791 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl @@ -845,6 +845,7 @@ Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: Microsoft.FSharp.Control. Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateFromFunctions[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateUsing[T,TCollection,TResult](T, Microsoft.FSharp.Core.FSharpFunc`2[T,TCollection]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateThenFinally[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) +Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateTryWith[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Int32], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Collections.Generic.IEnumerable`1[T]]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateWhile[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Boolean], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: System.IAsyncResult BeginInvoke(Microsoft.FSharp.Core.CompilerServices.ResumableStateMachine`1[TData] ByRef, System.Runtime.CompilerServices.IAsyncStateMachine, System.AsyncCallback, System.Object) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: Void .ctor(System.Object, IntPtr) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl index 029951a590b..a4701146aa9 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl @@ -845,6 +845,7 @@ Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: Microsoft.FSharp.Control. Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateFromFunctions[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T], Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[TResult] EnumerateUsing[T,TCollection,TResult](T, Microsoft.FSharp.Core.FSharpFunc`2[T,TCollection]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateThenFinally[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) +Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateTryWith[T](System.Collections.Generic.IEnumerable`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Int32], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,System.Collections.Generic.IEnumerable`1[T]]) Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers: System.Collections.Generic.IEnumerable`1[T] EnumerateWhile[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Boolean], System.Collections.Generic.IEnumerable`1[T]) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: System.IAsyncResult BeginInvoke(Microsoft.FSharp.Core.CompilerServices.ResumableStateMachine`1[TData] ByRef, System.Runtime.CompilerServices.IAsyncStateMachine, System.AsyncCallback, System.Object) Microsoft.FSharp.Core.CompilerServices.SetStateMachineMethodImpl`1[TData]: Void .ctor(System.Object, IntPtr) From 625e91c007404755551892d1d994383d5bdc0c17 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 1 Feb 2023 19:04:34 +0100 Subject: [PATCH 18/18] Code styling --- src/FSharp.Core/seqcore.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Core/seqcore.fs b/src/FSharp.Core/seqcore.fs index 411162b178d..d5426d4ffe0 100644 --- a/src/FSharp.Core/seqcore.fs +++ b/src/FSharp.Core/seqcore.fs @@ -391,7 +391,7 @@ module RuntimeHelpers = let EnumerateTryWith (source : seq<'T>) (exceptionFilter:exn -> int) (exceptionHandler:exn -> seq<'T>) = let originalSource = lazy(source.GetEnumerator()) let mutable shouldDisposeOriginalAtTheEnd = true - let mutable exceptionalSource : option> = None + let mutable exceptionalSource : IEnumerator<'T> option = None let current() = match exceptionalSource with