-
Notifications
You must be signed in to change notification settings - Fork 825
Allow _
in async use! _
pattern (lift FS1228 restriction)
#18189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
fbbf038
9673183
0d9320b
c0b7ec4
c3e22ee
718506f
fc2e47b
9f54de8
cfb67a1
26c62f4
61958b2
3be84b6
aa73e1f
08eb286
0b664a9
cddbb7a
ed914e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1900,105 +1900,162 @@ let rec TryTranslateComputationExpression | |
bindDebugPoint = spBind | ||
isUse = true | ||
isFromSource = isFromSource | ||
pat = SynPat.Named(ident = SynIdent(id, _); isThisVal = false) as pat | ||
pat = pat | ||
rhs = rhsExpr | ||
andBangs = [] | ||
body = innerComp | ||
trivia = { LetOrUseBangKeyword = mBind }) | ||
| SynExpr.LetOrUseBang( | ||
bindDebugPoint = spBind | ||
isUse = true | ||
isFromSource = isFromSource | ||
pat = SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ])) as pat | ||
rhs = rhsExpr | ||
andBangs = [] | ||
andBangs = andBangs | ||
body = innerComp | ||
trivia = { LetOrUseBangKeyword = mBind }) -> | ||
match pat with | ||
| SynPat.Named(ident = SynIdent(id, _); isThisVal = false) | ||
| SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ])) when andBangs.IsEmpty -> | ||
if ceenv.isQuery then | ||
error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) | ||
|
||
if ceenv.isQuery then | ||
error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) | ||
if | ||
isNil ( | ||
TryFindIntrinsicOrExtensionMethInfo | ||
ResultCollectionSettings.AtMostOneResult | ||
cenv | ||
ceenv.env | ||
mBind | ||
ceenv.ad | ||
"Using" | ||
ceenv.builderTy | ||
) | ||
then | ||
error (Error(FSComp.SR.tcRequireBuilderMethod ("Using"), mBind)) | ||
|
||
if | ||
isNil ( | ||
TryFindIntrinsicOrExtensionMethInfo | ||
ResultCollectionSettings.AtMostOneResult | ||
cenv | ||
ceenv.env | ||
mBind | ||
ceenv.ad | ||
"Using" | ||
ceenv.builderTy | ||
) | ||
then | ||
error (Error(FSComp.SR.tcRequireBuilderMethod ("Using"), mBind)) | ||
if | ||
isNil ( | ||
TryFindIntrinsicOrExtensionMethInfo | ||
ResultCollectionSettings.AtMostOneResult | ||
cenv | ||
ceenv.env | ||
mBind | ||
ceenv.ad | ||
"Bind" | ||
ceenv.builderTy | ||
) | ||
then | ||
error (Error(FSComp.SR.tcRequireBuilderMethod ("Bind"), mBind)) | ||
|
||
if | ||
isNil ( | ||
TryFindIntrinsicOrExtensionMethInfo | ||
ResultCollectionSettings.AtMostOneResult | ||
cenv | ||
ceenv.env | ||
mBind | ||
ceenv.ad | ||
"Bind" | ||
ceenv.builderTy | ||
) | ||
then | ||
error (Error(FSComp.SR.tcRequireBuilderMethod ("Bind"), mBind)) | ||
let bindExpr = | ||
let consumeExpr = | ||
SynExpr.MatchLambda( | ||
false, | ||
mBind, | ||
[ | ||
SynMatchClause( | ||
pat, | ||
None, | ||
TranslateComputationExpressionNoQueryOps ceenv innerComp, | ||
innerComp.Range, | ||
DebugPointAtTarget.Yes, | ||
SynMatchClauseTrivia.Zero | ||
) | ||
], | ||
DebugPointAtBinding.NoneAtInvisible, | ||
mBind | ||
) | ||
|
||
let bindExpr = | ||
let consumeExpr = | ||
SynExpr.MatchLambda( | ||
false, | ||
mBind, | ||
[ | ||
SynMatchClause( | ||
pat, | ||
None, | ||
TranslateComputationExpressionNoQueryOps ceenv innerComp, | ||
innerComp.Range, | ||
DebugPointAtTarget.Yes, | ||
SynMatchClauseTrivia.Zero | ||
) | ||
], | ||
DebugPointAtBinding.NoneAtInvisible, | ||
mBind | ||
) | ||
let consumeExpr = | ||
mkSynCall "Using" mBind [ SynExpr.Ident id; consumeExpr ] ceenv.builderValName | ||
|
||
let consumeExpr = | ||
mkSynCall "Using" mBind [ SynExpr.Ident id; consumeExpr ] ceenv.builderValName | ||
let consumeExpr = | ||
SynExpr.MatchLambda( | ||
false, | ||
mBind, | ||
[ | ||
SynMatchClause(pat, None, consumeExpr, id.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero) | ||
], | ||
DebugPointAtBinding.NoneAtInvisible, | ||
mBind | ||
) | ||
|
||
let consumeExpr = | ||
SynExpr.MatchLambda( | ||
false, | ||
mBind, | ||
[ | ||
SynMatchClause(pat, None, consumeExpr, id.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero) | ||
], | ||
DebugPointAtBinding.NoneAtInvisible, | ||
mBind | ||
) | ||
let rhsExpr = | ||
mkSourceExprConditional isFromSource rhsExpr ceenv.sourceMethInfo ceenv.builderValName | ||
|
||
let rhsExpr = | ||
mkSourceExprConditional isFromSource rhsExpr ceenv.sourceMethInfo ceenv.builderValName | ||
mkSynCall "Bind" mBind [ rhsExpr; consumeExpr ] ceenv.builderValName | ||
|> addBindDebugPoint spBind | ||
|
||
mkSynCall "Bind" mBind [ rhsExpr; consumeExpr ] ceenv.builderValName | ||
|> addBindDebugPoint spBind | ||
Some(translatedCtxt bindExpr) | ||
|
||
Some(translatedCtxt bindExpr) | ||
| SynPat.Wild _ -> | ||
if not (isNil andBangs) then | ||
let m = | ||
match andBangs with | ||
| [] -> comp.Range | ||
| h :: _ -> h.Trivia.AndBangKeyword | ||
|
||
// 'use! pat = e1 ... in e2' where 'pat' is not a simple name -> error | ||
| SynExpr.LetOrUseBang(isUse = true; andBangs = andBangs; trivia = { LetOrUseBangKeyword = mBind }) -> | ||
if isNil andBangs then | ||
error (Error(FSComp.SR.tcInvalidUseBangBinding (), mBind)) | ||
else | ||
let m = | ||
match andBangs with | ||
| [] -> comp.Range | ||
| h :: _ -> h.Trivia.AndBangKeyword | ||
error (Error(FSComp.SR.tcInvalidUseBangBindingNoAndBangs (), m)) | ||
else | ||
if ceenv.isQuery then | ||
error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) | ||
|
||
if | ||
isNil ( | ||
TryFindIntrinsicOrExtensionMethInfo | ||
ResultCollectionSettings.AtMostOneResult | ||
cenv | ||
ceenv.env | ||
mBind | ||
ceenv.ad | ||
"Using" | ||
ceenv.builderTy | ||
) | ||
then | ||
error (Error(FSComp.SR.tcRequireBuilderMethod "Using", mBind)) | ||
|
||
if | ||
isNil ( | ||
TryFindIntrinsicOrExtensionMethInfo | ||
ResultCollectionSettings.AtMostOneResult | ||
cenv | ||
ceenv.env | ||
mBind | ||
ceenv.ad | ||
"Bind" | ||
ceenv.builderTy | ||
) | ||
then | ||
error (Error(FSComp.SR.tcRequireBuilderMethod "Bind", mBind)) | ||
|
||
edgarfgp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let bindExpr = | ||
let consumeExpr = | ||
SynExpr.MatchLambda( | ||
false, | ||
mBind, | ||
[ | ||
SynMatchClause( | ||
pat, | ||
None, | ||
TranslateComputationExpressionNoQueryOps ceenv innerComp, | ||
innerComp.Range, | ||
DebugPointAtTarget.Yes, | ||
SynMatchClauseTrivia.Zero | ||
) | ||
], | ||
DebugPointAtBinding.NoneAtInvisible, | ||
mBind | ||
) | ||
|
||
let rhsExpr = | ||
mkSourceExprConditional isFromSource rhsExpr ceenv.sourceMethInfo ceenv.builderValName | ||
|
||
error (Error(FSComp.SR.tcInvalidUseBangBindingNoAndBangs (), m)) | ||
mkSynCall "Bind" mBind [ rhsExpr; consumeExpr ] ceenv.builderValName | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was expecting a call to "Using" somewhere, but cannot find it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way I understood the logic was that Tested this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it only does "Bind" and not an "Using", it will lack the CE's implementation of disposal (which can be anything the CE wants to do to cleanup). |
||
|> addBindDebugPoint spBind | ||
edgarfgp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Some(translatedCtxt bindExpr) | ||
| _ -> | ||
if isNil andBangs then | ||
error (Error(FSComp.SR.tcInvalidUseBangBinding (), mBind)) | ||
else | ||
let m = | ||
match andBangs with | ||
| [] -> comp.Range | ||
| h :: _ -> h.Trivia.AndBangKeyword | ||
|
||
error (Error(FSComp.SR.tcInvalidUseBangBindingNoAndBangs (), m)) | ||
// 'let! pat1 = expr1 and! pat2 = expr2 in ...' --> | ||
// build.BindN(expr1, expr2, ...) | ||
// or | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -214,6 +214,37 @@ let run r2 r3 = | |
(Error 3345, Line 18, Col 9, Line 18, Col 13, "use! may not be combined with and!") | ||
] | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do add positive tests which include a |
||
[<Fact>] | ||
let ``multiple use! _ may not be combined with and!`` () = | ||
Fsx """ | ||
module Result = | ||
let zip x1 x2 = | ||
match x1,x2 with | ||
| Ok x1res, Ok x2res -> Ok (x1res, x2res) | ||
| Error e, _ -> Error e | ||
| _, Error e -> Error e | ||
|
||
type ResultBuilder() = | ||
member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2 | ||
member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x | ||
|
||
let result = ResultBuilder() | ||
|
||
let run r2 r3 = | ||
result { | ||
use! _ = r2 | ||
and! c = r3 | ||
use! _ = r2 | ||
return b - c | ||
} | ||
""" | ||
|> ignoreWarnings | ||
|> typecheck | ||
|> shouldFail | ||
|> withDiagnostics [ | ||
(Error 3345, Line 18, Col 9, Line 18, Col 13, "use! may not be combined with and!") | ||
] | ||
|
||
[<Fact>] | ||
let ``multiple use! may not be combined with multiple and!`` () = | ||
Fsx """ | ||
|
@@ -725,4 +756,36 @@ let x18mutable = | |
(Error 3147, Line 5, Col 17, Line 5, Col 20, "This 'let' definition may not be used in a query. Only simple value definitions may be used in queries.") | ||
(Error 3147, Line 13, Col 20, Line 13, Col 23, "This 'let' definition may not be used in a query. Only simple value definitions may be used in queries.") | ||
(Error 3147, Line 20, Col 21, Line 20, Col 22, "This 'let' definition may not be used in a query. Only simple value definitions may be used in queries.") | ||
] | ||
|
||
[<Fact>] | ||
let ``Allow _ in async use! _ pattern (lift FS1228 restriction)`` () = | ||
Fsx """ | ||
open System | ||
let doSomething () = | ||
async { | ||
use _ = { new IDisposable with member _.Dispose() = printfn "disposed" } | ||
use! _ = Async.OnCancel (fun () -> printfn "disposed") | ||
use! res = Async.OnCancel (fun () -> printfn "disposed") | ||
return () | ||
} | ||
""" | ||
|> typecheck | ||
|> shouldSucceed | ||
|
||
[<Fact>] | ||
let ``error when using use! binding with wrong form.`` () = | ||
Fsx """ | ||
open System | ||
let doSomething () = | ||
async { | ||
use! [| r1; r2 |] = Async.Parallel [| async.Return(1); async.Return(2) |] | ||
let y = 4 | ||
return r1,r2 | ||
} | ||
""" | ||
|> typecheck | ||
|> shouldFail | ||
|> withDiagnostics [ | ||
(Error 1228, Line 5, Col 9, Line 5, Col 13, "'use!' bindings must be of the form 'use! <var> = <expr>'") | ||
] |
Uh oh!
There was an error while loading. Please reload this page.