@@ -36,15 +36,32 @@ type SharedFlow struct {
36
36
}
37
37
38
38
type FlowState struct {
39
- reference * ast.Node
40
- declaredType * Type
41
- initialType * Type
42
- flowContainer * ast.Node
43
- refKey string
44
- depth int
45
- sharedFlowStart int
46
- reduceLabels []* ast.FlowReduceLabelData
47
- reduceLabelsBuffer [4 ]* ast.FlowReduceLabelData
39
+ reference * ast.Node
40
+ declaredType * Type
41
+ initialType * Type
42
+ flowContainer * ast.Node
43
+ refKey string
44
+ depth int
45
+ sharedFlowStart int
46
+ reduceLabels []* ast.FlowReduceLabelData
47
+ next * FlowState
48
+ }
49
+
50
+ func (c * Checker ) getFlowState () * FlowState {
51
+ f := c .freeFlowState
52
+ if f == nil {
53
+ f = & FlowState {}
54
+ }
55
+ c .freeFlowState = f .next
56
+ return f
57
+ }
58
+
59
+ func (c * Checker ) putFlowState (f * FlowState ) {
60
+ * f = FlowState {
61
+ reduceLabels : f .reduceLabels [:0 ],
62
+ next : c .freeFlowState ,
63
+ }
64
+ c .freeFlowState = f
48
65
}
49
66
50
67
func getFlowNodeOfNode (node * ast.Node ) * ast.FlowNode {
@@ -69,20 +86,16 @@ func (c *Checker) getFlowTypeOfReferenceEx(reference *ast.Node, declaredType *Ty
69
86
return declaredType
70
87
}
71
88
}
72
- flowStateCount := len (c .flowStates )
73
- c .flowStates = slices .Grow (c .flowStates , 1 )[:flowStateCount + 1 ]
74
- f := & c .flowStates [flowStateCount ]
89
+ f := c .getFlowState ()
75
90
f .reference = reference
76
91
f .declaredType = declaredType
77
92
f .initialType = core .Coalesce (initialType , declaredType )
78
93
f .flowContainer = flowContainer
79
94
f .sharedFlowStart = len (c .sharedFlows )
80
- f .reduceLabels = f .reduceLabelsBuffer [:0 ]
81
95
c .flowInvocationCount ++
82
96
evolvedType := c .getTypeAtFlowNode (f , flowNode ).t
83
97
c .sharedFlows = c .sharedFlows [:f .sharedFlowStart ]
84
- c .flowStates [flowStateCount ] = FlowState {}
85
- c .flowStates = c .flowStates [:flowStateCount ]
98
+ c .putFlowState (f )
86
99
// When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation,
87
100
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
88
101
// on empty arrays are possible without implicit any errors and new element types can be inferred without
@@ -2442,15 +2455,15 @@ func (c *Checker) getFlowTypeInStaticBlocks(symbol *ast.Symbol, staticBlocks []*
2442
2455
}
2443
2456
2444
2457
func (c * Checker ) isReachableFlowNode (flow * ast.FlowNode ) bool {
2445
- result := c .isReachableFlowNodeWorker (flow , false /*noCacheCheck*/ )
2458
+ f := c .getFlowState ()
2459
+ result := c .isReachableFlowNodeWorker (f , flow , false /*noCacheCheck*/ )
2460
+ c .putFlowState (f )
2446
2461
c .lastFlowNode = flow
2447
2462
c .lastFlowNodeReachable = result
2448
2463
return result
2449
2464
}
2450
2465
2451
- func (c * Checker ) isReachableFlowNodeWorker (flow * ast.FlowNode , noCacheCheck bool ) bool {
2452
- var reduceLabelsBuffer [4 ]* ast.FlowReduceLabelData
2453
- reduceLabels := reduceLabelsBuffer [:0 ]
2466
+ func (c * Checker ) isReachableFlowNodeWorker (f * FlowState , flow * ast.FlowNode , noCacheCheck bool ) bool {
2454
2467
for {
2455
2468
if flow == c .lastFlowNode {
2456
2469
return c .lastFlowNodeReachable
@@ -2461,7 +2474,7 @@ func (c *Checker) isReachableFlowNodeWorker(flow *ast.FlowNode, noCacheCheck boo
2461
2474
if reachable , ok := c .flowNodeReachable [flow ]; ok {
2462
2475
return reachable
2463
2476
}
2464
- reachable := c .isReachableFlowNodeWorker (flow , true /*noCacheCheck*/ )
2477
+ reachable := c .isReachableFlowNodeWorker (f , flow , true /*noCacheCheck*/ )
2465
2478
c .flowNodeReachable [flow ] = reachable
2466
2479
return reachable
2467
2480
}
@@ -2487,8 +2500,8 @@ func (c *Checker) isReachableFlowNodeWorker(flow *ast.FlowNode, noCacheCheck boo
2487
2500
flow = flow .Antecedent
2488
2501
case flags & ast .FlowFlagsBranchLabel != 0 :
2489
2502
// A branching point is reachable if any branch is reachable.
2490
- for list := getBranchLabelAntecedents (flow , reduceLabels ); list != nil ; list = list .Next {
2491
- if c .isReachableFlowNodeWorker (list .Flow , false /*noCacheCheck*/ ) {
2503
+ for list := getBranchLabelAntecedents (flow , f . reduceLabels ); list != nil ; list = list .Next {
2504
+ if c .isReachableFlowNodeWorker (f , list .Flow , false /*noCacheCheck*/ ) {
2492
2505
return true
2493
2506
}
2494
2507
}
@@ -2510,9 +2523,9 @@ func (c *Checker) isReachableFlowNodeWorker(flow *ast.FlowNode, noCacheCheck boo
2510
2523
case flags & ast .FlowFlagsReduceLabel != 0 :
2511
2524
// Cache is unreliable once we start adjusting labels
2512
2525
c .lastFlowNode = nil
2513
- reduceLabels = append (reduceLabels , flow .Node .AsFlowReduceLabelData ())
2514
- result := c .isReachableFlowNodeWorker (flow .Antecedent , false /*noCacheCheck*/ )
2515
- reduceLabels = reduceLabels [:len (reduceLabels )- 1 ]
2526
+ f . reduceLabels = append (f . reduceLabels , flow .Node .AsFlowReduceLabelData ())
2527
+ result := c .isReachableFlowNodeWorker (f , flow .Antecedent , false /*noCacheCheck*/ )
2528
+ f . reduceLabels = f . reduceLabels [:len (f . reduceLabels )- 1 ]
2516
2529
return result
2517
2530
default :
2518
2531
return flags & ast .FlowFlagsUnreachable == 0
@@ -2536,16 +2549,21 @@ func (c *Checker) isFalseExpression(expr *ast.Node) bool {
2536
2549
// Return true if the given flow node is preceded by a 'super(...)' call in every possible code path
2537
2550
// leading to the node.
2538
2551
func (c * Checker ) isPostSuperFlowNode (flow * ast.FlowNode , noCacheCheck bool ) bool {
2539
- var reduceLabelsBuffer [4 ]* ast.FlowReduceLabelData
2540
- reduceLabels := reduceLabelsBuffer [:0 ]
2552
+ f := c .getFlowState ()
2553
+ result := c .isPostSuperFlowNodeWorker (f , flow , noCacheCheck )
2554
+ c .putFlowState (f )
2555
+ return result
2556
+ }
2557
+
2558
+ func (c * Checker ) isPostSuperFlowNodeWorker (f * FlowState , flow * ast.FlowNode , noCacheCheck bool ) bool {
2541
2559
for {
2542
2560
flags := flow .Flags
2543
2561
if flags & ast .FlowFlagsShared != 0 {
2544
2562
if ! noCacheCheck {
2545
2563
if postSuper , ok := c .flowNodePostSuper [flow ]; ok {
2546
2564
return postSuper
2547
2565
}
2548
- postSuper := c .isPostSuperFlowNode ( flow , true /*noCacheCheck*/ )
2566
+ postSuper := c .isPostSuperFlowNodeWorker ( f , flow , true /*noCacheCheck*/ )
2549
2567
c .flowNodePostSuper [flow ] = postSuper
2550
2568
}
2551
2569
noCacheCheck = false
@@ -2559,8 +2577,8 @@ func (c *Checker) isPostSuperFlowNode(flow *ast.FlowNode, noCacheCheck bool) boo
2559
2577
}
2560
2578
flow = flow .Antecedent
2561
2579
case flags & ast .FlowFlagsBranchLabel != 0 :
2562
- for list := getBranchLabelAntecedents (flow , reduceLabels ); list != nil ; list = list .Next {
2563
- if ! c .isPostSuperFlowNode ( list .Flow , false /*noCacheCheck*/ ) {
2580
+ for list := getBranchLabelAntecedents (flow , f . reduceLabels ); list != nil ; list = list .Next {
2581
+ if ! c .isPostSuperFlowNodeWorker ( f , list .Flow , false /*noCacheCheck*/ ) {
2564
2582
return false
2565
2583
}
2566
2584
}
@@ -2569,9 +2587,9 @@ func (c *Checker) isPostSuperFlowNode(flow *ast.FlowNode, noCacheCheck bool) boo
2569
2587
// A loop is post-super if the control flow path that leads to the top is post-super.
2570
2588
flow = flow .Antecedents .Flow
2571
2589
case flags & ast .FlowFlagsReduceLabel != 0 :
2572
- reduceLabels = append (reduceLabels , flow .Node .AsFlowReduceLabelData ())
2573
- result := c .isPostSuperFlowNode ( flow .Antecedent , false /*noCacheCheck*/ )
2574
- reduceLabels = reduceLabels [:len (reduceLabels )- 1 ]
2590
+ f . reduceLabels = append (f . reduceLabels , flow .Node .AsFlowReduceLabelData ())
2591
+ result := c .isPostSuperFlowNodeWorker ( f , flow .Antecedent , false /*noCacheCheck*/ )
2592
+ f . reduceLabels = f . reduceLabels [:len (f . reduceLabels )- 1 ]
2575
2593
return result
2576
2594
default :
2577
2595
// Unreachable nodes are considered post-super to silence errors
0 commit comments