Skip to content
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

Add while! (while bang) #14238

Merged
merged 25 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 14 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
38 changes: 36 additions & 2 deletions src/Compiler/Checking/CheckComputationExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ let YieldFree (cenv: cenv) expr =
| SynExpr.TryFinally (tryExpr=body)
| SynExpr.LetOrUse (body=body)
| SynExpr.While (doExpr=body)
| SynExpr.WhileBang (doExpr=body)
| SynExpr.ForEach (bodyExpr=body) ->
YieldFree body

Expand Down Expand Up @@ -142,6 +143,7 @@ let YieldFree (cenv: cenv) expr =
| SynExpr.TryFinally (tryExpr=body)
| SynExpr.LetOrUse (body=body)
| SynExpr.While (doExpr=body)
| SynExpr.WhileBang (doExpr=body)
| SynExpr.ForEach (bodyExpr=body) ->
YieldFree body

Expand Down Expand Up @@ -177,7 +179,8 @@ let (|SimpleSemicolonSequence|_|) cenv acceptDeprecated cexpr =
| SynExpr.Do _
| SynExpr.MatchBang _
| SynExpr.LetOrUseBang _
| SynExpr.While _ -> false
| SynExpr.While _
| SynExpr.WhileBang _ -> false
| _ -> true

let rec TryGetSimpleSemicolonSequenceOfComprehension expr acc =
Expand Down Expand Up @@ -1027,6 +1030,36 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol
[ mkSynDelay2 guardExpr;
mkSynCall "Delay" mWhile [mkSynDelay innerComp.Range holeFill]])) )

| SynExpr.WhileBang (spWhile, guardExpr, innerComp, mOrig) ->
let mGuard = guardExpr.Range
let mWhile = match spWhile with DebugPointAtWhile.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.While) | _ -> mGuard
let mGuard = mGuard.MakeSynthetic()

// 'while!' is hit just before each time the guard is called
let guardExpr =
match spWhile with
| DebugPointAtWhile.Yes _ ->
SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mWhile, false, guardExpr)
| DebugPointAtWhile.No -> guardExpr

let rewrittenWhileExpr =
let idFirst = mkSynId mGuard (CompilerGeneratedName "first")
let patFirst = mkSynPatVar None idFirst

let body =
let idCond = mkSynId mGuard (CompilerGeneratedName "cond")
let patCond = mkSynPatVar None idCond
let condBinding = mkSynBinding (Xml.PreXmlDoc.Empty, patCond) (None, false, true, mGuard, DebugPointAtBinding.NoneAtSticky, None, SynExpr.Ident idFirst, mGuard, [], [], None, SynBindingTrivia.Zero)
let setCondExpr = SynExpr.Set (SynExpr.Ident idCond, SynExpr.Ident idFirst, mGuard)
let bindCondExpr = SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtSticky, false, true, patFirst, guardExpr, [], setCondExpr, mGuard, SynExprLetOrUseBangTrivia.Zero)

let whileExpr = SynExpr.While (DebugPointAtWhile.No, SynExpr.Ident idCond, SynExpr.Sequential (DebugPointAtSequential.SuppressBoth, true, innerComp, bindCondExpr, mWhile), mOrig)
SynExpr.LetOrUse (false, false, [ condBinding ], whileExpr, mGuard, SynExprLetOrUseTrivia.Zero)

SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtSticky, false, true, patFirst, guardExpr, [], body, mGuard, SynExprLetOrUseBangTrivia.Zero)

tryTrans CompExprTranslationPass.Initial q varSpace rewrittenWhileExpr translatedCtxt

| SynExpr.TryFinally (innerComp, unwindExpr, _mTryToLast, spTry, spFinally, trivia) ->

let mTry = match spTry with DebugPointAtTry.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Try) | _ -> trivia.TryKeyword
Expand Down Expand Up @@ -1733,14 +1766,15 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol

| _ -> None

/// Check is an expression has no computation expression constructs
/// Check if an expression has no computation expression constructs
and isSimpleExpr comp =

match comp with
| ForEachThenJoinOrGroupJoinOrZipClause false _ -> false
| SynExpr.ForEach _ -> false
| SynExpr.For _ -> false
| SynExpr.While _ -> false
| SynExpr.WhileBang _ -> false
| SynExpr.TryFinally _ -> false
| SynExpr.ImplicitZero _ -> false
| OptionalSequential (JoinOrGroupJoinOrZipClause _, _) -> false
Expand Down
8 changes: 4 additions & 4 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5700,10 +5700,9 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE
error(Error(FSComp.SR.tcConstructRequiresSequenceOrComputations(), m))

| SynExpr.DoBang (_, m)
| SynExpr.LetOrUseBang (range=m) ->
error(Error(FSComp.SR.tcConstructRequiresComputationExpression(), m))

