@@ -191,90 +191,6 @@ module Util =
191191 | None -> failwithf $" Cannot find DecisionTree target %i {targetIndex}"
192192 | Some( idents, target) -> idents, target
193193
194- let isConditionalStament ctx guard thenExpr elseExpr =
195- isStatement ctx guard
196- || isStatement ctx thenExpr
197- || isStatement ctx elseExpr
198-
199- let isStatement ctx ( e : Fable.Expr ) =
200- match e with
201- | Fable.Unresolved _
202- | Fable.Import _ | Fable.IdentExpr _ -> false
203-
204- | Fable.Test( e,_,_) | Fable.TypeCast( e,_) -> isStatement ctx e
205- | Fable.Get( e, kind, _, _) ->
206- match kind with
207- | Fable.ListHead | Fable.ListTail | Fable.OptionValue | Fable.TupleIndex _ | Fable.UnionTag
208- | Fable.UnionField _ | Fable.FieldGet _ -> isStatement ctx e
209- | Fable.ExprGet e2 -> isStatement ctx e || isStatement ctx e2
210-
211- // Closures cannot be statements because they create their own scope
212- | Fable.Lambda _ | Fable.Delegate _ | Fable.ObjectExpr _ -> false
213-
214- | Fable.Value( v,_) ->
215- match v with
216- | Fable.UnitConstant _ -> true
217- | Fable.ThisValue _ | Fable.BaseValue _
218- | Fable.TypeInfo _ | Fable.Null _
219- | Fable.BoolConstant _ | Fable.CharConstant _ | Fable.StringConstant _
220- | Fable.NumberConstant _ | Fable.RegexConstant _ -> false
221-
222- | Fable.NewRecord( e,_,_)
223- | Fable.NewAnonymousRecord( e,_,_)
224- | Fable.NewUnion( e,_,_,_)
225- | Fable.StringTemplate(_,_, e)
226- | Fable.NewTuple( e,_) -> List.exists ( isStatement ctx) e
227- | Fable.NewArray( kind,_,_) ->
228- match kind with
229- | Fable.ArrayValues e -> List.exists ( isStatement ctx) e
230- | Fable.ArrayAlloc e
231- | Fable.ArrayFrom e -> isStatement ctx e
232- | Fable.NewOption( Some e,_,_) -> isStatement ctx e
233- | Fable.NewOption( None,_,_) -> false
234- | Fable.NewList( Some( e1, e2),_) -> isStatement ctx e1 || isStatement ctx e2
235- | Fable.NewList( None,_) -> false
236-
237- | Fable.CurriedApply( callee, args, _, _) -> callee::( discardSingleUnitArg args) |> List.exists ( isStatement ctx)
238- | Fable.Call( e1, info, _, _) -> e1 :: ( Option.toList info.ThisArg) @ ( discardSingleUnitArg info.Args) |> List.exists ( isStatement ctx)
239- | Fable.Operation( kind, _, _) ->
240- match kind with
241- | Fable.Unary(_, operand) -> isStatement ctx operand
242- | Fable.Binary(_, left, right) -> isStatement ctx left || isStatement ctx right
243- | Fable.Logical(_, left, right) -> isStatement ctx left || isStatement ctx right
244-
245- | Fable.Emit( i,_,_) ->
246- i.IsStatement
247- || ( Option.toList i.CallInfo.ThisArg) @ ( discardSingleUnitArg i.CallInfo.Args) |> List.exists ( isStatement ctx)
248-
249- | Fable.Set _
250- | Fable.Let _
251- | Fable.LetRec _
252- | Fable.Sequential _
253- | Fable.TryCatch _
254- | Fable.ForLoop _
255- | Fable.WhileLoop _ -> true
256-
257- | Fable.Extended( kind, _) ->
258- match kind with
259- | Fable.RegionStart _ -> true
260- | Fable.Throw( Some e, _) -> isStatement ctx e
261- | Fable.Throw( None, _)
262- | Fable.Debugger _
263- | Fable.Curry _ -> false
264-
265- | Fable.DecisionTree( e, targets) ->
266- // We should also check if one target is duplicated
267- List.length targets > 2
268- || isStatement { ctx with DecisionTargets = targets } e
269- || List.exists ( snd >> ( isStatement ctx)) targets
270-
271- | Fable.DecisionTreeSuccess( targetIndex,_, _) ->
272- getDecisionTarget ctx targetIndex
273- |> snd |> isStatement ctx
274-
275- | Fable.IfThenElse( guard, thenExpr, elseExpr,_) ->
276- elseExpr.Type = Fable.Unit || isConditionalStament ctx guard thenExpr elseExpr
277-
278194 let isInt64OrLess = function
279195 | Fable.Number( DartInt, _) -> true
280196 | _ -> false
@@ -441,7 +357,7 @@ module Util =
441357 statements, expr
442358 else
443359 let ident = getUniqueNameInDeclarationScope ctx " tmp" |> makeImmutableIdent expr.Type
444- let varDecl = localVarDecl ident Final expr
360+ let varDecl = Statement.variableDeclaration ( ident, Final, expr)
445361 statements @ [ varDecl], ident.Expr
446362 | _ -> statements, ignoreExpr com ctx None
447363
@@ -464,7 +380,7 @@ module Util =
464380 calleeStatements @ argStatements, callee
465381 else
466382 let ident = getUniqueNameInDeclarationScope ctx " tmp" |> makeImmutableIdent callee.Type
467- let varDecl = localVarDecl ident Final callee
383+ let varDecl = Statement.variableDeclaration ( ident, Final, callee)
468384 calleeStatements @ [ varDecl] @ argStatements, ident.Expr
469385
470386 let transformExprsAndResolve com ctx returnStrategy exprs transformExprs =
@@ -486,13 +402,6 @@ module Util =
486402 let statements2 , capturedExpr = transformExprs exprs[ 0 ] exprs[ 1 ] |> resolveExpr returnStrategy
487403 statements @ statements2, capturedExpr
488404
489- let transformExprsAndResolve3 com ctx returnStrategy expr0 expr1 expr2 transformExprs =
490- List.map ( transform com ctx Capture) [ expr0; expr1; expr2]
491- |> combineCapturedExprs com ctx
492- |> fun ( statements , exprs ) ->
493- let statements2 , capturedExpr = transformExprs exprs[ 0 ] exprs[ 1 ] exprs[ 2 ] |> resolveExpr returnStrategy
494- statements @ statements2, capturedExpr
495-
496405 let ignoreExpr com ctx = function
497406 | None -> Expression.nullLiteral Void
498407 | Some expr -> libCall com ctx Fable.Unit " Types" " ignore" [ expr]
@@ -839,7 +748,7 @@ module Util =
839748 | Fable.DeclaredType( baseEnt, _), _
840749 when typeImplementsOrExtends com baseEnt expr.Type ->
841750 com.Transform( ctx, returnStrategy, expr)
842-
751+
843752 | Fable.Any, _ -> com.Transform( ctx, returnStrategy, expr)
844753 | Fable.Unit, _ -> com.Transform( ctx, ReturnVoid, expr)
845754
@@ -851,8 +760,8 @@ module Util =
851760
852761 // TODO: Try to identify type testing in the catch clause and use Dart's `on ...` exception checking
853762 let transformTryCatch com ctx _r returnStrategy ( body : Fable.Expr , catch , finalizer ) =
854- let returnStrategy , prevStmnt , captureExpr =
855- convertCaptureStrategyIntoAssign com ctx body.Type returnStrategy
763+ let prevStmnt , returnStrategy , captureExpr =
764+ convertCaptureStrategyIntoAssign com ctx body.Type [] returnStrategy
856765 // try .. catch statements cannot be tail call optimized
857766 let ctx = { ctx with TailCallOpportunity = None }
858767 let handlers =
@@ -869,37 +778,45 @@ module Util =
869778
870779 /// Branching expressions like conditionals, decision trees or try catch cannot capture
871780 /// the resulting expression at once so declare a variable and assign the potential results to it
872- let convertCaptureStrategyIntoAssign com ctx t returnStrategy =
781+ let convertCaptureStrategyIntoAssign com ctx t prevStatements returnStrategy =
873782 match returnStrategy with
874783 | Capture ->
875784 let t = transformType com ctx t
876785 let ident = getUniqueNameInDeclarationScope ctx " tmp" |> makeImmutableIdent t
877786 let varDecl = Statement.variableDeclaration( ident, Var)
878- Assign ident.Expr, [ varDecl ] , Some ident.Expr
879- | _ -> returnStrategy , [] , None
787+ varDecl :: prevStatements , Assign ident.Expr, Some ident.Expr
788+ | _ -> prevStatements , returnStrategy , None
880789
881790 let transformConditional ( com : IDartCompiler ) ctx _r returnStrategy guardExpr thenExpr elseExpr =
882- let asStatement =
791+ let prevStmnt , guardExpr = transformAndCaptureExpr com ctx guardExpr
792+
793+ match guardExpr with
794+ | Literal( BooleanLiteral( value= value)) ->
795+ let bodyStmnt , captureExpr = com.Transform( ctx, returnStrategy, if value then thenExpr else elseExpr)
796+ prevStmnt @ bodyStmnt, captureExpr
797+
798+ | guardExpr ->
799+ let transformAsStatement prevStmnt returnStrategy captureExpr =
800+ let thenStmnt , capturedThen = com.Transform( ctx, returnStrategy, thenExpr)
801+ let elseStmnt , capturedElse = com.Transform( ctx, returnStrategy, elseExpr)
802+ prevStmnt @ [ Statement.ifStatement( guardExpr, thenStmnt, elseStmnt)], captureExpr
803+
804+ // If strategy is Capture, try to transform as conditional expression.
805+ // Note we need to transform again thenExpr/elseExpr with Assign strategy if we cannot
806+ // use conditional expression, but I cannot think of a more efficient way at the moment
883807 match returnStrategy with
884- | ReturnVoid -> true
885- | Target _ -> true // Compile as statement so values can be bound
886- | Capture | Assign _ -> isConditionalStament ctx guardExpr thenExpr elseExpr
887- | Return -> Option.isSome ctx.TailCallOpportunity || isConditionalStament ctx guardExpr thenExpr elseExpr
888- if not asStatement then
889- transformExprsAndResolve3 com ctx returnStrategy guardExpr thenExpr elseExpr
890- ( fun guardExpr thenExpr elseExpr -> Expression.conditionalExpression( guardExpr, thenExpr, elseExpr))
891- else
892- let prevStmnt , guardExpr = transformAndCaptureExpr com ctx guardExpr
893- match guardExpr with
894- | Literal( BooleanLiteral( value= value)) ->
895- let bodyStmnt , captureExpr = com.Transform( ctx, returnStrategy, if value then thenExpr else elseExpr)
896- prevStmnt @ bodyStmnt, captureExpr
897- | guardExpr ->
898- let returnStrategy , prevStmnt2 , captureExpr =
899- convertCaptureStrategyIntoAssign com ctx thenExpr.Type returnStrategy
900- let thenStmnt , _ = com.Transform( ctx, returnStrategy, thenExpr)
901- let elseStmnt , _ = com.Transform( ctx, returnStrategy, elseExpr)
902- prevStmnt @ prevStmnt2 @ [ Statement.ifStatement( guardExpr, thenStmnt, elseStmnt)], captureExpr
808+ | Capture ->
809+ match com.Transform( ctx, Capture, thenExpr) with
810+ | [], Some capturedThenExpr ->
811+ match com.Transform( ctx, Capture, elseExpr) with
812+ | [], Some capturedElseExpr ->
813+ prevStmnt, Expression.conditionalExpression( guardExpr, capturedThenExpr, capturedElseExpr) |> Some
814+ | _ ->
815+ convertCaptureStrategyIntoAssign com ctx thenExpr.Type prevStmnt returnStrategy |||> transformAsStatement
816+ | _ ->
817+ convertCaptureStrategyIntoAssign com ctx thenExpr.Type prevStmnt returnStrategy |||> transformAsStatement
818+ | _ ->
819+ transformAsStatement prevStmnt returnStrategy None
903820
904821 let transformGet ( com : IDartCompiler ) ctx _range t returnStrategy kind fableExpr =
905822
@@ -1016,24 +933,23 @@ module Util =
1016933 let stmnts2 , _ = transform com ctx ( Assign toBeSet) value
1017934 stmnts1 @ stmnts2
1018935
1019- let localVarDecl ( ident : Ident ) kind value =
1020- match kind, value with
1021- | Final, AnonymousFunction( args, body, genParams, returnType) ->
1022- let args = args |> List.map FunctionArg
1023- let genParams = genParams |> List.map ( fun g -> { Name = g; Extends = None })
1024- Statement.functionDeclaration( ident.Name, args, body, returnType, genParams= genParams)
1025- | _ ->
1026- Statement.variableDeclaration( ident, Var, value)
1027-
1028936 let transformBinding ( com : IDartCompiler ) ctx ( var : Fable.Ident ) ( value : Fable.Expr ) =
1029937 let ident = transformIdent com ctx var
1030- let valueStmnts , value = transformAndCaptureExpr com ctx value
938+ let valueStmnts , value =
939+ match value with
940+ | Function( args, body) ->
941+ let genParams = args |> List.map ( fun a -> a.Type) |> getLocalFunctionGenericParams com ctx
942+ // Pass the name of the bound ident to enable tail-call optimizations
943+ let args , body , returnType = transformFunction com ctx ( Some var.Name) args body
944+ [], Expression.anonymousFunction( args, body, returnType, genParams)
945+ | _ -> transformAndCaptureExpr com ctx value
1031946 let kind , value = getVarKind ctx var.IsMutable value
1032947 let ctx =
1033948 match kind with
1034949 | Const -> { ctx with ConstIdents = Set.add ident.Name ctx.ConstIdents }
1035950 | Var | Final -> ctx
1036- ctx, valueStmnts @ [ localVarDecl ident kind value]
951+ // If value is an anonymous function this will be converted into function declaration in printing step
952+ ctx, valueStmnts @ [ Statement.variableDeclaration( ident, kind, value)]
1037953
1038954 let transformSwitch ( com : IDartCompiler ) ctx returnStrategy evalExpr cases defaultCase =
1039955 let cases =
@@ -1216,7 +1132,7 @@ module Util =
12161132 let transformDecisionTree ( com : IDartCompiler ) ( ctx : Context ) returnStrategy
12171133 ( targets : ( Fable.Ident list * Fable.Expr ) list ) ( treeExpr : Fable.Expr ) =
12181134 let t = treeExpr.Type
1219- let returnStrategy , prevStmnt , captureExpr = convertCaptureStrategyIntoAssign com ctx t returnStrategy
1135+ let prevStmnt , returnStrategy , captureExpr = convertCaptureStrategyIntoAssign com ctx t [] returnStrategy
12201136 let resolve stmnts = prevStmnt @ stmnts, captureExpr
12211137
12221138 // If some targets are referenced multiple times, hoist bound idents,
@@ -1571,7 +1487,7 @@ module Util =
15711487 ident, InstanceVariable( ident, kind= kind))
15721488 |> List.unzip
15731489
1574- let consArgs = fields |> List.map ( fun f -> FunctionArg( f, isConsThisArg= true ))
1490+ let consArgs = fields |> List.map ( fun f -> FunctionArg( f, isConsThisArg= true ))
15751491 let constructor = Constructor( args= consArgs, isConst= not hasMutableFields)
15761492
15771493 // TODO: implement toString
@@ -1657,7 +1573,7 @@ module Util =
16571573
16581574 let transformAttachedMember ( com : IDartCompiler ) ctx ( memb : Fable.MemberDecl ) =
16591575 let isStatic = not memb.Info.IsInstance
1660- let entAndMembGenParams =
1576+ let entAndMembGenParams =
16611577 match memb.DeclaringEntity with
16621578 | Some e ->
16631579 let e = com.GetEntity( e)
0 commit comments