Skip to content

Commit

Permalink
EnC support for lambdas & closures in C# compiler
Browse files Browse the repository at this point in the history
1) Change MethdCompiler.BindMethodBody to associate correct syntax with BoundBlocks it creates when binding constructor with constructor initializer call (two bound blocks are created – outer one defines a closure scope for constructor parameters, the inner one defines a closure scope for variables in the body).
2) Introduce MethodDebugId – a method ordinal and generation ordinal pair
3) Introduce LamdbaDebugInfo and ClosureDebugInfo to represent information (syntax offset) we use to figure out how to map lambdas and closures to the previous generation.
4) Adds a new PDB CDI record (#7) to store lambda and closure debug info.
5) Generalizes CalculateLocalSyntaxOffset to handle positions in field/property initializers and constructor initializers. Use it to calculate syntax offsets of lambdas and closure scopes. (TODO: rename CalculateLocalSyntaxOffset to CalculateSyntaxOffset).
6) Replace lambda and scope ordinal dispenser integers with array builders that collect LambdaDebugInfo and ClosureDebugInfo.
7) Use TryGet- pattern for all VariableSlotAllocator APIs.
8) Implements mapping of lambda method and display class names to previous generation via VariableSlotAllocator.
 (changeset 1407240)
  • Loading branch information
tmat committed Jan 31, 2015
1 parent e14d07c commit ebc795d
Show file tree
Hide file tree
Showing 89 changed files with 3,533 additions and 1,298 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,20 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class Binder
{
delegate BoundBlock LambdaBodyResolver(LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag diagnostics);
private delegate BoundBlock LambdaBodyFactory(LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag diagnostics);

private class QueryUnboundLambdaState : UnboundLambdaState
{
private readonly ImmutableArray<RangeVariableSymbol> parameters;
private readonly LambdaBodyResolver bodyResolver;
private readonly LambdaBodyFactory bodyFactory;
private readonly RangeVariableMap rangeVariableMap;

public QueryUnboundLambdaState(UnboundLambda unbound, Binder binder, RangeVariableMap rangeVariableMap, ImmutableArray<RangeVariableSymbol> parameters, LambdaBodyResolver bodyResolver)
: base(unbound, binder)
public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap, ImmutableArray<RangeVariableSymbol> parameters, LambdaBodyFactory bodyFactory)
: base(binder, unboundLambdaOpt: null)
{
this.parameters = parameters;
this.bodyResolver = bodyResolver;
this.rangeVariableMap = rangeVariableMap;
}

public QueryUnboundLambdaState(UnboundLambda unbound, Binder binder, RangeVariableMap rangeVariableMap, ImmutableArray<RangeVariableSymbol> parameters, ExpressionSyntax body, TypeSyntax castTypeSyntax, TypeSymbol castType)
: this(unbound, binder, rangeVariableMap, parameters, (LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag diagnostics) =>
{
BoundExpression expression = lambdaBodyBinder.BindValue(body, diagnostics, BindValueKind.RValue);
Debug.Assert((object)castType != null);
Debug.Assert(castTypeSyntax != null);
// We transform the expression from "expr" to "expr.Cast<castTypeOpt>()".
expression = lambdaBodyBinder.MakeQueryInvocation(body, expression, "Cast", castTypeSyntax, castType, diagnostics);
return lambdaBodyBinder.CreateBlockFromExpression(body, lambdaBodyBinder.Locals, body, expression, diagnostics);
})
{ }

public QueryUnboundLambdaState(UnboundLambda unbound, Binder binder, RangeVariableMap rangeVariableMap, ImmutableArray<RangeVariableSymbol> parameters, ExpressionSyntax body)
: this(unbound, binder, rangeVariableMap, parameters, (LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag diagnostics) =>
{
return lambdaBodyBinder.BindLambdaExpressionAsBlock(body, diagnostics);
})
{ }

internal void SetUnboundLambda(UnboundLambda unbound)
{
Debug.Assert(base.unboundLambda == null);
base.unboundLambda = unbound;
this.bodyFactory = bodyFactory;
}

public override string ParameterName(int index) { return parameters[index].Name; }
Expand All @@ -74,7 +49,7 @@ public override Binder ParameterBinder(LambdaSymbol lambdaSymbol, Binder binder)

protected override BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag diagnostics)
{
return bodyResolver(lambdaSymbol, ref lambdaBodyBinder, diagnostics);
return bodyFactory(lambdaSymbol, ref lambdaBodyBinder, diagnostics);
}
}
}
Expand Down
223 changes: 134 additions & 89 deletions src/Compilers/CSharp/Portable/Binder/Binder_Query.cs

