@@ -118,7 +118,7 @@ public VisitArgumentResult(VisitResult visitResult, Optional<LocalState> stateFo
118
118
/// <summary>
119
119
/// The inferred type at the point of declaration of var locals and parameters.
120
120
/// </summary>
121
- private readonly PooledDictionary < Symbol , TypeWithAnnotations > _variableTypes = SpecializedSymbolCollections . GetPooledSymbolDictionaryInstance < Symbol , TypeWithAnnotations > ( ) ;
121
+ private PooledDictionary < Symbol , TypeWithAnnotations > _variableTypes = SpecializedSymbolCollections . GetPooledSymbolDictionaryInstance < Symbol , TypeWithAnnotations > ( ) ;
122
122
123
123
/// <summary>
124
124
/// Binder for symbol being analyzed.
@@ -192,6 +192,11 @@ public VisitArgumentResult(VisitResult visitResult, Optional<LocalState> stateFo
192
192
/// </summary>
193
193
private readonly bool _isSpeculative ;
194
194
195
+ /// <summary>
196
+ /// Is a method that contains only blocks, expression statements, and lambdas.
197
+ /// </summary>
198
+ private readonly bool _isSimpleMethod ;
199
+
195
200
/// <summary>
196
201
/// True if this walker was created using an initial state.
197
202
/// </summary>
@@ -394,6 +399,7 @@ private NullableWalker(
394
399
_returnTypesOpt = returnTypesOpt ;
395
400
_snapshotBuilderOpt = snapshotBuilderOpt ;
396
401
_isSpeculative = isSpeculative ;
402
+ _isSimpleMethod = IsSimpleMethodVisitor . IsSimpleMethod ( node ) ;
397
403
398
404
if ( initialState != null )
399
405
{
@@ -418,6 +424,68 @@ private NullableWalker(
418
424
}
419
425
}
420
426
427
+ internal sealed class IsSimpleMethodVisitor : BoundTreeWalkerWithStackGuard
428
+ {
429
+ private bool _hasComplexity ;
430
+
431
+ internal static bool IsSimpleMethod ( BoundNode ? node )
432
+ {
433
+ if ( node is BoundConstructorMethodBody constructorBody && constructorBody . Initializer is { } )
434
+ {
435
+ return false ;
436
+ }
437
+ if ( node is BoundMethodBodyBase methodBody )
438
+ {
439
+ var blockBody = methodBody . BlockBody ;
440
+ var expressionBody = methodBody . ExpressionBody ;
441
+ node = blockBody ;
442
+ if ( node is { } )
443
+ {
444
+ if ( expressionBody is { } ) return false ;
445
+ }
446
+ else
447
+ {
448
+ node = expressionBody ;
449
+ }
450
+ }
451
+ var visitor = new IsSimpleMethodVisitor ( ) ;
452
+ try
453
+ {
454
+ visitor . Visit ( node ) ;
455
+ return ! visitor . _hasComplexity ;
456
+ }
457
+ catch ( CancelledByStackGuardException )
458
+ {
459
+ return false ;
460
+ }
461
+ }
462
+
463
+ public override BoundNode ? Visit ( BoundNode ? node )
464
+ {
465
+ if ( node is null )
466
+ {
467
+ return null ;
468
+ }
469
+ if ( _hasComplexity )
470
+ {
471
+ return node ;
472
+ }
473
+ if ( node is BoundExpression )
474
+ {
475
+ return base . Visit ( node ) ;
476
+ }
477
+ switch ( node . Kind )
478
+ {
479
+ case BoundKind . Block :
480
+ case BoundKind . ExpressionStatement :
481
+ case BoundKind . ReturnStatement :
482
+ return base . Visit ( node ) ;
483
+ }
484
+ _hasComplexity = true ;
485
+ return node ;
486
+ }
487
+ }
488
+
421
489
public string GetDebuggerDisplay ( )
422
490
{
423
491
if ( this . IsConditionalState )
@@ -2535,6 +2603,37 @@ private void AnalyzeLocalFunctionOrLambda(
2535
2603
var oldState = this . State ;
2536
2604
this . State = state ;
2537
2605
2606
+ var oldVariableSlot = _variableSlot ;
2607
+ var oldVariableTypes = _variableTypes ;
2608
+ var oldVariableBySlot = variableBySlot ;
2609
+ var oldNextVariableSlot = nextVariableSlot ;
2610
+
2611
+ // As an optimization, if the entire method is simple enough,
2612
+ // we'll reset the set of variable slots and types after analyzing the nested function,
2613
+ // to avoid accumulating entries in the outer function for variables that are
2614
+ // local to the nested function. (Of course, this will drop slots associated
2615
+ // with variables in the outer function that were first used in the nested function,
2616
+ // such as a field access on a captured local, but the state associated with
2617
+ // any such entries are dropped, so the slots can be dropped as well.)
2618
+ // We don't optimize more complicated methods (methods that contain labels,
2619
+ // branches, try blocks, local functions) because we track additional state for
2620
+ // those nodes that might be invalidated if we drop the associated slots or types.
2621
+ if ( _isSimpleMethod )
2622
+ {
2623
+ _variableSlot = PooledDictionary < VariableIdentifier , int > . GetInstance ( ) ;
2624
+ foreach ( var pair in oldVariableSlot )
2625
+ {
2626
+ _variableSlot . Add ( pair . Key , pair . Value ) ;
2627
+ }
2628
+ _variableTypes = SpecializedSymbolCollections . GetPooledSymbolDictionaryInstance < Symbol , TypeWithAnnotations > ( ) ;
2629
+ foreach ( var pair in oldVariableTypes )
2630
+ {
2631
+ _variableTypes . Add ( pair . Key , pair . Value ) ;
2632
+ }
2633
+ variableBySlot = new VariableIdentifier [ oldVariableBySlot . Length ] ;
2634
+ Array . Copy ( oldVariableBySlot , variableBySlot , oldVariableBySlot . Length ) ;
2635
+ }
2636
+
2538
2637
var previousSlot = _snapshotBuilderOpt ? . EnterNewWalker ( lambdaOrFunctionSymbol ) ?? - 1 ;
2539
2638
2540
2639
var oldPending = SavePending ( ) ;
@@ -2576,6 +2675,16 @@ private void AnalyzeLocalFunctionOrLambda(
2576
2675
2577
2676
_snapshotBuilderOpt ? . ExitWalker ( this . SaveSharedState ( ) , previousSlot ) ;
2578
2677
2678
+ if ( _isSimpleMethod )
2679
+ {
2680
+ nextVariableSlot = oldNextVariableSlot ;
2681
+ variableBySlot = oldVariableBySlot ;
2682
+ _variableTypes . Free ( ) ;
2683
+ _variableTypes = oldVariableTypes ;
2684
+ _variableSlot . Free ( ) ;
2685
+ _variableSlot = oldVariableSlot ;
2686
+ }
2687
+
2579
2688
this . State = oldState ;
2580
2689
_returnTypesOpt = oldReturnTypes ;
2581
2690
_useDelegateInvokeParameterTypes = oldUseDelegateInvokeParameterTypes ;
0 commit comments