| SynExpr.MatchBang (range=m) ->
| SynExpr.MatchBang (range = m)
| SynExpr.WhileBang (range = m)
| SynExpr.LetOrUseBang (range = m) ->
error(Error(FSComp.SR.tcConstructRequiresComputationExpression(), m))

// Part of 'T.Ident
Expand Down Expand Up @@ -8684,6 +8683,7 @@ and TcImplicitOpItemThen (cenv: cenv) overallTy env id sln tpenv mItem delayed =
| SynExpr.MatchBang _
| SynExpr.LetOrUseBang _
| SynExpr.DoBang _
| SynExpr.WhileBang _
| SynExpr.TraitCall _
| SynExpr.IndexFromEnd _
| SynExpr.IndexRange _
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Driver/CompilerDiagnostics.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,7 @@ type Exception with
| Parser.TOKEN_INLINE -> SR.GetString("Parser.TOKEN.INLINE")
| Parser.TOKEN_WHEN -> SR.GetString("Parser.TOKEN.WHEN")
| Parser.TOKEN_WHILE -> SR.GetString("Parser.TOKEN.WHILE")
| Parser.TOKEN_WHILE_BANG -> SR.GetString("Parser.TOKEN.WHILE.BANG")
| Parser.TOKEN_WITH -> SR.GetString("Parser.TOKEN.WITH")
| Parser.TOKEN_IF -> SR.GetString("Parser.TOKEN.IF")
| Parser.TOKEN_DO -> SR.GetString("Parser.TOKEN.DO")
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,7 @@ keywordDescriptionVal,"Used in a signature to indicate a value, or in a type to
keywordDescriptionVoid,"Indicates the .NET void type. Used when interoperating with other .NET languages."
keywordDescriptionWhen,"Used for Boolean conditions (when guards) on pattern matches and to introduce a constraint clause for a generic type parameter."
keywordDescriptionWhile,"Introduces a looping construct."
keywordDescriptionWhileBang,"Used in computation expressions to introduce a looping construct where the condition is the result of another computation expression."
keywordDescriptionWith,"Used together with the match keyword in pattern matching expressions. Also used in object expressions, record copying expressions, and type extensions to introduce member definitions, and to introduce exception handlers."
keywordDescriptionYield,"Used in a sequence expression to produce a value for a sequence."
keywordDescriptionYieldBang,"Used in a computation expression to append the result of a given computation expression to a collection of results for the containing computation expression."
Expand Down
7 changes: 5 additions & 2 deletions src/Compiler/FSStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SeeAlso" xml:space="preserve">
<value>. See also {0}.</value>
Expand Down Expand Up @@ -1113,4 +1113,7 @@
<data name="ErrorFromAddingTypeEquationTuples" xml:space="preserve">
<value>Type mismatch. Expecting a tuple of length {0} of type\n {1} \nbut given a tuple of length {2} of type\n {3} {4}\n</value>
</data>
<data name="Parser.TOKEN.WHILE.BANG" xml:space="preserve">
<value>keyword 'while!'</value>
</data>
</root>
14 changes: 4 additions & 10 deletions src/Compiler/Service/FSharpParseFileResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,8 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,
for SynInterfaceImpl (bindings = bs) in is do
yield! walkBinds bs

| SynExpr.While (spWhile, e1, e2, _) ->
| SynExpr.While (spWhile, e1, e2, _)
| SynExpr.WhileBang (spWhile, e1, e2, _) ->
yield! walkWhileSeqPt spWhile
yield! walkExpr false e1
yield! walkExpr true e2
Expand Down Expand Up @@ -766,7 +767,8 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,

| SynExpr.Lambda (body = bodyExpr) -> yield! walkExpr true bodyExpr

| SynExpr.Match (matchDebugPoint = spBind; expr = inpExpr; clauses = cl) ->
| SynExpr.Match (matchDebugPoint = spBind; expr = inpExpr; clauses = cl)
| SynExpr.MatchBang (matchDebugPoint = spBind; expr = inpExpr; clauses = cl) ->
yield! walkBindSeqPt spBind
yield! walkExpr false inpExpr

Expand Down Expand Up @@ -848,14 +850,6 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,
yield! walkExpr true eAndBang

yield! walkExpr true bodyExpr

| SynExpr.MatchBang (matchDebugPoint = spBind; expr = inpExpr; clauses = clauses) ->
yield! walkBindSeqPt spBind
yield! walkExpr false inpExpr

for SynMatchClause (whenExpr = whenExpr; resultExpr = resExpr) in clauses do
yield! walkExprOpt true whenExpr
yield! walkExpr true resExpr
]

