Skip to content

Commit 3f9172a

Browse files
dsymeKevinRansom
andcommitted
squash and-bang implementation (#7756)
Co-authored-by: Kevin Ransom (msft) <[email protected]>
1 parent a14840b commit 3f9172a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1834
-162
lines changed

src/fsharp/CompileOps.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,8 @@ let OutputPhasedErrorR (os: StringBuilder) (err: PhasedDiagnostic) (canSuggestNa
10041004
| Parser.TOKEN_OLET(_) -> getErrorString("Parser.TOKEN.OLET")
10051005
| Parser.TOKEN_OBINDER
10061006
| Parser.TOKEN_BINDER -> getErrorString("Parser.TOKEN.BINDER")
1007+
| Parser.TOKEN_OAND_BANG
1008+
| Parser.TOKEN_AND_BANG -> getErrorString("Parser.TOKEN.AND.BANG")
10071009
| Parser.TOKEN_ODO -> getErrorString("Parser.TOKEN.ODO")
10081010
| Parser.TOKEN_OWITH -> getErrorString("Parser.TOKEN.OWITH")
10091011
| Parser.TOKEN_OFUNCTION -> getErrorString("Parser.TOKEN.OFUNCTION")

src/fsharp/FSComp.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,9 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl
14751475
3301,chkInvalidFunctionReturnType,"The function or method has an invalid return type '%s'. This is not permitted by the rules of Common IL."
14761476
3302,packageManagementRequiresVFive,"The package management feature requires language version 5.0 use /langversion:preview"
14771477
3303,fromEndSlicingRequiresVFive,"From the end slicing with requires language version 5.0, use /langversion:preview."
1478+
3343,tcRequireMergeSourcesOrBindN,"The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '%s' method or appropriate 'MergeSource' and 'Bind' methods"
1479+
3344,tcAndBangNotSupported,"This feature is not supported in this version of F#. You may need to add /langversion:preview to use this feature."
1480+
3345,tcInvalidUseBangBindingNoAndBangs,"use! may not be combined with and!"
14781481
useSdkRefs,"Use reference assemblies for .NET framework references when available (Enabled by default)."
14791482
fSharpBannerVersion,"%s for F# %s"
14801483
optsLangVersion,"Display the allowed values for language version, specify language version such as 'latest' or 'preview'"

src/fsharp/FSStrings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,9 @@
564564
<data name="Parser.TOKEN.AND" xml:space="preserve">
565565
<value>keyword 'and'</value>
566566
</data>
567+
!<data name="Parser.TOKEN.AND.BANG" xml:space="preserve">
568+
<value>keyword 'and!'</value>
569+
</data>
567570
<data name="Parser.TOKEN.AS" xml:space="preserve">
568571
<value>keyword 'as'</value>
569572
</data>

src/fsharp/LanguageFeatures.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type LanguageFeature =
2828
| PackageManagement
2929
| FromEndSlicing
3030
| FixedIndexSlice3d4d
31+
| AndBang
3132

3233
/// LanguageVersion management
3334
type LanguageVersion (specifiedVersionAsString) =
@@ -61,6 +62,7 @@ type LanguageVersion (specifiedVersionAsString) =
6162
LanguageFeature.NameOf, previewVersion
6263
LanguageFeature.OpenStaticClasses, previewVersion
6364
LanguageFeature.PackageManagement, previewVersion
65+
LanguageFeature.AndBang, previewVersion
6466
]
6567

6668
let specified =

src/fsharp/LanguageFeatures.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type LanguageFeature =
1616
| PackageManagement
1717
| FromEndSlicing
1818
| FixedIndexSlice3d4d
19-
19+
| AndBang
2020

2121
/// LanguageVersion management
2222
type LanguageVersion =

src/fsharp/LexFilter.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,6 +1784,13 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer,
17841784
pushCtxt tokenTup (CtxtLetDecl(blockLet, tokenStartPos))
17851785
returnToken tokenLexbufState (if blockLet then OBINDER b else token)
17861786

1787+
// and! ... ~~~> CtxtLetDecl
1788+
| AND_BANG isUse, (ctxt :: _) ->
1789+
let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false
1790+
if debug then dprintf "AND!: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n" blockLet outputPos tokenStartPos
1791+
pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos))
1792+
returnToken tokenLexbufState (if blockLet then OAND_BANG isUse else token)
1793+
17871794
| (VAL | STATIC | ABSTRACT | MEMBER | OVERRIDE | DEFAULT), ctxtStack when thereIsACtxtMemberBodyOnTheStackAndWeShouldPopStackForUpcomingMember ctxtStack ->
17881795
if debug then dprintf "STATIC/MEMBER/OVERRIDE/DEFAULT: already inside CtxtMemberBody, popping all that context before starting next member...\n"
17891796
// save this token, we'll consume it again later...

