Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/fsharp/CompileOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,8 @@ let OutputPhasedErrorR (os: StringBuilder) (err: PhasedDiagnostic) (canSuggestNa
| Parser.TOKEN_OLET(_) -> getErrorString("Parser.TOKEN.OLET")
| Parser.TOKEN_OBINDER
| Parser.TOKEN_BINDER -> getErrorString("Parser.TOKEN.BINDER")
| Parser.TOKEN_OAND_BANG
| Parser.TOKEN_AND_BANG -> getErrorString("Parser.TOKEN.AND.BANG")
| Parser.TOKEN_ODO -> getErrorString("Parser.TOKEN.ODO")
| Parser.TOKEN_OWITH -> getErrorString("Parser.TOKEN.OWITH")
| Parser.TOKEN_OFUNCTION -> getErrorString("Parser.TOKEN.OFUNCTION")
Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,9 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl
3301,chkInvalidFunctionReturnType,"The function or method has an invalid return type '%s'. This is not permitted by the rules of Common IL."
3302,packageManagementRequiresVFive,"The package management feature requires language version 5.0 use /langversion:preview"
3303,fromEndSlicingRequiresVFive,"From the end slicing with requires language version 5.0, use /langversion:preview."
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"
3344,tcAndBangNotSupported,"This feature is not supported in this version of F#. You may need to add /langversion:preview to use this feature."
3345,tcInvalidUseBangBindingNoAndBangs,"use! may not be combined with and!"
useSdkRefs,"Use reference assemblies for .NET framework references when available (Enabled by default)."
fSharpBannerVersion,"%s for F# %s"
optsLangVersion,"Display the allowed values for language version, specify language version such as 'latest' or 'preview'"
Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/FSStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,9 @@
<data name="Parser.TOKEN.AND" xml:space="preserve">
<value>keyword 'and'</value>
</data>
!<data name="Parser.TOKEN.AND.BANG" xml:space="preserve">
<value>keyword 'and!'</value>
</data>
<data name="Parser.TOKEN.AS" xml:space="preserve">
<value>keyword 'as'</value>
</data>
Expand Down
2 changes: 2 additions & 0 deletions src/fsharp/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type LanguageFeature =
| PackageManagement
| FromEndSlicing
| FixedIndexSlice3d4d
| AndBang

/// LanguageVersion management
type LanguageVersion (specifiedVersionAsString) =
Expand Down Expand Up @@ -61,6 +62,7 @@ type LanguageVersion (specifiedVersionAsString) =
LanguageFeature.NameOf, previewVersion
LanguageFeature.OpenStaticClasses, previewVersion
LanguageFeature.PackageManagement, previewVersion
LanguageFeature.AndBang, previewVersion
]

let specified =
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type LanguageFeature =
| PackageManagement
| FromEndSlicing
| FixedIndexSlice3d4d

| AndBang

/// LanguageVersion management
type LanguageVersion =
Expand Down
7 changes: 7 additions & 0 deletions src/fsharp/LexFilter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1784,6 +1784,13 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer,
pushCtxt tokenTup (CtxtLetDecl(blockLet, tokenStartPos))
returnToken tokenLexbufState (if blockLet then OBINDER b else token)

// and! ... ~~~> CtxtLetDecl
| AND_BANG isUse, (ctxt :: _) ->
let blockLet = match ctxt with CtxtSeqBlock _ -> true | _ -> false
if debug then dprintf "AND!: entering CtxtLetDecl(blockLet=%b), awaiting EQUALS to go to CtxtSeqBlock (%a)\n" blockLet outputPos tokenStartPos
pushCtxt tokenTup (CtxtLetDecl(blockLet,tokenStartPos))
returnToken tokenLexbufState (if blockLet then OAND_BANG isUse else token)

| (VAL | STATIC | ABSTRACT | MEMBER | OVERRIDE | DEFAULT), ctxtStack when thereIsACtxtMemberBodyOnTheStackAndWeShouldPopStackForUpcomingMember ctxtStack ->
if debug then dprintf "STATIC/MEMBER/OVERRIDE/DEFAULT: already inside CtxtMemberBody, popping all that context before starting next member...\n"
// save this token, we'll consume it again later...
Expand Down
362 changes: 293 additions & 69 deletions src/fsharp/TypeChecker.fs
100644 → 100755

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions src/fsharp/ast.fs
Original file line number Diff line number Diff line change
Expand Up @@ -764,12 +764,13 @@ and
/// Computation expressions only
| YieldOrReturnFrom of (bool * bool) * expr: SynExpr * range: range

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

/// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN
| 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 *)
Expand Down Expand Up @@ -2475,6 +2476,6 @@ let rec synExprContainsError inpExpr =