// Process a class declaration or F# type declaration
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Service/ServiceLexing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ module internal TokenClassifications =
| INLINE
| WHEN
| WHILE
| WHILE_BANG
| WITH
| IF
| THEN
Expand Down Expand Up @@ -1310,6 +1311,7 @@ type FSharpTokenKind =
| ColonEquals
| When
| While
| WhileBang
| With
| Hash
| Ampersand
Expand Down Expand Up @@ -1520,6 +1522,7 @@ type FSharpToken =
| SEMICOLON -> FSharpTokenKind.SemicolonSemicolon
| WHEN -> FSharpTokenKind.When
| WHILE -> FSharpTokenKind.While
| WHILE_BANG -> FSharpTokenKind.WhileBang
| WITH -> FSharpTokenKind.With
| HASH -> FSharpTokenKind.Hash
| AMP -> FSharpTokenKind.Ampersand
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Service/ServiceLexing.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ type public FSharpTokenKind =
| ColonEquals
| When
| While
| WhileBang
| With
| Hash
| Ampersand
Expand Down
20 changes: 6 additions & 14 deletions src/Compiler/Service/ServiceParseTreeWalk.fs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,8 @@ module SyntaxTraversal =
]
|> pick expr

| SynExpr.While (_spWhile, synExpr, synExpr2, _range) ->
| SynExpr.While (_spWhile, synExpr, synExpr2, _range)
| SynExpr.WhileBang (_spWhile, synExpr, synExpr2, _range) ->
[
dive synExpr synExpr.Range traverseSynExpr
dive synExpr2 synExpr2.Range traverseSynExpr
Expand Down Expand Up @@ -559,7 +560,8 @@ module SyntaxTraversal =
|> List.map (fun x -> dive x x.Range (traverseSynMatchClause path))
|> pick expr

| SynExpr.Match (expr = synExpr; clauses = synMatchClauseList) ->
| SynExpr.Match (expr = synExpr; clauses = synMatchClauseList)
| SynExpr.MatchBang (expr = synExpr; clauses = synMatchClauseList) ->
[
yield dive synExpr synExpr.Range traverseSynExpr
yield!
Expand All @@ -568,7 +570,8 @@ module SyntaxTraversal =
]
|> pick expr

| SynExpr.Do (synExpr, _range) -> traverseSynExpr synExpr
| SynExpr.Do (synExpr, _)
| SynExpr.DoBang (synExpr, _) -> traverseSynExpr synExpr

| SynExpr.Assert (synExpr, _range) -> traverseSynExpr synExpr

Expand Down Expand Up @@ -753,17 +756,6 @@ module SyntaxTraversal =
]
|> pick expr

| SynExpr.MatchBang (expr = synExpr; clauses = synMatchClauseList) ->
[
yield dive synExpr synExpr.Range traverseSynExpr
yield!
synMatchClauseList
|> List.map (fun x -> dive x x.Range (traverseSynMatchClause path))
]
|> pick expr

| SynExpr.DoBang (synExpr, _range) -> traverseSynExpr synExpr

| SynExpr.LibraryOnlyILAssembly _ -> None

| SynExpr.LibraryOnlyStaticOptimization _ -> None
Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/Service/ServiceStructure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,8 @@ module Structure =
parseExpr elseExpr
| None -> ()

| SynExpr.While (_, _, e, r) ->
| SynExpr.While (_, _, e, r)
| SynExpr.WhileBang (_, _, e, r) ->
rcheck Scope.While Collapse.Below r r
parseExpr e