src/fsharp/TypeChecker.fs

100644100755
Lines changed: 293 additions & 69 deletions
Large diffs are not rendered by default.

src/fsharp/ast.fs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -764,12 +764,13 @@ and
764764
/// Computation expressions only
765765
| YieldOrReturnFrom of (bool * bool) * expr: SynExpr * range: range
766766

767-
/// SynExpr.LetOrUseBang (spBind, isUse, isFromSource, pat, rhsExpr, bodyExpr, mWholeExpr).
767+
/// SynExpr.LetOrUseAndBang (spBind, isUse, isFromSource, pat, rhsExpr, mLetBangExpr, [(andBangSpBind, andBangIsUse, andBangIsFromSource, andBangPat, andBangRhsExpr, mAndBangExpr)], bodyExpr).
768768
///
769769
/// F# syntax: let! pat = expr in expr
770770
/// F# syntax: use! pat = expr in expr
771+
/// F# syntax: let! pat = expr and! ... and! ... and! pat = expr in expr
771772
/// Computation expressions only
772-
| LetOrUseBang of bindSeqPoint: SequencePointInfoForBinding * isUse: bool * isFromSource: bool * SynPat * SynExpr * SynExpr * range: range
773+
| LetOrUseBang of bindSeqPoint: SequencePointInfoForBinding * isUse: bool * isFromSource: bool * SynPat * rhs: SynExpr * andBangs:(SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * range) list * body:SynExpr * range: range
773774

774775
/// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN
775776
| MatchBang of matchSeqPoint: SequencePointInfoForBinding * expr: SynExpr * clauses: SynMatchClause list * range: range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *)
@@ -2475,6 +2476,6 @@ let rec synExprContainsError inpExpr =
24752476

24762477
| SynExpr.MatchBang (_, e, cl, _) ->
24772478
walkExpr e || walkMatchClauses cl
2478-
| SynExpr.LetOrUseBang (_, _, _, _, e1, e2, _) ->
2479-
walkExpr e1 || walkExpr e2
2479+
| SynExpr.LetOrUseBang (rhs=e1;body=e2;andBangs=es) ->
2480+
walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2
24802481
walkExpr inpExpr

src/fsharp/lex.fsl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,12 @@ rule token args skip = parse
233233
{ YIELD_BANG(false) }
234234
| "match!"
235235
{ MATCH_BANG }
236+
| "and!"
237+
{ AND_BANG(false) }
236238
| ident '!'
237239
{ let tok = Keywords.KeywordOrIdentifierToken args lexbuf (lexemeTrimRight lexbuf 1)
238240
match tok with
239-
| LET _ -> BINDER (lexemeTrimRight lexbuf 1)
241+
| LET _ -> BINDER (lexemeTrimRight lexbuf 1)
240242
| _ -> fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("!")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) }
241243
| ident ('#')
242244
{ fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("#")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) }

src/fsharp/pars.fsy

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ let rangeOfLongIdent(lid:LongIdent) =
189189
%token <char> CHAR
190190
%token <System.Decimal> DECIMAL
191191
%token <(string * string)> BIGNUM
192-
%token <bool> LET YIELD YIELD_BANG
192+
%token <bool> LET YIELD YIELD_BANG AND_BANG
193193
%token <bool> LESS GREATER /* here the bool indicates if the tokens are part of a type application or type parameter declaration, e.g. C<int>, detected by the lex filter */
194194
%token <string> PERCENT_OP BINDER
195195
%token <string * bool> LQUOTE RQUOTE RQUOTE_DOT
@@ -218,6 +218,7 @@ let rangeOfLongIdent(lid:LongIdent) =
218218
/* for offside rule */
219219
%token <bool> OLET /* LexFilter #light converts 'LET' tokens to 'OLET' when starting (CtxtLetDecl(blockLet=true)) */
220220
%token <string> OBINDER /* LexFilter #light converts 'BINDER' tokens to 'OBINDER' when starting (CtxtLetDecl(blockLet=true)) */
221+
%token <bool> OAND_BANG /* LexFilter #light converts 'AND_BANG' tokens to 'OAND_BANG' when starting (CtxtLetDecl(blockLet=true)) */
221222
%token ODO /* LexFilter #light converts 'DO' tokens to 'ODO' */
222223
%token ODO_BANG /* LexFilter #light converts 'DO_BANG' tokens to 'ODO_BANG' */
223224
%token OTHEN /* LexFilter #light converts 'THEN' tokens to 'OTHEN' */
@@ -453,7 +454,8 @@ let rangeOfLongIdent(lid:LongIdent) =
453454
%nonassoc paren_pat_colon
454455
%nonassoc paren_pat_attribs
455456
%left OR BAR_BAR JOIN_IN
456-
%left AND /* check */
457+
%left AND
458+
%left AND_BANG
457459
%left AMP AMP_AMP
458460
%nonassoc pat_conj
459461
%nonassoc expr_not
@@ -3060,6 +3062,20 @@ recover:
30603062
| error { debugPrint("recovering via error"); true }
30613063
| EOF { debugPrint("recovering via EOF"); false }
30623064

