Skip to content

Commit 1bc2896

Browse files
authored
Implement Langversion switch for implicit yields (#7166)
* Langversion tests for ImplicitYield * tweak * Update single-test.fs removed some printfns * Feedback * One more cleanup * Uncore une fois * Update TypeChecker.fs Fix bug
1 parent 658caa2 commit 1bc2896

37 files changed

Lines changed: 389 additions & 50 deletions

src/fsharp/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ tcUseWhenPatternGuard,"Character range matches have been removed in F#. Consider
574574
736,tcExprUndelayed,"TcExprUndelayed: delayed"
575575
737,tcExpressionRequiresSequence,"This expression form may only be used in sequence and computation expressions"
576576
738,tcInvalidObjectExpressionSyntaxForm,"Invalid object expression. Objects without overrides or interfaces should use the expression form 'new Type(args)' without braces."
577+
739,tcInvalidObjectSequenceOrRecordExpression,"Invalid object, sequence or record expression"
577578
740,tcInvalidSequenceExpressionSyntaxForm,"Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq {{ ... }}'"
578579
tcExpressionWithIfRequiresParenthesis,"This list or array expression includes an element of the form 'if ... then ... else'. Parenthesize this expression to indicate it is an individual element of the list or array, to disambiguate this from a list generated using a sequence expression"
579580
741,tcUnableToParseFormatString,"Unable to parse format string '%s'"

src/fsharp/LanguageFeatures.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ type LanguageFeature =
2525
| WildCardInForLoop = 3
2626
| RelaxWhitespace = 4
2727
| NameOf = 5
28+
| ImplicitYield = 6
29+
2830

2931
/// LanguageVersion management
3032
type LanguageVersion (specifiedVersion) =
@@ -49,6 +51,7 @@ type LanguageVersion (specifiedVersion) =
4951
LanguageFeature.WildCardInForLoop, previewVersion
5052
LanguageFeature.RelaxWhitespace, previewVersion
5153
LanguageFeature.NameOf, previewVersion
54+
LanguageFeature.ImplicitYield, previewVersion
5255
|]
5356

5457
let specified =

src/fsharp/LanguageFeatures.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ type LanguageFeature =
1212
| WildCardInForLoop = 3
1313
| RelaxWhitespace = 4
1414
| NameOf = 5
15+
| ImplicitYield = 6
16+
1517

1618
/// LanguageVersion management
1719
type LanguageVersion =

src/fsharp/TypeChecker.fs

Lines changed: 97 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3518,14 +3518,84 @@ let (|ExprAsPat|_|) (f: SynExpr) =
35183518
None
35193519
| _ -> None
35203520