Expand Down
4 changes: 2 additions & 2 deletions src/Compiler/SyntaxTree/LexFilter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,7 @@ type LexFilterImpl (
| EOF _ -> false
| _ ->
not (isSameLine()) ||
(match peekNextToken() with TRY | MATCH | MATCH_BANG | IF | LET _ | FOR | WHILE -> true | _ -> false)
(match peekNextToken() with TRY | MATCH | MATCH_BANG | IF | LET _ | FOR | WHILE | WHILE_BANG -> true | _ -> false)

// Look for '=' or '.Id.id.id = ' after an identifier
let rec isLongIdentEquals token =
Expand Down Expand Up @@ -2327,7 +2327,7 @@ type LexFilterImpl (
pushCtxt tokenTup (CtxtFor tokenStartPos)
returnToken tokenLexbufState token

| WHILE, _ ->
| (WHILE | WHILE_BANG), _ ->
if debug then dprintf "WHILE, pushing CtxtWhile(%a)\n" outputPos tokenStartPos
pushCtxt tokenTup (CtxtWhile tokenStartPos)
returnToken tokenLexbufState token
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/SyntaxTree/PrettyNaming.fs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ let keywordsWithDescription: (string * string) list =
"void", FSComp.SR.keywordDescriptionVoid ()
"when", FSComp.SR.keywordDescriptionWhen ()
"while", FSComp.SR.keywordDescriptionWhile ()
"while!", FSComp.SR.keywordDescriptionWhileBang ()
"with", FSComp.SR.keywordDescriptionWith ()
"yield", FSComp.SR.keywordDescriptionYield ()
"yield!", FSComp.SR.keywordDescriptionYieldBang ()
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,8 @@ type SynExpr =

| DoBang of expr: SynExpr * range: range

| WhileBang of whileDebugPoint: DebugPointAtWhile * whileExpr: SynExpr * doExpr: SynExpr * range: range

| LibraryOnlyILAssembly of
ilCode: obj * // this type is ILInstr[] but is hidden to avoid the representation of AbstractIL being public
typeArgs: SynType list *
Expand Down Expand Up @@ -777,6 +779,7 @@ type SynExpr =
| SynExpr.LetOrUseBang (range = m)
| SynExpr.MatchBang (range = m)
| SynExpr.DoBang (range = m)
| SynExpr.WhileBang (range = m)
| SynExpr.Fixed (range = m)
| SynExpr.InterpolatedString (range = m)
| SynExpr.Dynamic (range = m) -> m
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTree.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,9 @@ type SynExpr =
/// Computation expressions only
| DoBang of expr: SynExpr * range: range

/// F# syntax: 'while! ... do ...'
| WhileBang of whileDebugPoint: DebugPointAtWhile * whileExpr: SynExpr * doExpr: SynExpr * range: range

/// Only used in FSharp.Core
| LibraryOnlyILAssembly of
ilCode: obj * // this type is ILInstr[] but is hidden to avoid the representation of AbstractIL being public
Expand Down
8 changes: 4 additions & 4 deletions src/Compiler/SyntaxTree/SyntaxTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -866,15 +866,17 @@ let rec synExprContainsError inpExpr =
walkBinds bs || walkBinds binds

| SynExpr.ForEach (_, _, _, _, _, e1, e2, _)
| SynExpr.While (_, e1, e2, _) -> walkExpr e1 || walkExpr e2
| SynExpr.While (_, e1, e2, _)
| SynExpr.WhileBang (_, e1, e2, _) -> walkExpr e1 || walkExpr e2

| SynExpr.For (identBody = e1; toBody = e2; doBody = e3) -> walkExpr e1 || walkExpr e2 || walkExpr e3

| SynExpr.MatchLambda (_, _, cl, _, _) -> walkMatchClauses cl

| SynExpr.Lambda (body = e) -> walkExpr e

| SynExpr.Match (expr = e; clauses = cl) -> walkExpr e || walkMatchClauses cl
| SynExpr.Match (expr = e; clauses = cl)
| SynExpr.MatchBang (expr = e; clauses = cl) -> walkExpr e || walkMatchClauses cl

| SynExpr.LetOrUse (bindings = bs; body = e) -> walkBinds bs || walkExpr e

Expand Down Expand Up @@ -904,8 +906,6 @@ let rec synExprContainsError inpExpr =

| SynExpr.DotNamedIndexedPropertySet (e1, _, e2, e3, _) -> walkExpr e1 || walkExpr e2 || walkExpr e3

| SynExpr.MatchBang (expr = e; clauses = cl) -> walkExpr e || walkMatchClauses cl

| SynExpr.LetOrUseBang (rhs = e1; body = e2; andBangs = es) ->
walkExpr e1
|| walkExprs
Expand Down
7 changes: 6 additions & 1 deletion src/Compiler/SyntaxTree/SyntaxTrivia.fs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,12 @@ type SynExprLambdaTrivia =
static member Zero: SynExprLambdaTrivia = { ArrowRange = None }

[<NoEquality; NoComparison>]
type SynExprLetOrUseTrivia = { InKeyword: range option }
type SynExprLetOrUseTrivia =
{
InKeyword: range option
}

static member Zero: SynExprLetOrUseTrivia = { InKeyword = None }

[<NoEquality; NoComparison>]
type SynExprLetOrUseBangTrivia =
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/SyntaxTree/SyntaxTrivia.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ type SynExprLetOrUseTrivia =
InKeyword: range option
}

static member Zero: SynExprLetOrUseTrivia

/// Represents additional information for SynExpr.LetOrUseBang
[<NoEquality; NoComparison>]
type SynExprLetOrUseBangTrivia =
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/lex.fsl
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ rule token args skip = parse

| "and!"
{ AND_BANG(false) }

| "while!"
{ WHILE_BANG }

| ident '!'
{ let tok = Keywords.KeywordOrIdentifierToken args lexbuf (lexemeTrimRight lexbuf 1)
Expand Down
Loading