| SynExpr.MatchBang (_, e, cl, _) ->
walkExpr e || walkMatchClauses cl
| SynExpr.LetOrUseBang (_, _, _, _, e1, e2, _) ->
walkExpr e1 || walkExpr e2
| SynExpr.LetOrUseBang (rhs=e1;body=e2;andBangs=es) ->
walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2
walkExpr inpExpr
4 changes: 3 additions & 1 deletion src/fsharp/lex.fsl
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,12 @@ rule token args skip = parse
{ YIELD_BANG(false) }
| "match!"
{ MATCH_BANG }
| "and!"
{ AND_BANG(false) }
| ident '!'
{ let tok = Keywords.KeywordOrIdentifierToken args lexbuf (lexemeTrimRight lexbuf 1)
match tok with
| LET _ -> BINDER (lexemeTrimRight lexbuf 1)
| LET _ -> BINDER (lexemeTrimRight lexbuf 1)
| _ -> fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("!")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) }
| ident ('#')
{ fail args lexbuf (FSComp.SR.lexIdentEndInMarkReserved("#")) (Keywords.KeywordOrIdentifierToken args lexbuf (lexeme lexbuf)) }
Expand Down
44 changes: 34 additions & 10 deletions src/fsharp/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ let rangeOfLongIdent(lid:LongIdent) =
%token <char> CHAR
%token <System.Decimal> DECIMAL
%token <(string * string)> BIGNUM
%token <bool> LET YIELD YIELD_BANG
%token <bool> LET YIELD YIELD_BANG AND_BANG
%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 */
%token <string> PERCENT_OP BINDER
%token <string * bool> LQUOTE RQUOTE RQUOTE_DOT
Expand Down Expand Up @@ -218,6 +218,7 @@ let rangeOfLongIdent(lid:LongIdent) =
/* for offside rule */
%token <bool> OLET /* LexFilter #light converts 'LET' tokens to 'OLET' when starting (CtxtLetDecl(blockLet=true)) */
%token <string> OBINDER /* LexFilter #light converts 'BINDER' tokens to 'OBINDER' when starting (CtxtLetDecl(blockLet=true)) */
%token <bool> OAND_BANG /* LexFilter #light converts 'AND_BANG' tokens to 'OAND_BANG' when starting (CtxtLetDecl(blockLet=true)) */
%token ODO /* LexFilter #light converts 'DO' tokens to 'ODO' */
%token ODO_BANG /* LexFilter #light converts 'DO_BANG' tokens to 'ODO_BANG' */
%token OTHEN /* LexFilter #light converts 'THEN' tokens to 'OTHEN' */
Expand Down Expand Up @@ -453,7 +454,8 @@ let rangeOfLongIdent(lid:LongIdent) =
%nonassoc paren_pat_colon
%nonassoc paren_pat_attribs
%left OR BAR_BAR JOIN_IN
%left AND /* check */
%left AND
%left AND_BANG
%left AMP AMP_AMP
%nonassoc pat_conj
%nonassoc expr_not
Expand Down Expand Up @@ -3060,6 +3062,20 @@ recover:
| error { debugPrint("recovering via error"); true }
| EOF { debugPrint("recovering via EOF"); false }

morebinders:
| AND_BANG headBindingPattern EQUALS typedSeqExprBlock IN morebinders %prec expr_let
{ let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
let m = rhs parseState 1 (* TODO Pretty sure this is wrong *)
(spBind,$1,true,$2,$4,m) :: $6 }

| OAND_BANG headBindingPattern EQUALS typedSeqExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP morebinders %prec expr_let
{ $5 "and!" (rhs parseState 1) // report unterminated error
let spBind = SequencePointAtBinding(rhs2 parseState 1 5) (* TODO Pretty sure this is wrong *)
let m = rhs parseState 1 (* TODO Pretty sure this is wrong *)
(spBind,$1,true,$2,$4,m) :: $7 }

| %prec prec_no_more_attr_bindings
{ [] }

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

| BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP typedSeqExprBlock %prec expr_let
| YIELD recover
{ let mYieldAll = rhs parseState 1
SynExpr.YieldOrReturn (($1, not $1), arbExpr("yield", mYieldAll), mYieldAll) }

| YIELD_BANG recover
{ let mYieldAll = rhs parseState 1
SynExpr.YieldOrReturnFrom (($1, not $1), arbExpr("yield!", mYieldAll), mYieldAll) }