3521+
/// Check if a computation or sequence expression is syntactically free of 'yield' (though not yield!)
3522+
let YieldFree cenv expr =
3523+
if cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield then
3524+
3525+
// Implement yield free logic for F# Language including the LanguageFeature.ImplicitYield
3526+
let rec YieldFree expr =
3527+
match expr with
3528+
| SynExpr.Sequential (_, _, e1, e2, _) ->
3529+
YieldFree e1 && YieldFree e2
3530+
3531+
| SynExpr.IfThenElse (_, e2, e3opt, _, _, _, _) ->
3532+
YieldFree e2 && Option.forall YieldFree e3opt
3533+
3534+
| SynExpr.TryWith (e1, _, clauses, _, _, _, _) ->
3535+
YieldFree e1 && clauses |> List.forall (fun (Clause(_, _, e, _, _)) -> YieldFree e)
3536+
3537+
| (SynExpr.Match (_, _, clauses, _) | SynExpr.MatchBang (_, _, clauses, _)) ->
3538+
clauses |> List.forall (fun (Clause(_, _, e, _, _)) -> YieldFree e)
3539+
3540+
| SynExpr.For (_, _, _, _, _, body, _)
3541+
| SynExpr.TryFinally (body, _, _, _, _)
3542+
| SynExpr.LetOrUse (_, _, _, body, _)
3543+
| SynExpr.While (_, _, body, _)
3544+
| SynExpr.ForEach (_, _, _, _, _, body, _) ->
3545+
YieldFree body
3546+
3547+
| SynExpr.LetOrUseBang(_, _, _, _, _, body, _) ->
3548+
YieldFree body
3549+
3550+
| SynExpr.YieldOrReturn((true, _), _, _) -> false
3551+
3552+
| _ -> true
3553+
3554+
YieldFree expr
3555+
else
3556+
// Implement yield free logic for F# Language without the LanguageFeature.ImplicitYield
3557+
let rec YieldFree expr =
3558+
match expr with
3559+
| SynExpr.Sequential (_, _, e1, e2, _) ->
3560+
YieldFree e1 && YieldFree e2
3561+
3562+
| SynExpr.IfThenElse (_, e2, e3opt, _, _, _, _) ->
3563+
YieldFree e2 && Option.forall YieldFree e3opt
3564+
3565+
| SynExpr.TryWith (e1, _, clauses, _, _, _, _) ->
3566+
YieldFree e1 && clauses |> List.forall (fun (Clause(_, _, e, _, _)) -> YieldFree e)
3567+
3568+
| (SynExpr.Match (_, _, clauses, _) | SynExpr.MatchBang (_, _, clauses, _)) ->
3569+
clauses |> List.forall (fun (Clause(_, _, e, _, _)) -> YieldFree e)
3570+
3571+
| SynExpr.For (_, _, _, _, _, body, _)
3572+
| SynExpr.TryFinally (body, _, _, _, _)
3573+
| SynExpr.LetOrUse (_, _, _, body, _)
3574+
| SynExpr.While (_, _, body, _)
3575+
| SynExpr.ForEach (_, _, _, _, _, body, _) ->
3576+
YieldFree body
3577+
3578+
| SynExpr.LetOrUseBang _
3579+
| SynExpr.YieldOrReturnFrom _
3580+
| SynExpr.YieldOrReturn _
3581+
| SynExpr.ImplicitZero _
3582+
| SynExpr.Do _ -> false
3583+
3584+
| _ -> true
3585+
3586+
YieldFree expr
3587+
3588+
35213589
/// Determine if a syntactic expression inside 'seq { ... }' or '[...]' counts as a "simple sequence
35223590
/// of semicolon separated values". For example [1;2;3].
3591+
/// 'acceptDeprecated' is true for the '[ ... ]' case, where we allow the syntax '[ if g then t else e ]' but ask it to be parenthesized
35233592
///
3524-
let (|SimpleSemicolonSequence|_|) cexpr =
3593+
let (|SimpleSemicolonSequence|_|) cenv acceptDeprecated cexpr =
35253594

35263595
let IsSimpleSemicolonSequenceElement expr =
3527-
match expr with
3528-
| SynExpr.IfThenElse _
3596+
match expr with
3597+
| SynExpr.IfThenElse _ when acceptDeprecated && YieldFree cenv expr -> true
3598+
| SynExpr.IfThenElse _
35293599
| SynExpr.TryWith _
35303600
| SynExpr.Match _
35313601
| SynExpr.For _
@@ -5734,6 +5804,13 @@ and TcExprUndelayedNoType cenv env tpenv synExpr: Expr * TType * _ =
57345804
expr, overallTy, tpenv
57355805