Large diffs are not rendered by default.

8 changes: 0 additions & 8 deletions src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,14 +465,6 @@ public BoundBadExpression(CSharpSyntaxNode syntax, LookupResultKind resultKind,
}
}

internal sealed partial class BoundLambda
{
public BoundLambda(CSharpSyntaxNode syntax, BoundBlock body, ImmutableArray<Diagnostic> diagnostics, Binder binder, TypeSymbol type)
: this(syntax, (LambdaSymbol)binder.ContainingMemberOrLambda, body, diagnostics, binder, type)
{
}
}

internal partial class BoundStatementList
{
public static BoundStatementList Synthesized(CSharpSyntaxNode syntax, params BoundStatement[] statements)
Expand Down
45 changes: 33 additions & 12 deletions src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,23 @@ public bool InferredFromSingleType
}
}

public BoundLambda(CSharpSyntaxNode syntax, BoundBlock body, ImmutableArray<Diagnostic> diagnostics, Binder binder, TypeSymbol type, bool inferReturnTypeMarker)
public BoundLambda(CSharpSyntaxNode syntax, BoundBlock body, ImmutableArray<Diagnostic> diagnostics, Binder binder, TypeSymbol type, bool inferReturnType)
: this(syntax, (LambdaSymbol)binder.ContainingMemberOrLambda, body, diagnostics, binder, type)
{
this.inferredReturnType = InferReturnType(this.Body, this.Binder, this.Symbol.IsAsync, ref this.inferredReturnTypeUseSiteDiagnostics, out this.inferredFromSingleType);
if (inferReturnType)
{
this.inferredReturnType = InferReturnType(this.Body, this.Binder, this.Symbol.IsAsync, ref this.inferredReturnTypeUseSiteDiagnostics, out this.inferredFromSingleType);

#if DEBUG
this.hasInferredReturnType = true;
this.hasInferredReturnType = true;
#endif
}

Debug.Assert(
syntax.IsAnonymousFunction() || // lambda expressions
syntax is ExpressionSyntax && SyntaxFacts.IsLambdaBody(syntax) || // query lambdas
SyntaxFacts.IsQueryPairLambda(syntax) // "pair" lambdas in queries
);
}

public TypeSymbol InferredReturnType(ref HashSet<DiagnosticInfo> useSiteDiagnostics)
Expand Down Expand Up @@ -200,7 +209,7 @@ public UnboundLambda(

internal abstract class UnboundLambdaState
{
protected UnboundLambda unboundLambda; // we would prefer this readonly, but we have an initialization cycle.
private UnboundLambda unboundLambda; // we would prefer this readonly, but we have an initialization cycle.
protected readonly Binder binder;
private readonly ConcurrentDictionary<object, BoundLambda> bindingCache = new ConcurrentDictionary<object, BoundLambda>();

Expand All @@ -209,12 +218,24 @@ internal abstract class UnboundLambdaState

private BoundLambda errorBinding;

public UnboundLambdaState(UnboundLambda unboundLambda, Binder binder)
public UnboundLambdaState(Binder binder, UnboundLambda unboundLambdaOpt)
{
this.unboundLambda = unboundLambda;
Debug.Assert(binder != null);

// might be initialized later (for query lambdas)
this.unboundLambda = unboundLambdaOpt;
this.binder = binder;
}

public void SetUnboundLambda(UnboundLambda unbound)
{
Debug.Assert(unbound != null);
Debug.Assert(unboundLambda == null);
unboundLambda = unbound;
}

public UnboundLambda UnboundLambda => unboundLambda;

public abstract MessageID MessageID { get; }
public abstract string ParameterName(int index);
public abstract bool HasSignature { get; }
Expand Down Expand Up @@ -368,7 +389,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol, diagnostics, lambdaSymbol.Locations[0]);
}