| BINDER headBindingPattern EQUALS typedSeqExprBlock IN opt_OBLOCKSEP morebinders typedSeqExprBlock %prec expr_let
{ let spBind = SequencePointAtBinding(rhs2 parseState 1 5)
let m = unionRanges (rhs parseState 1) $7.Range
SynExpr.LetOrUseBang (spBind,($1 = "use"),true,$2,$4,$7,m) }
let m = unionRanges (rhs parseState 1) $8.Range
SynExpr.LetOrUseBang(spBind, ($1 = "use"), true, $2, $4, $7, $8, m) }

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

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

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

| ODO_BANG typedSeqExprBlock hardwhiteDefnBindingsTerminator %prec expr_let
{ SynExpr.DoBang ($2, unionRanges (rhs parseState 1) $2.Range) }
Expand Down
8 changes: 6 additions & 2 deletions src/fsharp/service/ServiceAssemblyContent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -703,9 +703,13 @@ module ParsedInput =
addLongIdentWithDots ident
List.iter walkExpr [e1; e2; e3]
| SynExpr.JoinIn (e1, _, e2, _) -> List.iter walkExpr [e1; e2]
| SynExpr.LetOrUseBang (_, _, _, pat, e1, e2, _) ->
| SynExpr.LetOrUseBang (_, _, _, pat, e1, es, e2, _) ->
walkPat pat
List.iter walkExpr [e1; e2]
walkExpr e1
for (_,_,_,patAndBang,eAndBang,_) in es do
walkPat patAndBang
walkExpr eAndBang
walkExpr e2
| SynExpr.TraitCall (ts, sign, e, _) ->
List.iter walkTypar ts
walkMemberSig sign
Expand Down
10 changes: 8 additions & 2 deletions src/fsharp/service/ServiceInterfaceStubGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -896,8 +896,14 @@ module InterfaceStubGenerator =
| SynExpr.DoBang (synExpr, _range) ->
walkExpr synExpr

| SynExpr.LetOrUseBang (_sequencePointInfoForBinding, _, _, _synPat, synExpr1, synExpr2, _range) ->
List.tryPick walkExpr [synExpr1; synExpr2]
| SynExpr.LetOrUseBang (_sequencePointInfoForBinding, _, _, _synPat, synExpr1, synExprAndBangs, synExpr2, _range) ->
[
yield synExpr1
for (_,_,_,_,eAndBang,_) in synExprAndBangs do
yield eAndBang
yield synExpr2
]
|> List.tryPick walkExpr

