Skip to content

Commit c9167c1

Browse files
authored
Merge pull request #1336 from dsyme/fix-779
Fix processing of interleaved let/sequential bindings
2 parents cd30ad1 + 139eaa0 commit c9167c1

File tree

3 files changed

+1643
-59
lines changed

3 files changed

+1643
-59
lines changed

src/fsharp/TypeChecker.fs

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5343,9 +5343,8 @@ and TcExprNoRecover cenv ty (env: TcEnv) tpenv (expr: SynExpr) =
53435343
if GetCtorShapeCounter env > 0 then AdjustCtorShapeCounter (fun x -> x - 1) env
53445344
else env
53455345

5346-
let tm,tpenv = TcExprThen cenv ty env tpenv expr []
5346+
TcExprThen cenv ty env tpenv expr []
53475347

5348-
tm,tpenv
53495348

53505349
// This recursive entry is only used from one callsite (DiscardAfterMissingQualificationAfterDot)
53515350
// and has been added relatively late in F# 4.0 to preserve the structure of previous code. It pushes a 'delayed' parameter
@@ -5696,8 +5695,8 @@ and TcExprUndelayed cenv overallTy env tpenv (expr: SynExpr) =
56965695
((if cenv.g.compilingFslib then id else mkCallSeq cenv.g m genCollElemTy)
56975696
(mkCoerceExpr(expr,genEnumTy,expr.Range,exprty))),tpenv
56985697

5699-
| SynExpr.LetOrUse (isRec,isUse,binds,body,m) ->
5700-
TcLinearLetExprs (TcExprThatCanBeCtorBody cenv) cenv env overallTy (fun x -> x) tpenv (true(*consume use bindings*),isRec,isUse,binds,body,m)
5698+
| SynExpr.LetOrUse _ ->
5699+
TcLinearExprs (TcExprThatCanBeCtorBody cenv) cenv env overallTy tpenv false expr (fun x -> x)
57015700

57025701
| SynExpr.TryWith (e1,_mTryToWith,clauses,mWithToLast,mTryToLast,spTry,spWith) ->
57035702
let e1',tpenv = TcExpr cenv overallTy env tpenv e1
@@ -5736,17 +5735,7 @@ and TcExprUndelayed cenv overallTy env tpenv (expr: SynExpr) =
57365735