3065+
morebinders:
3066+
| AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let
3067+
{ let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
3068+
let m = rhs parseState 1 (* TODO Pretty sure this is wrong *)
3069+
(spBind,$1,true,$2,$4,m) :: $6 }
3070+
3071+
| OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let
3072+
{ $5 "and!" (rhs parseState 1) // report unterminated error
3073+
let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
3074+
let m = rhs parseState 1 (* TODO Pretty sure this is wrong *)
3075+
(spBind,$1,true,$2,$4,m) :: $7 }
3076+
3077+
| %prec prec_no_more_attr_bindings
3078+
{ [] }
30633079

30643080
declExpr:
30653081
| defnBindings IN typedSeqExpr %prec expr_let
@@ -3353,27 +3369,35 @@ declExpr:
33533369
| YIELD_BANG declExpr
33543370
{ SynExpr.YieldOrReturnFrom (($1,not $1), $2, unionRanges (rhs parseState 1) $2.Range) }
33553371

3356-
| BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let
3372+
| YIELD recover
3373+
{ let mYieldAll = rhs parseState 1
3374+
SynExpr.YieldOrReturn (($1, not $1), arbExpr("yield", mYieldAll), mYieldAll) }
3375+
3376+
| YIELD_BANG recover
3377+
{ let mYieldAll = rhs parseState 1
3378+
SynExpr.YieldOrReturnFrom (($1, not $1), arbExpr("yield!", mYieldAll), mYieldAll) }
3379+
3380+
| BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let
33573381
{ let spBind = SequencePointAtBinding(rhs2 parseState 1 5)
3358-
let m = unionRanges (rhs parseState 1) $7.Range
3359-
SynExpr.LetOrUseBang (spBind,($1 = "use"),true,$2,$4,$7,m) }
3382+
let m = unionRanges (rhs parseState 1) $8.Range
3383+
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m) }
33603384

3361-
| OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP typedSeqExprBlock %prec expr_let
3385+
| OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let
33623386
{ $5 (if $1 = "use" then "use!" else "let!") (rhs parseState 1) // report unterminated error
33633387
let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range)
3364-
let m = unionRanges (rhs parseState 1) $7.Range
3365-
SynExpr.LetOrUseBang (spBind,($1 = "use"),true,$2,$4,$7,m) }
3388+
let m = unionRanges (rhs parseState 1) $8.Range
3389+
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m) }
33663390

33673391
| OBINDER headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let
33683392
{ // error recovery that allows intellisense when writing incomplete computation expressions
33693393
let spBind = SequencePointAtBinding(unionRanges (rhs parseState 1) $4.Range)
33703394
let mAll = unionRanges (rhs parseState 1) (rhs parseState 7)
33713395
let m = $4.Range.EndRange // zero-width range
3372-
SynExpr.LetOrUseBang (spBind,($1 = "use"),true,$2,$4, SynExpr.ImplicitZero m, mAll) }
3396+
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, [], SynExpr.ImplicitZero m, mAll) }
33733397

33743398
| DO_BANG typedSeqExpr IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let
33753399
{ let spBind = NoSequencePointAtDoBinding
3376-
SynExpr.LetOrUseBang (spBind,false,true,SynPat.Const(SynConst.Unit,$2.Range),$2,$5, unionRanges (rhs parseState 1) $5.Range) }
3400+
SynExpr.LetOrUseBang(spBind, false, true, SynPat.Const(SynConst.Unit,$2.Range), $2, [], $5, unionRanges (rhs parseState 1) $5.Range) }
33773401

33783402
| ODO_BANG typedSeqExprBlock hardwhiteDefnBindingsTerminator %prec expr_let
33793403
{ SynExpr.DoBang ($2, unionRanges (rhs parseState 1) $2.Range) }

0 commit comments

Comments
 (0)