| SynExpr.LibraryOnlyILAssembly _
| SynExpr.LibraryOnlyStaticOptimization _
Expand Down
4 changes: 2 additions & 2 deletions src/fsharp/service/ServiceLexing.fs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,14 @@ module internal TokenClassifications =
| MEMBER | STATIC | NAMESPACE
| OASSERT | OLAZY | ODECLEND | OBLOCKSEP | OEND | OBLOCKBEGIN | ORIGHT_BLOCK_END
| OBLOCKEND | OBLOCKEND_COMING_SOON | OBLOCKEND_IS_HERE | OTHEN | OELSE | OLET(_)
| OBINDER _ | BINDER _ | ODO | OWITH | OFUNCTION | OFUN | ORESET | ODUMMY _ | DO_BANG
| OBINDER _ | OAND_BANG _ | BINDER _ | ODO | OWITH | OFUNCTION | OFUN | ORESET | ODUMMY _ | DO_BANG
| ODO_BANG | YIELD _ | YIELD_BANG _ | OINTERFACE_MEMBER
| ELIF | RARROW | LARROW | SIG | STRUCT
| UPCAST | DOWNCAST | NULL | RESERVED | MODULE | AND | AS | ASSERT | ASR
| DOWNTO | EXCEPTION | FALSE | FOR | FUN | FUNCTION
| FINALLY | LAZY | MATCH | MATCH_BANG | MUTABLE | NEW | OF | OPEN | OR | VOID | EXTERN
| INTERFACE | REC | TO | TRUE | TRY | TYPE | VAL | INLINE | WHEN | WHILE | WITH
| IF | THEN | ELSE | DO | DONE | LET(_) | IN (*| NAMESPACE*) | CONST
| IF | THEN | ELSE | DO | DONE | LET _ | AND_BANG _ | IN | CONST
| HIGH_PRECEDENCE_PAREN_APP | FIXED
| HIGH_PRECEDENCE_BRACK_APP
| TYPE_COMING_SOON | TYPE_IS_HERE | MODULE_COMING_SOON | MODULE_IS_HERE ->
Expand Down
14 changes: 10 additions & 4 deletions src/fsharp/service/ServiceParseTreeWalk.fs
Original file line number Diff line number Diff line change
Expand Up @@ -473,10 +473,16 @@ module public AstTraversal =
| SynExpr.ImplicitZero (_range) -> None
| SynExpr.YieldOrReturn (_, synExpr, _range) -> traverseSynExpr synExpr
| SynExpr.YieldOrReturnFrom (_, synExpr, _range) -> traverseSynExpr synExpr
| SynExpr.LetOrUseBang (_sequencePointInfoForBinding, _, _, synPat, synExpr, synExpr2, _range) ->
[dive synPat synPat.Range traversePat
dive synExpr synExpr.Range traverseSynExpr
dive synExpr2 synExpr2.Range traverseSynExpr]
| SynExpr.LetOrUseBang(_sequencePointInfoForBinding, _, _, synPat, synExpr, andBangSynExprs, synExpr2, _range) ->
[
yield dive synPat synPat.Range traversePat
yield dive synExpr synExpr.Range traverseSynExpr
yield!
[ for (_,_,_,andBangSynPat,andBangSynExpr,_) in andBangSynExprs do
yield (dive andBangSynPat andBangSynPat.Range traversePat)
yield (dive andBangSynExpr andBangSynExpr.Range traverseSynExpr)]
yield dive synExpr2 synExpr2.Range traverseSynExpr
]
|> pick expr
| SynExpr.MatchBang (_sequencePointInfoForBinding, synExpr, synMatchClauseList, _range) ->
[yield dive synExpr synExpr.Range traverseSynExpr
Expand Down
23 changes: 15 additions & 8 deletions src/fsharp/service/ServiceStructure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,21 @@ module Structure =
| SynExpr.DoBang (e, r) ->
rcheck Scope.Do Collapse.Below r <| Range.modStart 3 r
parseExpr e
| SynExpr.LetOrUseBang (_, _, _, pat, e1, e2, _) ->
// for `let!` or `use!` the pattern begins at the end of the keyword so that
// this scope can be used without adjustment if there is no `=` on the same line
// if there is an `=` the range will be adjusted during the tooltip creation
let r = Range.endToEnd pat.Range e1.Range
rcheck Scope.LetOrUseBang Collapse.Below r r
parseExpr e1
parseExpr e2
| SynExpr.LetOrUseBang (_, _, _, pat, eLet, es, eBody, _) ->
[
yield eLet
yield! [ for (_,_,_,_,eAndBang,_) in es do yield eAndBang ]
]
|> List.iter (fun e ->
// for `let!`, `use!` or `and!` the pattern begins at the end of the
// keyword so that this scope can be used without adjustment if there is no `=`
// on the same line. If there is an `=` the range will be adjusted during the
// tooltip creation
let r = Range.endToEnd pat.Range e.Range
rcheck Scope.LetOrUseBang Collapse.Below r r
parseExpr e
)
parseExpr eBody
| SynExpr.For (_, _, _, _, _, e, r)
| SynExpr.ForEach (_, _, _, _, _, e, r) ->
rcheck Scope.For Collapse.Below r r
Expand Down
14 changes: 12 additions & 2 deletions src/fsharp/service/ServiceUntypedParse.fs
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,12 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: Ast.ParsedInput op
yield! walkExpr false e2
yield! walkExpr false e3

| SynExpr.LetOrUseBang (spBind, _, _, _, e1, e2, _) ->
| SynExpr.LetOrUseBang (spBind, _, _, _, e1, es, e2, _) ->
yield! walkBindSeqPt spBind
yield! walkExpr true e1
for (andBangSpBind,_,_,_,eAndBang,_) in es do
yield! walkBindSeqPt andBangSpBind
yield! walkExpr true eAndBang
yield! walkExpr true e2

| SynExpr.MatchBang (spBind, e, cl, _) ->
Expand Down Expand Up @@ -880,7 +883,14 @@ module UntypedParseImpl =
| SynExpr.Match (_, e, synMatchClauseList, _)
| SynExpr.MatchBang (_, e, synMatchClauseList, _) ->
walkExprWithKind parentKind e |> Option.orElse (List.tryPick walkClause synMatchClauseList)
| SynExpr.LetOrUseBang (_, _, _, _, e1, e2, _) -> List.tryPick (walkExprWithKind parentKind) [e1; e2]
| SynExpr.LetOrUseBang(_, _, _, _, e1, es, e2, _) ->
[
yield e1
for (_,_,_,_,eAndBang,_) in es do
yield eAndBang
yield e2
]
|> List.tryPick (walkExprWithKind parentKind)
| SynExpr.DoBang (e, _) -> walkExprWithKind parentKind e
| SynExpr.TraitCall (ts, sign, e, _) ->
List.tryPick walkTypar ts
Expand Down
Loading