57365806
and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) =
5807+
5808+
// LanguageFeatures.ImplicitYield do not require this validation
5809+
let implicitYieldEnabled = cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield
5810+
let validateObjectSequenceOrRecordExpression = not implicitYieldEnabled
5811+
let validateExpressionWithIfRequiresParenethesis = not implicitYieldEnabled
5812+
let acceptDeprecatedIfThenExpression = not implicitYieldEnabled
5813+
57375814
match synExpr with
57385815
| SynExpr.Paren (expr2, _, _, mWholeExprIncludingParentheses) ->
57395816
// We invoke CallExprHasTypeSink for every construct which is atomic in the syntax, i.e. where a '.' immediately following the
@@ -5932,6 +6009,8 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) =
59326009
match comp with
59336010
| SynExpr.New _ ->
59346011
errorR(Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm(), m))
6012+
| SimpleSemicolonSequence cenv false _ when validateObjectSequenceOrRecordExpression ->
6013+
errorR(Error(FSComp.SR.tcInvalidObjectSequenceOrRecordExpression(), m))
59356014
| _ ->
59366015
()
59376016
if not !isNotNakedRefCell && not cenv.g.compilingFslib then
@@ -5941,9 +6020,12 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) =
59416020

59426021
| SynExpr.ArrayOrListOfSeqExpr (isArray, comp, m) ->
59436022
CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.DisplayEnv, env.eAccessRights)
5944-
59456023
match comp with
5946-
| SynExpr.CompExpr (_, _, SimpleSemicolonSequence elems, _) ->
6024+
| SynExpr.CompExpr (_, _, (SimpleSemicolonSequence cenv acceptDeprecatedIfThenExpression elems as body), _) ->
6025+
match body with
6026+
| SimpleSemicolonSequence cenv false _ -> ()
6027+
| _ when validateExpressionWithIfRequiresParenethesis -> errorR(Deprecated(FSComp.SR.tcExpressionWithIfRequiresParenthesis(), m))
6028+
| _ -> ()
59476029