57375736
| SynExpr.Sequential (sp,dir,e1,e2,m) ->
57385737
if dir then
5739-
// Use continuations to cope with long linear sequences
5740-
let rec TcLinearSeqs expr cont =
5741-
match expr with
5742-
| SynExpr.Sequential (sp,true,e1,e2,m) ->
5743-
let e1',_ = TcStmtThatCantBeCtorBody cenv env tpenv e1
5744-
TcLinearSeqs e2 (fun (e2',tpenv) ->
5745-
cont (Expr.Sequential(e1',e2',NormalSeq,sp,m),tpenv))
5746-
5747-
| _ ->
5748-
cont (TcExprThatCanBeCtorBody cenv overallTy env tpenv expr)
5749-
TcLinearSeqs expr (fun res -> res)
5738+
TcLinearExprs (TcExprThatCanBeCtorBody cenv) cenv env overallTy tpenv false expr (fun x -> x)
57505739
else
57515740
// Constructors using "new (...) = <ctor-expr> then <expr>"
57525741
let e1',tpenv = TcExprThatCanBeCtorBody cenv overallTy env tpenv e1
@@ -7946,13 +7935,14 @@ and TcSequenceExpression cenv env tpenv comp overallTy m =
79467935
Some(mkCond spIfToThen SequencePointAtTarget mIfToEndOfElseBranch genOuterTy guardExpr' thenExpr elseExpr, tpenv)
79477936

79487937
// 'let x = expr in expr'
7949-
| SynExpr.LetOrUse (isRec,false (* not a 'use' binding *),binds,body,m) ->
7950-
TcLinearLetExprs
7938+
| SynExpr.LetOrUse (_,false (* not a 'use' binding *),_,_,_) ->
7939+
TcLinearExprs
79517940
(fun ty envinner tpenv e -> tcSequenceExprBody envinner ty tpenv e)
79527941
cenv env overallTy
7953-
(fun x -> x)
79547942
tpenv
7955-
(false(* don't consume 'use' bindings*),isRec,false,binds,body,m) |> Some
7943+
true
7944+
comp
7945+
(fun x -> x) |> Some
79567946

79577947
// 'use x = expr in expr'
79587948
| SynExpr.LetOrUse (_isRec,true,[Binding (_vis,NormalBinding,_,_,_,_,_,pat,_,rhsExpr,_,_spBind)],innerComp,wholeExprMark) ->
@@ -9766,32 +9756,34 @@ and CheckRecursiveBindingIds binds =
97669756
error(Duplicate("value",nm,m))
97679757
else hashOfBinds.[nm] <- b
97689758

9769-
/// Process a sequence of iterated lets "let ... in let ... in ..." in a tail recursive way
9770-
/// This avoids stack overflow on really larger "let" and "letrec" lists
9771-
and TcLinearLetExprs bodyChecker cenv env overallTy builder tpenv (processUseBindings,isRec,isUse,binds,body,m) =
9772-
assert (not isUse || processUseBindings)
9759+
/// Process a sequence of seqeuntials mixed with iterated lets "let ... in let ... in ..." in a tail recursive way
9760+
/// This avoids stack overflow on really large "let" and "letrec" lists
9761+
and TcLinearExprs bodyChecker cenv env overallTy tpenv isCompExpr expr cont =
9762+
match expr with
9763+
| SynExpr.Sequential (sp,true,e1,e2,m) when not isCompExpr ->
9764+
let e1',_ = TcStmtThatCantBeCtorBody cenv env tpenv e1
9765+
// tailcall
9766+
TcLinearExprs bodyChecker cenv env overallTy tpenv isCompExpr e2 (fun (e2',tpenv) ->
9767+
cont (Expr.Sequential(e1',e2',NormalSeq,sp,m),tpenv))
9768+
9769+
| SynExpr.LetOrUse (isRec,isUse,binds,body,m) when not (isUse && isCompExpr) ->
97739770

9774-
if isRec then
9775-
// TcLinearLetExprs processes at most one recursive binding
9776-
CheckRecursiveBindingIds binds
9777-
let binds = List.map (fun x -> RecDefnBindingInfo(ExprContainerInfo,NoNewSlots,ExpressionBinding,x)) binds
9778-
if isUse then errorR(Error(FSComp.SR.tcBindingCannotBeUseAndRec(),m))
9779-
let binds,envinner,tpenv = TcLetrec ErrorOnOverrides cenv env tpenv (binds,m,m)
9780-
let bodyExpr,tpenv = bodyChecker overallTy envinner tpenv body
9781-
let bodyExpr = bindLetRec (FlatList.ofList binds) m bodyExpr
9782-
fst (builder (bodyExpr,overallTy)),tpenv
9783-
else
9784-
// TcLinearLetExprs processes multiple 'let' bindings in a tail recursive way
9785-
// We process one binding, then look for additional linear bindings and accumulate the builder continuation.
9786-
// Don't processes 'use' bindings (e.g. in sequence expressions) unless directed to.
9787-
let mkf,envinner,tpenv = TcLetBinding cenv isUse env ExprContainerInfo ExpressionBinding tpenv (binds,m,body.Range)
9788-
let builder' x = builder (mkf x)
9789-
match body with
9790-
| SynExpr.LetOrUse (isRec',isUse',binds',bodyExpr,m') when (not isUse' || processUseBindings) ->
9791-
TcLinearLetExprs bodyChecker cenv envinner overallTy builder' tpenv (processUseBindings,isRec',isUse',binds',bodyExpr,m')
9792-
| _ ->
9771+
if isRec then
9772+
// TcLinearExprs processes at most one recursive binding, this is not tailcalling
9773+
CheckRecursiveBindingIds binds
9774+
let binds = List.map (fun x -> RecDefnBindingInfo(ExprContainerInfo,NoNewSlots,ExpressionBinding,x)) binds
9775+
if isUse then errorR(Error(FSComp.SR.tcBindingCannotBeUseAndRec(),m))
9776+
let binds,envinner,tpenv = TcLetrec ErrorOnOverrides cenv env tpenv (binds,m,m)
97939777
let bodyExpr,tpenv = bodyChecker overallTy envinner tpenv body
9794-
fst (builder' (bodyExpr,overallTy)),tpenv
9778+
let bodyExpr = bindLetRec (FlatList.ofList binds) m bodyExpr
9779+
cont (bodyExpr,tpenv)
9780+
else
9781+
// TcLinearExprs processes multiple 'let' bindings in a tail recursive way
9782+
let mkf,envinner,tpenv = TcLetBinding cenv isUse env ExprContainerInfo ExpressionBinding tpenv (binds,m,body.Range)
9783+
TcLinearExprs bodyChecker cenv envinner overallTy tpenv isCompExpr body (fun (x,tpenv) ->
9784+
cont (fst (mkf (x,overallTy)), tpenv))
9785+
| _ ->
9786+
cont (bodyChecker overallTy env tpenv expr)
97959787

97969788
/// Typecheck and compile pattern-matching constructs
97979789
and TcAndPatternCompileMatchClauses mExpr matchm actionOnFailure cenv inputTy resultTy env tpenv clauses =
@@ -10437,7 +10429,7 @@ and TcLetBinding cenv isUse env containerInfo declKind tpenv (binds,bindsm,scope
1043710429
else
1043810430
(bodyExpr,bodyExprTy)
1043910431

10440-
((mkRhsBind << mkPatBind << mkCleanup << mkf_sofar),
10432+
((mkf_sofar >> mkCleanup >> mkPatBind >> mkRhsBind),
1044110433
AddLocalValMap cenv.tcSink scopem prelimRecValues env,
1044210434
tpenv))
1044310435

0 commit comments

Comments
 (0)