var result = new BoundLambda(this.unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType)
var result = new BoundLambda(this.unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: false)
{ WasCompilerGenerated = this.unboundLambda.WasCompilerGenerated };

return result;
Expand Down Expand Up @@ -405,7 +426,7 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType)
Binder lambdaBodyBinder = new ExecutableCodeBinder(this.unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder));
var block = BindLambdaBody(lambdaSymbol, ref lambdaBodyBinder, diagnostics);

var result = new BoundLambda(this.unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnTypeMarker: true)
var result = new BoundLambda(this.unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: true)
{ WasCompilerGenerated = this.unboundLambda.WasCompilerGenerated };

HashSet<DiagnosticInfo> useSiteDiagnostics = null; // TODO: figure out if this should be somehow merged into BoundLambda.Diagnostics.
Expand Down Expand Up @@ -672,7 +693,7 @@ internal PlainUnboundLambdaState(
ImmutableArray<TypeSymbol> parameterTypes,
ImmutableArray<RefKind> parameterRefKinds,
bool isAsync)
: base(unboundLambda, binder)
: base(binder, unboundLambda)
{
this.parameterNames = parameterNames;
this.parameterTypes = parameterTypes;
Expand All @@ -688,13 +709,13 @@ internal PlainUnboundLambdaState(

public override bool IsAsync { get { return this.isAsync; } }

public override MessageID MessageID { get { return this.unboundLambda.Syntax.Kind() == SyntaxKind.AnonymousMethodExpression ? MessageID.IDS_AnonMethod : MessageID.IDS_Lambda; } }
public override MessageID MessageID { get { return this.UnboundLambda.Syntax.Kind() == SyntaxKind.AnonymousMethodExpression ? MessageID.IDS_AnonMethod : MessageID.IDS_Lambda; } }

private CSharpSyntaxNode Body
{
get
{
var Syntax = unboundLambda.Syntax;
var Syntax = UnboundLambda.Syntax;
switch (Syntax.Kind())
{
default:
Expand All @@ -711,7 +732,7 @@ private CSharpSyntaxNode Body
public override Location ParameterLocation(int index)
{
Debug.Assert(HasSignature && 0 <= index && index < ParameterCount);
var Syntax = unboundLambda.Syntax;
var Syntax = UnboundLambda.Syntax;
switch (Syntax.Kind())
{
default:
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@
<Compile Include="Syntax\SyntaxEquivalence.cs" />
<Compile Include="Syntax\SyntaxExtensions.cs" />
<Compile Include="Syntax\SyntaxFactory.cs" />
<Compile Include="Syntax\SyntaxUtilities.cs" />
<Compile Include="Syntax\SyntaxFormatter.cs" />
<Compile Include="Syntax\SyntaxKind.cs" />
<Compile Include="Syntax\SyntaxKindEqualityComparer.cs" />
Expand All @@ -808,7 +809,7 @@
<Compile Include="Syntax\SyntaxListBuilderExtensions.cs" />
<Compile Include="Syntax\SyntaxNavigator.cs" />
<Compile Include="Syntax\SyntaxNodeExtensions.cs" />
<Compile Include="Syntax\SyntaxNodeFacts.cs" />
<Compile Include="Syntax\SyntaxFacts.cs" />
<Compile Include="Syntax\SyntaxNodeOrTokenListBuilder.cs" />
<Compile Include="Syntax\SyntaxNodeRemover.cs" />
<Compile Include="Syntax\SyntaxReplacer.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,10 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEve

}

internal static BoundBlock ConstructDestructorBody(CSharpSyntaxNode syntax, MethodSymbol method, BoundBlock block)
internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlock block)
{
var syntax = block.Syntax;

Debug.Assert(method.MethodKind == MethodKind.Destructor);
Debug.Assert(syntax.Kind() == SyntaxKind.Block);

Expand Down
Loading

0 comments on commit ebc795d

Please sign in to comment.