59486030
let replacementExpr =
59496031
if isArray then
@@ -7331,31 +7413,6 @@ and TcQuotationExpr cenv overallTy env tpenv (_oper, raw, ast, isFromQueryExpres
73317413
/// Ignores an attribute
73327414
and IgnoreAttribute _ = None
73337415

7334-
/// Check if a computation or sequence expression is syntactically free of 'yield' (though not yield!)
7335-
and YieldFree expr =
7336-
match expr with
7337-
| SynExpr.Sequential (_, _, e1, e2, _) -> YieldFree e1 && YieldFree e2
7338-
| SynExpr.IfThenElse (_, e2, e3opt, _, _, _, _) -> YieldFree e2 && Option.forall YieldFree e3opt
7339-
| SynExpr.TryWith (e1, _, clauses, _, _, _, _) ->
7340-
YieldFree e1 && clauses |> List.forall (fun (Clause(_, _, e, _, _)) -> YieldFree e)
7341-
| (SynExpr.Match (_, _, clauses, _) | SynExpr.MatchBang (_, _, clauses, _)) ->
7342-
clauses |> List.forall (fun (Clause(_, _, e, _, _)) -> YieldFree e)
7343-
| SynExpr.For (_, _, _, _, _, body, _)
7344-
| SynExpr.TryFinally (body, _, _, _, _)
7345-
| SynExpr.LetOrUse (_, _, _, body, _)
7346-
| SynExpr.LetOrUseBang(_, _, _, _, _, body, _)
7347-
| SynExpr.LetOrUse (_, _, _, body, _)
7348-
| SynExpr.While (_, _, body, _)
7349-
| SynExpr.ForEach (_, _, _, _, _, body, _) ->
7350-
YieldFree body
7351-
7352-
// 'yield!' in expressions doesn't trigger the 'yield free' rule
7353-
//| SynExpr.YieldOrReturnFrom _
7354-
| SynExpr.YieldOrReturn((true, _), _, _) ->
7355-
false
7356-
7357-
| _ -> true
7358-
73597416
/// Used for all computation expressions except sequence expressions
73607417
and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builderTy tpenv (comp: SynExpr) =
73617418

@@ -7814,7 +7871,9 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
78147871
// If there are no 'yield' in the computation expression, and the builder supports 'Yield',
78157872
// then allow the type-directed rule interpreting non-unit-typed expressions in statement
78167873
// positions as 'yield'. 'yield!' may be present in the computation expression.
7817-
let enableImplicitYield = hasMethInfo "Yield" && hasMethInfo "Combine" && hasMethInfo "Delay" && YieldFree comp
7874+
let enableImplicitYield =
7875+
cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield
7876+
&& (hasMethInfo "Yield" && hasMethInfo "Combine" && hasMethInfo "Delay" && YieldFree cenv comp)
78187877

78197878
// q - a flag indicating if custom operators are allowed. They are not allowed inside try/with, try/finally, if/then/else etc.
78207879
// varSpace - a lazy data structure indicating the variables bound so far in the overall computation
@@ -8200,8 +8259,7 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
82008259
| _ ->
82018260
Some (trans true q varSpace innerComp2 (fun holeFill ->
82028261
let fillExpr =
8203-
if enableImplicitYield then
8204-
8262+
if enableImplicitYield then
82058263
// When implicit yields are enabled, then if the 'innerComp1' checks as type
82068264
// 'unit' we interpret the expression as a sequential, and when it doesn't
82078265
// have type 'unit' we interpret it as a 'Yield + Combine'.
@@ -8443,7 +8501,9 @@ and TcSequenceExpression cenv env tpenv comp overallTy m =
84438501
// If there are no 'yield' in the computation expression then allow the type-directed rule
84448502
// interpreting non-unit-typed expressions in statement positions as 'yield'. 'yield!' may be
84458503
// present in the computation expression.
8446-
let enableImplicitYield = YieldFree comp
8504+
let enableImplicitYield =
8505+
cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield
8506+
&& (YieldFree cenv comp)
84478507

84488508
let mkDelayedExpr (coreExpr: Expr) =
84498509
let m = coreExpr.Range
@@ -8505,6 +8565,9 @@ and TcSequenceExpression cenv env tpenv comp overallTy m =
85058565

85068566
Some(mkSeqFinally cenv env innerExprMark genOuterTy innerExpr unwindExpr, tpenv)
85078567

8568+
| SynExpr.Paren (_, _, _, m) when not (cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield)->
8569+
error(Error(FSComp.SR.tcConstructIsAmbiguousInSequenceExpression(), m))
8570+
85088571
| SynExpr.ImplicitZero m ->
85098572
Some(mkSeqEmpty cenv env m genOuterTy, tpenv )
85108573

src/fsharp/xlf/FSComp.txt.cs.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,11 @@
28472847
<target state="translated">Neplatný objektový výraz. U objektů bez přepsání nebo rozhraní by se měl výraz formulovat pomocí notace new Type(args) bez složených závorek.</target>
28482848
<note />
28492849
</trans-unit>
2850+
<trans-unit id="tcInvalidObjectSequenceOrRecordExpression">
2851+
<source>Invalid object, sequence or record expression</source>
2852+
<target state="translated">Neplatný výraz objektu, pořadí nebo záznamu</target>
2853+
<note />
2854+
</trans-unit>
28502855
<trans-unit id="tcInvalidSequenceExpressionSyntaxForm">
28512856
<source>Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq {{ ... }}'</source>
28522857
<target state="translated">Neplatný výraz záznamu, pořadí nebo výpočtu. Výrazy pořadí by měly mít notaci seq {{ ... }}.</target>

src/fsharp/xlf/FSComp.txt.de.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,11 @@
28472847
<target state="translated">Ungültiger Objektausdruck. Objekte ohne Überschreibungen oder Schnittstellen sollten das Ausdrucksformat "new Type(args)" ohne geschweifte Klammern verwenden.</target>
28482848
<note />
28492849
</trans-unit>
2850+
<trans-unit id="tcInvalidObjectSequenceOrRecordExpression">
2851+
<source>Invalid object, sequence or record expression</source>
2852+
<target state="translated">Ungültiger Objekt-, Sequenz- oder Datensatzausdruck.</target>
2853+
<note />
2854+
</trans-unit>
28502855
<trans-unit id="tcInvalidSequenceExpressionSyntaxForm">
28512856
<source>Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq {{ ... }}'</source>
28522857
<target state="translated">Ungültiger Datensatz-, Sequenz- oder Berechnungsausdruck. Sequenzausdrücke müssen das Format "seq {{ ... }}" besitzen.</target>

src/fsharp/xlf/FSComp.txt.es.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,11 @@
28472847
<target state="translated">Expresión de objeto no válida. Los objetos sin invalidaciones ni interfaces deben usar el formato de expresión 'new Type(args)' sin llaves.</target>
28482848
<note />
28492849
</trans-unit>
2850+
<trans-unit id="tcInvalidObjectSequenceOrRecordExpression">
2851+
<source>Invalid object, sequence or record expression</source>
2852+
<target state="translated">Expresión de objeto, secuencia o registro no válida.</target>
2853+
<note />
2854+
</trans-unit>
28502855
<trans-unit id="tcInvalidSequenceExpressionSyntaxForm">
28512856
<source>Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq {{ ... }}'</source>
28522857
<target state="translated">Expresión de registro, secuencia o cómputo no válida. Las expresiones de secuencia deben tener el formato 'seq {{ ... }}'.</target>

src/fsharp/xlf/FSComp.txt.fr.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,11 @@
28472847
<target state="translated">Expression d'objet non valide. Les objets sans substitutions ou interfaces doivent utiliser la forme d'expression 'new Type(args)' sans accolades.</target>
28482848
<note />
28492849
</trans-unit>
2850+
<trans-unit id="tcInvalidObjectSequenceOrRecordExpression">
2851+
<source>Invalid object, sequence or record expression</source>
2852+
<target state="translated">Expression d'objet, de séquence ou d'enregistrement non valide</target>
2853+
<note />
2854+
</trans-unit>
28502855
<trans-unit id="tcInvalidSequenceExpressionSyntaxForm">
28512856
<source>Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq {{ ... }}'</source>
28522857
<target state="translated">Expression d'enregistrement, de séquence ou de calcul non valide. Les expressions de séquence doivent avoir le format 'seq {{ ... }}'</target>

src/fsharp/xlf/FSComp.txt.it.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,11 @@
28472847
<target state="translated">Espressione oggetto non valida. Gli oggetti senza override o interfacce devono usare il formato di espressione 'new Type(args)' senza parentesi graffe.</target>
28482848
<note />
28492849
</trans-unit>
2850+
<trans-unit id="tcInvalidObjectSequenceOrRecordExpression">
2851+
<source>Invalid object, sequence or record expression</source>
2852+
<target state="translated">Espressione record, sequenza o oggetto non valida</target>
2853+
<note />
2854+
</trans-unit>
28502855
<trans-unit id="tcInvalidSequenceExpressionSyntaxForm">
28512856
<source>Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq {{ ... }}'</source>
28522857
<target state="translated">Espressione di calcolo, sequenza o record non valida. Il formato delle espressioni sequenza deve essere 'seq {{ ... }}'</target>

src/fsharp/xlf/FSComp.txt.ja.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,11 @@
28472847
<target state="translated">オブジェクト式が無効です。オーバーライドまたはインターフェイスがないオブジェクトには、かっこなしで 'new Type(args)' という形式の式を使用してください。</target>
28482848
<note />
28492849
</trans-unit>
2850+
<trans-unit id="tcInvalidObjectSequenceOrRecordExpression">
2851+
<source>Invalid object, sequence or record expression</source>
2852+
<target state="translated">オブジェクト式、シーケンス式、またはレコード式が無効です</target>
2853+
<note />
2854+
</trans-unit>
28502855
<trans-unit id="tcInvalidSequenceExpressionSyntaxForm">
28512856
<source>Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq {{ ... }}'</source>
28522857
<target state="translated">無効なレコード、シーケンス式、またはコンピュテーション式です。シーケンス式は 'seq {{ ... }}' という形式にしてください。</target>

0 commit comments

Comments
 (0)