Skip to content

Test plan for static local functions #32069

@jaredpar

Description

@jaredpar

Feature

Static Local Functions

This feature allows for the following changes to local functions:

  1. Allow static modifier to be applied to local function declarations. Such a function will not be allowed to capture
    any state from the enclosing functions: locals, parameters or this. Additionally the function is guaranteed to be
    emitted as static in the binary.
  2. Allow locals and parameters in a local function, static or not, to shadow identifiers in the enclosing scope.

Code Examples

Static enforcement

int field;
void M() {
    int x = 42;

    M(x);
    static int M(int p) {
        Console.WriteLine(x);       // Error: Can't capture local
        Console.WriteLine(field);   // Error: Can't capture this
        return p;
    }
}

Shadowing

void G(DiagnosticBag diagnostics) {

    void M(DiagnosticsBag diagnostics) {
        DiagnosticsBag diagnostics;     // Error: can't shadow in same scope
    }
}

Compiler Scenarios

  • Ensure all features tied to the C# 8.0 feature flag
    • Can't shadow
    • Can't annotate a local function with static
  • static local function
    • Can't capture this, local or parameter (function or lambda)
    • Can't call instance methods
    • Can't use instance fields
    • Can access private data from enclosing scope as long as it's static
    • Parsing
      • Fails for static static
      • Fails for <return type> static
    • Definition is emitted as static in the binary static Local Functions are not being emitted as static #38143
    • Can't declare an extension method
    • Semantic API correctly implements the is static check for local functions
  • Shadowing
    • Can't shadow within a scope
    • Can shadow in non-static local functions
    • Test semantic model (LookupSymbols, GetSymbolInfo, GetOperation in particular)
    • Can shadow local, parameter, lambda parameter, local function parameter
    • Can shadow in nested scenarios
    • Can shadow with different type
    • Can shadow in
      • LINQ query from
      • LINQ query group by
      • Pattern expression
      • out variable declaration
      • Type parameter
    • Can't shadow in lambda expressions (not part of this implementation)
    • nameof on identifiers that shadow (variables and type parameter)
      • On shadowed variables
      • On variables in enclosing scope
    • Ensure self referencing initializers that shadow use final identifier
      void M() { 
          int x = 13;
          void G() { 
              int x = (x = 0) + 42;
              Console.WriteLine(x);   // Prints 42
          }
          G();
          Console.WriteLine(x);       // Prints 13
      }
    • IOperation can produce nodes for identifiers that shadow
    • CFG can represent control flow of shadowed identifiers properly
  • Confirm shadowing expectations for static and non-static local functions with LDM
  • Design principal: removing static from a local function in a valid program should not change the meaning of the program
  • Emit as static method with same signature. If the containing method is generic, the method emitted for the local function should be generic, even if the local function does not reference the type parameters. EmittedAsStatic_03
  • Can reference type parameters from enclosing scope. StaticWithTypeParameterReferences
  • Allow shadowing type parameter in containing method with local, parameter, type parameter, range variable in local function. Shadowing a type parameter with a type parameter should be a warning. ShadowNames_TypeParameter
  • Allow shadowing local, parameter, type parameter, range variable in containing method with type parameter in local function.
  • Allow using local function name for local, parameter, type parameter, range variable in same local function: void Local(object Local) { }. ShadowNames_ThisLocalFunction
  • No new diagnostics for existing scenarios
  • Non-static local function inside static local function can capture variables from static local function but not variables outside the static local function StaticWithLocalFunctionVariableReference_01, _02
  • nameof(identifier) should bind to shadowing symbol
  • typeof(T) should bind to type parameter T even if T is shadowed by a variable
  • sizeof(T) should bind to type parameter T even if T is shadowed by a variable
  • _ should bind to _ symbol in outer scope even in static local function. UnderscoreInOuterScope
  • var should bind to var symbol in outer scope even in static local function. VarInOuterScope
  • await should be contextual keyword in the same way regardless of whether local function is static. AwaitWithinAsyncOuterScope_02
  • Test overloading with mix of static and non-static
  • Test IOperation and CFG to ensure IDE does not crash
  • EE: should be easier to allow invoking static local functions than non-static local functions
  • Intellisense should only include one identifier for shadowed symbol
  • Intellisense should only include shadowing local function, not shadowed
  • Invoking a static local function in an expression tree should be an error. ExpressionTreeLocalFunctionUsage_02
  • Emit call rather than callvirt for local functions regardless of whether local function is static. EmitCallInstruction
  • ISymbol.IsStatic should return false for non-static local function (not blocking)

IDE Scenarios

Metadata

Metadata

Assignees

Labels

Area-CompilersTestTest failures in roslyn-CITest-GapDescribes a specific feature or scenario that does not have test coverage

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions