Delegate UnboundLambda to BoundLambda conversion to the semantic model.

上级 dfd7a70e
......@@ -51,7 +51,7 @@ protected MemberSemanticModel(CSharpCompilation compilation, CSharpSyntaxNode ro
_parentSemanticModelOpt = parentSemanticModelOpt;
_speculatedPosition = speculatedPosition;
_operationFactory = new CSharpOperationFactory();
_operationFactory = new CSharpOperationFactory(this);
}
public override CSharpCompilation Compilation
......@@ -983,6 +983,10 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, GetOperat
break;
}
// The CSharp operation factory assumes that UnboundLambda will be bound for error recovery and never be passed to the factory
// as the start of a tree to get operations for. This is guaranteed by the builder that populates the node map, as it will call
// UnboundLambda.BindForErrorRecovery() when it encounters an UnboundLambda node.
Debug.Assert(result.Kind != BoundKind.UnboundLambda);
return _operationFactory.Create(result);
}
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
......@@ -13,6 +14,12 @@ internal sealed partial class CSharpOperationFactory
{
private readonly ConcurrentDictionary<BoundNode, IOperation> _cache =
new ConcurrentDictionary<BoundNode, IOperation>(concurrencyLevel: 2, capacity: 10);
private readonly SemanticModel _semanticModel;
public CSharpOperationFactory(SemanticModel semanticModel)
{
_semanticModel = semanticModel;
}
public IOperation Create(BoundNode boundNode)
{
......@@ -353,18 +360,15 @@ private IObjectCreationExpression CreateBoundObjectCreationExpressionOperation(B
return new LazyObjectCreationExpression(constructor, memberInitializers, argumentsInEvaluationOrder, isInvalid, syntax, type, constantValue);
}
private ILambdaExpression CreateUnboundLambdaOperation(UnboundLambda unboundLambda)
private IOperation CreateUnboundLambdaOperation(UnboundLambda unboundLambda)
{
bool isInvalid = unboundLambda.HasErrors;
SyntaxNode syntax = unboundLambda.Syntax;
// This matches the SemanticModel implementation. This is because in VB, lambdas by themselves
// do not have a type. To get the type of a lambda expression in the SemanticModel, you need to look at
// TypeInfo.ConvertedType, rather than TypeInfo.Type. We replicate that behavior here. To get the type of
// an IUnboundLambdaExpression, you need to look at the parent IConversionExpression.
ITypeSymbol type = null;
Optional<object> constantValue = ConvertToOptional(unboundLambda.ConstantValue);
Lazy<ILambdaExpression> internalLambda = new Lazy<ILambdaExpression>(() => CreateBoundLambdaOperation(unboundLambda.BindForErrorRecovery()));
return new LazyLambdaFromUnboundLambdaExpression(internalLambda, isInvalid, syntax, type, constantValue);
// We want to ensure that we never see the UnboundLambda node, and that we don't end up having two different IOperation
// nodes for the lambda expression. So, we ask the semantic model for the IOperation node for the unbound lambda syntax.
// We are counting on the fact that will do the error recovery and actually create the BoundLambda node appropriate for
// this syntax node.
var lambdaOperation = _semanticModel.GetOperationInternal(unboundLambda.Syntax);
Debug.Assert(lambdaOperation.Kind == OperationKind.LambdaExpression);
return lambdaOperation;
}
private ILambdaExpression CreateBoundLambdaOperation(BoundLambda boundLambda)
......
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Semantics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
......@@ -112,5 +114,49 @@ static void F()
VerifyOperationTreeAndDiagnosticsForTest<EqualsValueClauseSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
[Fact]
public void ILambdaExpression_UnboundLambda_ReferenceEquality()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
var x /*<bind>*/= () => F()/*</bind>*/;
}
static void F()
{
}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var syntaxTree = compilation.SyntaxTrees[0];
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var variableDeclaration = syntaxTree.GetRoot().DescendantNodes().OfType<VariableDeclarationSyntax>().Single();
var lambdaSyntax = (LambdaExpressionSyntax)variableDeclaration.Variables.Single().Initializer.Value;
var variableDeclarationOperation = (IVariableDeclarationStatement)semanticModel.GetOperationInternal(variableDeclaration);
var variableTreeLambdaOperation = (ILambdaExpression)variableDeclarationOperation.Declarations.Single().Initializer;
var lambdaOperation = (ILambdaExpression)semanticModel.GetOperationInternal(lambdaSyntax);
// Assert that both ways of getting to the lambda (requesting the lambda directly, and requesting via the lambda syntax)
// return the same bound node.
Assert.Same(variableTreeLambdaOperation, lambdaOperation);
var variableDeclarationOperationSecondRequest = (IVariableDeclarationStatement)semanticModel.GetOperationInternal(variableDeclaration);
var variableTreeLambdaOperationSecondRequest = (ILambdaExpression)variableDeclarationOperation.Declarations.Single().Initializer;
var lambdaOperationSecondRequest = (ILambdaExpression)semanticModel.GetOperationInternal(lambdaSyntax);
// Assert that, when request the variable declaration or the lambda for a second time, there is no rebinding of the
// underlying UnboundLambda, and we get the same ILambdaExpression as before
Assert.Same(variableTreeLambdaOperation, variableTreeLambdaOperationSecondRequest);
Assert.Same(lambdaOperation, lambdaOperationSecondRequest);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册