Browse Source

Implemented type inference for implicitly typed lambdas.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
8c8caf76f6
  1. 4
      ICSharpCode.NRefactory.Demo/CSDemo.Designer.cs
  2. 2
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs
  3. 2
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs
  4. 21
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  5. 6
      ICSharpCode.NRefactory/CSharp/Resolver/LambdaResolveResult.cs
  6. 23
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  7. 373
      ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  8. 2
      ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs

4
ICSharpCode.NRefactory.Demo/CSDemo.Designer.cs generated

@ -77,8 +77,8 @@ namespace ICSharpCode.NRefactory.Demo
this.csharpCodeTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.csharpCodeTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.csharpCodeTextBox.Size = new System.Drawing.Size(475, 178); this.csharpCodeTextBox.Size = new System.Drawing.Size(475, 178);
this.csharpCodeTextBox.TabIndex = 0; this.csharpCodeTextBox.TabIndex = 0;
this.csharpCodeTextBox.Text = "using System;\r\nclass Test\r\n{\r\n public void Main(string[] args)\r\n {\r\n " + this.csharpCodeTextBox.Text = "using System;\r\nusing System.Linq;\r\nclass Test\r\n{\r\n public void Main(string[] a" +
" Console.WriteLine(\"Hello, World\");\r\n }\r\n}"; "rgs)\r\n {\r\n Console.WriteLine(\"Hello, World\");\r\n }\r\n}";
this.csharpCodeTextBox.WordWrap = false; this.csharpCodeTextBox.WordWrap = false;
this.csharpCodeTextBox.TextChanged += new System.EventHandler(this.CsharpCodeTextBoxTextChanged); this.csharpCodeTextBox.TextChanged += new System.EventHandler(this.CsharpCodeTextBoxTextChanged);
// //

2
ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs

@ -203,7 +203,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
get { return parameters; } get { return parameters; }
} }
public override bool IsValid(IType[] parameterTypes, IType returnType, Conversions conversions) public override Conversion IsValid(IType[] parameterTypes, IType returnType, Conversions conversions)
{ {
return conversions.ImplicitConversion(inferredReturnType, returnType); return conversions.ImplicitConversion(inferredReturnType, returnType);
} }

2
ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs

@ -134,7 +134,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
get { throw new NotImplementedException(); } get { throw new NotImplementedException(); }
} }
public override bool IsValid(IType[] parameterTypes, IType returnType, Conversions conversions) public override Conversion IsValid(IType[] parameterTypes, IType returnType, Conversions conversions)
{ {
Assert.AreEqual(expectedParameterTypes, parameterTypes); Assert.AreEqual(expectedParameterTypes, parameterTypes);
return conversions.ImplicitConversion(inferredReturnType, returnType); return conversions.ImplicitConversion(inferredReturnType, returnType);

21
ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs

@ -58,12 +58,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return new Conversion(methodGroupConversionKind, chosenMethod); return new Conversion(methodGroupConversionKind, chosenMethod);
} }
public static readonly Conversion AnonymousFunctionConversion = new Conversion(anonymousFunctionConversionKind); /// <summary>
/// Creates a new anonymous function conversion.
/// </summary>
/// <param name="data">Used by ResolveVisitor to pass the LambdaTypeHypothesis.</param>
public static Conversion AnonymousFunctionConversion(object data)
{
return new Conversion(anonymousFunctionConversionKind, data);
}
readonly int kind; readonly int kind;
readonly object data; internal readonly object data;
private Conversion(int kind, object data = null) public Conversion(int kind, object data = null)
{ {
this.kind = kind; this.kind = kind;
this.data = data; this.data = data;
@ -594,11 +601,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
if (f.IsValid(dParamTypes, dReturnType, this)) { return f.IsValid(dParamTypes, dReturnType, this);
return Conversion.AnonymousFunctionConversion;
} else {
return Conversion.None;
}
} }
static IType UnpackExpressionTreeType(IType type) static IType UnpackExpressionTreeType(IType type)
@ -670,6 +673,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (!parameterTypes[i].Equals(m2.Parameters[i].Type.Resolve(context))) if (!parameterTypes[i].Equals(m2.Parameters[i].Type.Resolve(context)))
return 0; return 0;
} }
if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count)
return 0;
IType ret1 = m1.ReturnType.Resolve(context); IType ret1 = m1.ReturnType.Resolve(context);
IType ret2 = m2.ReturnType.Resolve(context); IType ret2 = m2.ReturnType.Resolve(context);

6
ICSharpCode.NRefactory/CSharp/Resolver/LambdaResolveResult.cs

@ -47,6 +47,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary> /// <summary>
/// Gets whether the lambda body is valid for the given parameter types and return type. /// Gets whether the lambda body is valid for the given parameter types and return type.
/// </summary> /// </summary>
public abstract bool IsValid(IType[] parameterTypes, IType returnType, Conversions conversions); /// <returns>
/// Produces a <see cref="Conversion.AnonymousFunctionConversion"/> if the lambda is valid;
/// otherwise returns <see cref="Conversion.None"/>.
/// </returns>
public abstract Conversion IsValid(IType[] parameterTypes, IType returnType, Conversions conversions);
} }
} }

23
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs

@ -40,6 +40,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public IList<IParameter> Parameters { get { return Member.Parameters; } } public IList<IParameter> Parameters { get { return Member.Parameters; } }
/// <summary>
/// Conversions applied to the arguments.
/// This field is set by the CheckApplicability step.
/// </summary>
public Conversion[] ArgumentConversions;
public bool IsGenericMethod { public bool IsGenericMethod {
get { get {
IMethod method = Member as IMethod; IMethod method = Member as IMethod;
@ -328,6 +334,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
candidate.ArgumentConversions = new Conversion[arguments.Length];
// Test whether argument passing mode matches the parameter passing mode // Test whether argument passing mode matches the parameter passing mode
for (int i = 0; i < arguments.Length; i++) { for (int i = 0; i < arguments.Length; i++) {
int parameterIndex = candidate.ArgumentToParameterMap[i]; int parameterIndex = candidate.ArgumentToParameterMap[i];
@ -341,7 +348,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef) if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef)
candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
} }
if (!conversions.ImplicitConversion(arguments[i], candidate.ParameterTypes[parameterIndex])) Conversion c = conversions.ImplicitConversion(arguments[i], candidate.ParameterTypes[parameterIndex]);
candidate.ArgumentConversions[i] = c;
if (!c)
candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
} }
} }
@ -555,5 +564,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return EmptyList<IType>.Instance; return EmptyList<IType>.Instance;
} }
} }
/// <summary>
/// Gets the implicit conversions that are being applied to the arguments.
/// </summary>
public IList<Conversion> ArgumentConversions {
get {
if (bestCandidate != null && bestCandidate.ArgumentConversions != null)
return bestCandidate.ArgumentConversions;
else
return new Conversion[arguments.Length];
}
}
} }
} }

373
ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs

@ -3,9 +3,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -45,6 +46,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
readonly IResolveVisitorNavigator navigator; readonly IResolveVisitorNavigator navigator;
ResolveVisitorNavigationMode mode = ResolveVisitorNavigationMode.Scan; ResolveVisitorNavigationMode mode = ResolveVisitorNavigationMode.Scan;
List<ImplicitlyTypedLambda> outstandingLambdas;
#region Constructor #region Constructor
/// <summary> /// <summary>
@ -186,6 +188,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary> /// </summary>
public ResolveResult GetResolveResult(AstNode node) public ResolveResult GetResolveResult(AstNode node)
{ {
MergeOutstandingLambdas();
ResolveResult result; ResolveResult result;
if (resolveResultCache.TryGetValue(node, out result)) if (resolveResultCache.TryGetValue(node, out result))
return result; return result;
@ -199,6 +202,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary> /// </summary>
public CSharpResolver GetResolverStateBefore(AstNode node) public CSharpResolver GetResolverStateBefore(AstNode node)
{ {
MergeOutstandingLambdas();
CSharpResolver r; CSharpResolver r;
if (resolverBeforeDict.TryGetValue(node, out r)) if (resolverBeforeDict.TryGetValue(node, out r))
return r; return r;
@ -1019,11 +1023,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (IsVariableReferenceWithSameType(target, identifierExpression.Identifier, out trr)) { if (IsVariableReferenceWithSameType(target, identifierExpression.Identifier, out trr)) {
// It's ambiguous // It's ambiguous
ResolveResult rr = ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression); ResolveResult rr = ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression);
resolveResultCache.Add(identifierExpression, IsStaticResult(rr, null) ? trr : target); resolveResultCache[identifierExpression] = IsStaticResult(rr, null) ? trr : target;
return rr; return rr;
} else { } else {
// It's not ambiguous // It's not ambiguous
resolveResultCache.Add(identifierExpression, target); resolveResultCache[identifierExpression] = target;
if (resolverEnabled) { if (resolverEnabled) {
return ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression); return ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression);
} else { } else {
@ -1068,16 +1072,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// Special handling for §7.6.4.1 Identicial simple names and type names // Special handling for §7.6.4.1 Identicial simple names and type names
ResolveResult idRR = resolver.ResolveSimpleName(identifierExpression.Identifier, EmptyList<IType>.Instance); ResolveResult idRR = resolver.ResolveSimpleName(identifierExpression.Identifier, EmptyList<IType>.Instance);
ResolveResult target = ResolveMemberReferenceOnGivenTarget(idRR, mre); ResolveResult target = ResolveMemberReferenceOnGivenTarget(idRR, mre);
resolveResultCache.Add(mre, target); resolveResultCache[mre] = target;
TypeResolveResult trr; TypeResolveResult trr;
if (IsVariableReferenceWithSameType(idRR, identifierExpression.Identifier, out trr)) { if (IsVariableReferenceWithSameType(idRR, identifierExpression.Identifier, out trr)) {
// It's ambiguous // It's ambiguous
ResolveResult rr = ResolveInvocationOnGivenTarget(target, invocationExpression); ResolveResult rr = ResolveInvocationOnGivenTarget(target, invocationExpression);
resolveResultCache.Add(identifierExpression, IsStaticResult(target, rr) ? trr : idRR); resolveResultCache[identifierExpression] = IsStaticResult(target, rr) ? trr : idRR;
return rr; return rr;
} else { } else {
// It's not ambiguous // It's not ambiguous
resolveResultCache.Add(identifierExpression, idRR); resolveResultCache[identifierExpression] = idRR;
if (resolverEnabled) { if (resolverEnabled) {
return ResolveInvocationOnGivenTarget(target, invocationExpression); return ResolveInvocationOnGivenTarget(target, invocationExpression);
} else { } else {
@ -1129,8 +1133,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
lambdaExpression.Parameters, lambdaExpression.Body, lambdaExpression.Parameters, lambdaExpression.Body,
isAnonymousMethod: false, hasParameterList: true); isAnonymousMethod: false, hasParameterList: true);
} else { } else {
// Implicitly typed lambda return new ImplicitlyTypedLambda(lambdaExpression, this);
throw new NotImplementedException();
} }
} }
@ -1178,6 +1181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
IType inferredReturnType; IType inferredReturnType;
IList<ResolveResult> returnValues; IList<ResolveResult> returnValues;
bool isValidAsVoidMethod; bool isValidAsVoidMethod;
bool success;
public ExplicitlyTypedLambda(IList<IParameter> parameters, bool isAnonymousMethod, CSharpResolver storedContext, ResolveVisitor visitor, AstNode body) public ExplicitlyTypedLambda(IList<IParameter> parameters, bool isAnonymousMethod, CSharpResolver storedContext, ResolveVisitor visitor, AstNode body)
{ {
@ -1194,74 +1198,28 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
void Analyze() bool Analyze()
{ {
if (storedContext == null) // If it's not already analyzed
return; // already analyzed if (storedContext != null) {
visitor.ResetContext( visitor.ResetContext(
storedContext, storedContext,
delegate { delegate {
Expression expr = body as Expression; visitor.AnalyzeLambda(body, out success, out isValidAsVoidMethod, out inferredReturnType, out returnValues);
if (expr != null) { });
isValidAsVoidMethod = ExpressionPermittedAsStatement(expr); storedContext = null;
returnValues = new[] { visitor.Resolve(expr) }; visitor = null;
inferredReturnType = returnValues[0].Type; body = null;
} else {
AnalyzeLambdaVisitor alv = new AnalyzeLambdaVisitor();
body.AcceptVisitor(alv, null);
isValidAsVoidMethod = (alv.ReturnExpressions.Count == 0);
if (alv.HasVoidReturnStatements) {
returnValues = EmptyList<ResolveResult>.Instance;
} else {
returnValues = new ResolveResult[alv.ReturnExpressions.Count];
for (int i = 0; i < returnValues.Count; i++) {
returnValues[i] = visitor.Resolve(alv.ReturnExpressions[i]);
}
TypeInference ti = new TypeInference(visitor.TypeResolveContext);
bool success;
inferredReturnType = ti.GetBestCommonType(returnValues, out success);
}
}
});
storedContext = null;
visitor = null;
body = null;
}
static bool ExpressionPermittedAsStatement(Expression expr)
{
UnaryOperatorExpression uoe = expr as UnaryOperatorExpression;
if (uoe != null) {
switch (uoe.Operator) {
case UnaryOperatorType.Increment:
case UnaryOperatorType.Decrement:
case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.PostDecrement:
case UnaryOperatorType.Await:
return true;
default:
return false;
}
} }
return expr is InvocationExpression return success;
|| expr is ObjectCreateExpression
|| expr is AssignmentExpression;
} }
public override bool IsValid(IType[] parameterTypes, IType returnType, Conversions conversions) public override Conversion IsValid(IType[] parameterTypes, IType returnType, Conversions conversions)
{ {
Analyze(); if (Analyze() && IsValidLambda(isValidAsVoidMethod, returnValues, returnType, conversions))
if (returnType.Kind == TypeKind.Void) { return Conversion.AnonymousFunctionConversion(null);
return isValidAsVoidMethod; else
} else { return Conversion.None;
if (returnValues.Count == 0)
return false;
foreach (ResolveResult returnRR in returnValues) {
if (!conversions.ImplicitConversion(returnRR, returnType))
return false;
}
return true;
}
} }
public override IType GetInferredReturnType(IType[] parameterTypes) public override IType GetInferredReturnType(IType[] parameterTypes)
@ -1281,10 +1239,281 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public override bool HasParameterList { public override bool HasParameterList {
get { return parameters != null; } get { return parameters != null; }
} }
public override string ToString()
{
return "[ExplicitlyTypedLambda " + body.Parent + "]";
}
}
#endregion
#region Implicitly typed
sealed class ImplicitlyTypedLambda : LambdaResolveResult
{
internal readonly LambdaExpression lambda;
readonly CSharpResolver storedContext;
readonly ParsedFile parsedFile;
readonly List<LambdaTypeHypothesis> hypotheses = new List<LambdaTypeHypothesis>();
readonly List<IParameter> parameters = new List<IParameter>();
public ImplicitlyTypedLambda(LambdaExpression lambda, ResolveVisitor visitor)
{
this.lambda = lambda;
this.storedContext = visitor.resolver.Clone();
this.parsedFile = visitor.parsedFile;
foreach (var pd in lambda.Parameters) {
parameters.Add(new DefaultParameter(SharedTypes.UnknownType, pd.Name) {
Region = visitor.MakeRegion(pd)
});
}
if (visitor.outstandingLambdas == null)
visitor.outstandingLambdas = new List<ImplicitlyTypedLambda>();
visitor.outstandingLambdas.Add(this);
}
public override IList<IParameter> Parameters {
get { return parameters; }
}
public override Conversion IsValid(IType[] parameterTypes, IType returnType, Conversions conversions)
{
return GetHypothesis(parameterTypes).IsValid(returnType, conversions);
}
public override IType GetInferredReturnType(IType[] parameterTypes)
{
return GetHypothesis(parameterTypes).inferredReturnType;
}
LambdaTypeHypothesis GetHypothesis(IType[] parameterTypes)
{
if (parameterTypes.Length != parameters.Count)
throw new ArgumentException("Incorrect parameter type count");
foreach (var h in hypotheses) {
bool ok = true;
for (int i = 0; i < parameterTypes.Length; i++) {
if (!parameterTypes[i].Equals(h.parameterTypes[i])) {
ok = false;
break;
}
}
if (ok)
return h;
}
ResolveVisitor visitor = new ResolveVisitor(storedContext.Clone(), parsedFile);
LambdaTypeHypothesis newHypothesis = new LambdaTypeHypothesis(this, parameterTypes, visitor);
hypotheses.Add(newHypothesis);
return newHypothesis;
}
/// <summary>
/// Get any hypothesis for this lambda.
/// This method is used as fallback if the lambda isn't merged the normal way (AnonymousFunctionConversion)
/// </summary>
internal LambdaTypeHypothesis GetAnyHypothesis()
{
if (hypotheses.Count == 0) {
// make a new hypothesis with unknown parameter types
IType[] parameterTypes = new IType[parameters.Count];
for (int i = 0; i < parameterTypes.Length; i++) {
parameterTypes[i] = SharedTypes.UnknownType;
}
return GetHypothesis(parameterTypes);
} else {
// We have the choice, so pick the hypothesis with the least missing parameter types
LambdaTypeHypothesis bestHypothesis = hypotheses[0];
int bestHypothesisUnknownParameters = bestHypothesis.CountUnknownParameters();
for (int i = 1; i < hypotheses.Count; i++) {
int c = hypotheses[i].CountUnknownParameters();
if (c < bestHypothesisUnknownParameters ||
(c == bestHypothesisUnknownParameters && hypotheses[i].success && !bestHypothesis.success))
{
bestHypothesis = hypotheses[i];
bestHypothesisUnknownParameters = c;
}
}
return bestHypothesis;
}
}
public override bool IsImplicitlyTyped {
get { return true; }
}
public override bool IsAnonymousMethod {
get { return false; }
}
public override bool HasParameterList {
get { return true; }
}
public override string ToString()
{
return "[ImplicitlyTypedLambda " + lambda + "]";
}
}
/// <summary>
/// Every possible set of parameter types gets its own 'hypothetical world'.
/// It uses a nested ResolveVisitor that has its own resolve cache, so that resolve results cannot leave the hypothetical world.
///
/// Only after overload resolution is applied and the actual parameter types are known, the winning hypothesis will be merged
/// with the parent ResolveVisitor.
/// This is done when the AnonymousFunctionConversion is applied on the parent visitor.
/// </summary>
sealed class LambdaTypeHypothesis
{
readonly ImplicitlyTypedLambda lambda;
internal readonly IType[] parameterTypes;
readonly ResolveVisitor visitor;
internal readonly IType inferredReturnType;
IList<ResolveResult> returnValues;
bool isValidAsVoidMethod;
internal bool success;
public LambdaTypeHypothesis(ImplicitlyTypedLambda lambda, IType[] parameterTypes, ResolveVisitor visitor)
{
Debug.Assert(parameterTypes.Length == lambda.Parameters.Count);
this.lambda = lambda;
this.parameterTypes = parameterTypes;
this.visitor = visitor;
visitor.resolver.PushBlock();
int i = 0;
foreach (var pd in lambda.lambda.Parameters) {
visitor.resolver.AddLambdaParameter(parameterTypes[i], visitor.MakeRegion(pd), pd.Name, false, false);
i++;
visitor.Scan(pd);
}
visitor.AnalyzeLambda(lambda.lambda.Body, out success, out isValidAsVoidMethod, out inferredReturnType, out returnValues);
visitor.resolver.PopBlock();
}
internal int CountUnknownParameters()
{
int c = 0;
foreach (IType t in parameterTypes) {
if (t.Kind == TypeKind.Unknown)
c++;
}
return c;
}
public Conversion IsValid(IType returnType, Conversions conversions)
{
if (success && IsValidLambda(isValidAsVoidMethod, returnValues, returnType, conversions))
return Conversion.AnonymousFunctionConversion(this);
else
return Conversion.None;
}
public void MergeInto(ResolveVisitor parentVisitor)
{
visitor.MergeOutstandingLambdas();
foreach (var pair in visitor.resolveResultCache) {
parentVisitor.resolveResultCache.Add(pair.Key, pair.Value);
}
foreach (var pair in visitor.resolverBeforeDict) {
parentVisitor.resolverBeforeDict.Add(pair.Key, pair.Value);
}
parentVisitor.outstandingLambdas.Remove(lambda);
}
}
void MergeOutstandingLambdas()
{
if (outstandingLambdas == null)
return;
while (outstandingLambdas.Count > 0) {
ImplicitlyTypedLambda implicitlyTypedLambda = outstandingLambdas[0];
LambdaExpression lambdaExpr = implicitlyTypedLambda.lambda;
AstNode parent = lambdaExpr.Parent;
while (parent is ParenthesizedExpression)
parent = parent.Parent;
CSharpResolver storedResolver;
if (resolverBeforeDict.TryGetValue(parent, out storedResolver)) {
ResetContext(storedResolver, delegate { Resolve(parent); });
}
if (outstandingLambdas.Count > 0 && outstandingLambdas[0] == implicitlyTypedLambda) {
// Lambda wasn't merged by resolving its parent -> enforce merging
implicitlyTypedLambda.GetAnyHypothesis().MergeInto(this);
}
}
} }
#endregion #endregion
#region Find Return Expressions in Lambda #region AnalyzeLambda
void AnalyzeLambda(AstNode body, out bool success, out bool isValidAsVoidMethod, out IType inferredReturnType, out IList<ResolveResult> returnValues)
{
mode = ResolveVisitorNavigationMode.ResolveAll;
Expression expr = body as Expression;
if (expr != null) {
isValidAsVoidMethod = ExpressionPermittedAsStatement(expr);
returnValues = new[] { Resolve(expr) };
inferredReturnType = returnValues[0].Type;
success = true;
} else {
Scan(body);
AnalyzeLambdaVisitor alv = new AnalyzeLambdaVisitor();
body.AcceptVisitor(alv, null);
isValidAsVoidMethod = (alv.ReturnExpressions.Count == 0);
if (alv.HasVoidReturnStatements) {
returnValues = EmptyList<ResolveResult>.Instance;
inferredReturnType = KnownTypeReference.Void.Resolve(resolver.Context);
success = true;
} else {
returnValues = new ResolveResult[alv.ReturnExpressions.Count];
for (int i = 0; i < returnValues.Count; i++) {
returnValues[i] = resolveResultCache[alv.ReturnExpressions[i]];
}
TypeInference ti = new TypeInference(resolver.Context);
inferredReturnType = ti.GetBestCommonType(returnValues, out success);
}
}
// TODO: check for compiler errors within the lambda body
// success &= ..;
}
static bool ExpressionPermittedAsStatement(Expression expr)
{
UnaryOperatorExpression uoe = expr as UnaryOperatorExpression;
if (uoe != null) {
switch (uoe.Operator) {
case UnaryOperatorType.Increment:
case UnaryOperatorType.Decrement:
case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.PostDecrement:
case UnaryOperatorType.Await:
return true;
default:
return false;
}
}
return expr is InvocationExpression
|| expr is ObjectCreateExpression
|| expr is AssignmentExpression;
}
static bool IsValidLambda(bool isValidAsVoidMethod, IList<ResolveResult> returnValues, IType returnType, Conversions conversions)
{
if (returnType.Kind == TypeKind.Void) {
return isValidAsVoidMethod;
} else {
if (returnValues.Count == 0)
return false;
foreach (ResolveResult returnRR in returnValues) {
if (!conversions.ImplicitConversion(returnRR, returnType))
return false;
}
return true;
}
}
sealed class AnalyzeLambdaVisitor : DepthFirstAstVisitor<object, object> sealed class AnalyzeLambdaVisitor : DepthFirstAstVisitor<object, object>
{ {
public bool HasVoidReturnStatements; public bool HasVoidReturnStatements;

2
ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs

@ -426,6 +426,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (m != null) { if (m != null) {
IType inferredReturnType; IType inferredReturnType;
if (lrr.IsImplicitlyTyped) { if (lrr.IsImplicitlyTyped) {
if (m.Parameters.Count != lrr.Parameters.Count)
return; // cannot infer due to mismatched parameter lists
MethodTypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); MethodTypeParameterSubstitution substitution = GetSubstitutionForFixedTPs();
IType[] inferredParameterTypes = new IType[m.Parameters.Count]; IType[] inferredParameterTypes = new IType[m.Parameters.Count];
for (int i = 0; i < inferredParameterTypes.Length; i++) { for (int i = 0; i < inferredParameterTypes.Length; i++) {

Loading…
Cancel
Save