Browse Source

Implement type argument inference for generic method calls.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@449 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 20 years ago
parent
commit
395257996d
  1. 4
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs
  2. 3
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs
  3. 4
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs
  4. 3
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs
  5. 9
      src/Libraries/NRefactory/Project/Src/Parser/AST/General/TypeLevel/ParameterDeclarationExpression.cs
  6. 2
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  7. 26
      src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs
  8. 152
      src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs
  9. 2
      src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs
  10. 2
      src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs
  11. 6
      src/Main/Base/Project/Src/Services/ParserService/ParserService.cs
  12. 8
      src/Main/Base/Test/GenericResolverTests.cs

4
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs

@ -499,8 +499,8 @@ namespace ICSharpCode.Core @@ -499,8 +499,8 @@ namespace ICSharpCode.Core
}
builder.Append(']');
UnpackNestedType(builder, art.ElementType);
} else if (returnType is SpecificReturnType) {
SpecificReturnType rt = (SpecificReturnType)returnType;
} else if (returnType is ConstructedReturnType) {
ConstructedReturnType rt = (ConstructedReturnType)returnType;
UnpackNestedType(builder, rt.BaseType);
builder.Append('<');
for (int i = 0; i < rt.TypeParameters.Count; ++i) {

3
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs

@ -137,7 +137,8 @@ namespace CSharpBinding @@ -137,7 +137,8 @@ namespace CSharpBinding
parameterTypes[i] = rr.ResolvedType;
}
}
int[] ranking = MemberLookupHelper.RankOverloads(methods, parameterTypes, true, out overloadIsSure);
IReturnType[][] tmp;
int[] ranking = MemberLookupHelper.RankOverloads(methods, parameterTypes, true, out overloadIsSure, out tmp);
bool multipleBest = false;
int bestRanking = -1;
int best = 0;

4
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs

@ -501,8 +501,8 @@ namespace VBNetBinding @@ -501,8 +501,8 @@ namespace VBNetBinding
}
builder.Append(')');
UnpackNestedType(builder, art.ElementType);
} else if (returnType is SpecificReturnType) {
SpecificReturnType rt = (SpecificReturnType)returnType;
} else if (returnType is ConstructedReturnType) {
ConstructedReturnType rt = (ConstructedReturnType)returnType;
UnpackNestedType(builder, rt.BaseType);
builder.Append("(Of ");
for (int i = 0; i < rt.TypeParameters.Count; ++i) {

3
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs

@ -142,7 +142,8 @@ namespace VBNetBinding @@ -142,7 +142,8 @@ namespace VBNetBinding
parameterTypes[i] = rr.ResolvedType;
}
}
int[] ranking = MemberLookupHelper.RankOverloads(methods, parameterTypes, true, out overloadIsSure);
IReturnType[][] tmp;
int[] ranking = MemberLookupHelper.RankOverloads(methods, parameterTypes, true, out overloadIsSure, out tmp);
bool multipleBest = false;
int bestRanking = -1;
int best = 0;

9
src/Libraries/NRefactory/Project/Src/Parser/AST/General/TypeLevel/ParameterDeclarationExpression.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// <file>
// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="none" email=""/>
@ -33,8 +33,7 @@ namespace ICSharpCode.NRefactory.Parser.AST @@ -33,8 +33,7 @@ namespace ICSharpCode.NRefactory.Parser.AST
return typeReference;
}
set {
Debug.Assert(value != null);
typeReference = value;
typeReference = value ?? TypeReference.Null;
}
}
public string ParameterName {
@ -68,10 +67,10 @@ namespace ICSharpCode.NRefactory.Parser.AST @@ -68,10 +67,10 @@ namespace ICSharpCode.NRefactory.Parser.AST
public ParameterDeclarationExpression(TypeReference typeReference, string parameterName)
{
this.typeReference = typeReference;
this.TypeReference = typeReference;
if (parameterName == null || parameterName.Length == 0)
parameterName = "?";
this.parameterName = parameterName;
this.parameterName = parameterName;
this.paramModifier = ParamModifier.In;
}

2
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -641,7 +641,7 @@ @@ -641,7 +641,7 @@
<Compile Include="Src\Dom\Implementations\GetClassReturnType.cs" />
<Compile Include="Src\Dom\Implementations\GenericReturnType.cs" />
<Compile Include="Src\Dom\Implementations\ArrayReturnType.cs" />
<Compile Include="Src\Dom\Implementations\SpecificReturnType.cs" />
<Compile Include="Src\Dom\Implementations\ConstructedReturnType.cs" />
<Compile Include="Src\Dom\LanguageProperties.cs" />
<Compile Include="Src\Gui\BrowserDisplayBinding\SchemeExtension.cs" />
<Compile Include="Src\Gui\BrowserDisplayBinding\ExtendedWebBrowser.cs">

26
src/Main/Base/Project/Src/Dom/Implementations/SpecificReturnType.cs → src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs

@ -13,13 +13,13 @@ using ICSharpCode.Core; @@ -13,13 +13,13 @@ using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// SpecificReturnType is a reference to generic class that specifies the type parameters.
/// ConstructedReturnType is a reference to generic class that specifies the type parameters.
/// When getting the Members, this return type modifies the lists in such a way that the
/// <see cref="GenericReturnType"/>s are replaced with the return types in the type parameters
/// collection.
/// Example: List&lt;string&gt;
/// </summary>
public sealed class SpecificReturnType : ProxyReturnType
public sealed class ConstructedReturnType : ProxyReturnType
{
// Return types that should be substituted for the generic types
// If a substitution is unknown (type could not be resolved), the list
@ -33,7 +33,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -33,7 +33,7 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
public SpecificReturnType(IReturnType baseType, List<IReturnType> typeParameters)
public ConstructedReturnType(IReturnType baseType, List<IReturnType> typeParameters)
{
if (baseType == null)
throw new ArgumentNullException("baseType");
@ -45,7 +45,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -45,7 +45,7 @@ namespace ICSharpCode.SharpDevelop.Dom
public override bool Equals(object o)
{
SpecificReturnType rt = o as SpecificReturnType;
ConstructedReturnType rt = o as ConstructedReturnType;
if (rt == null) return false;
if (!baseType.Equals(rt.baseType)) return false;
if (typeParameters.Count != rt.typeParameters.Count) return false;
@ -77,8 +77,8 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -77,8 +77,8 @@ namespace ICSharpCode.SharpDevelop.Dom
return rt.TypeParameter.Method == null;
} else if (t is ArrayReturnType) {
return CheckReturnType(((ArrayReturnType)t).ElementType);
} else if (t is SpecificReturnType) {
foreach (IReturnType para in ((SpecificReturnType)t).TypeParameters) {
} else if (t is ConstructedReturnType) {
foreach (IReturnType para in ((ConstructedReturnType)t).TypeParameters) {
if (CheckReturnType(para)) return true;
}
return false;
@ -116,11 +116,17 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -116,11 +116,17 @@ namespace ICSharpCode.SharpDevelop.Dom
public static IReturnType TranslateType(IReturnType input, IList<IReturnType> typeParameters, bool convertForMethod)
{
if (typeParameters == null || typeParameters.Count == 0) {
return input; // nothing to do when there are no type parameters specified
}
if (input is GenericReturnType) {
GenericReturnType rt = (GenericReturnType)input;
if (convertForMethod ? (rt.TypeParameter.Method != null) : (rt.TypeParameter.Method == null)) {
if (rt.TypeParameter.Index < typeParameters.Count) {
return typeParameters[rt.TypeParameter.Index];
IReturnType newType = typeParameters[rt.TypeParameter.Index];
if (newType != null) {
return newType;
}
}
}
} else if (input is ArrayReturnType) {
@ -128,13 +134,13 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -128,13 +134,13 @@ namespace ICSharpCode.SharpDevelop.Dom
IReturnType t = TranslateType(e, typeParameters, convertForMethod);
if (e != t && t != null)
return new ArrayReturnType(t, input.ArrayDimensions);
} else if (input is SpecificReturnType) {
SpecificReturnType r = (SpecificReturnType)input;
} else if (input is ConstructedReturnType) {
ConstructedReturnType r = (ConstructedReturnType)input;
List<IReturnType> para = new List<IReturnType>(r.TypeParameters.Count);
for (int i = 0; i < r.TypeParameters.Count; ++i) {
para.Add(TranslateType(r.TypeParameters[i], typeParameters, convertForMethod));
}
return new SpecificReturnType(r.baseType, para);
return new ConstructedReturnType(r.baseType, para);
}
return input;
}

152
src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs

@ -6,7 +6,6 @@ @@ -6,7 +6,6 @@
// </file>
using System;
using System.Collections;
using System.Collections.Generic;
using ICSharpCode.Core;
@ -43,10 +42,10 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -43,10 +42,10 @@ namespace ICSharpCode.SharpDevelop.Dom
{
if (properties.Count == 0)
return null;
bool tmp;
bool tmp1; IReturnType[][] tmp2;
List<IMethodOrProperty> newList = new List<IMethodOrProperty>(properties.Count);
foreach (IProperty p in properties) newList.Add(p);
int[] ranking = RankOverloads(newList, arguments, false, out tmp);
int[] ranking = RankOverloads(newList, arguments, false, out tmp1, out tmp2);
int bestRanking = -1;
int best = 0;
for (int i = 0; i < ranking.Length; i++) {
@ -85,6 +84,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -85,6 +84,7 @@ namespace ICSharpCode.SharpDevelop.Dom
if (list.Count == 0) return new int[] {};
List<IMethodOrProperty> l2 = new List<IMethodOrProperty>(list.Count);
IReturnType[][] inferredTypeParameters;
// See ECMA-334, § 14.3
// If type parameters are specified, remove all methods from the list that do not
@ -94,16 +94,16 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -94,16 +94,16 @@ namespace ICSharpCode.SharpDevelop.Dom
IMethod m = list[i];
if (m.TypeParameters.Count == typeParameters.Length) {
m = (IMethod)m.Clone();
m.ReturnType = SpecificReturnType.TranslateType(m.ReturnType, typeParameters, true);
m.ReturnType = ConstructedReturnType.TranslateType(m.ReturnType, typeParameters, true);
for (int j = 0; j < m.Parameters.Count; ++j) {
m.Parameters[j].ReturnType = SpecificReturnType.TranslateType(m.Parameters[j].ReturnType, typeParameters, true);
m.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(m.Parameters[j].ReturnType, typeParameters, true);
}
list[i] = m;
l2.Add(m);
}
}
int[] innerRanking = RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch);
int[] innerRanking = RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch, out inferredTypeParameters);
int[] ranking = new int[list.Count];
int innerIndex = 0;
for (int i = 0; i < ranking.Length; i++) {
@ -120,7 +120,26 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -120,7 +120,26 @@ namespace ICSharpCode.SharpDevelop.Dom
// type arguments.
foreach (IMethod m in list) l2.Add(m);
return RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch);
int[] ranking = RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch, out inferredTypeParameters);
ApplyInferredTypeParameters(list, inferredTypeParameters);
return ranking;
}
}
static void ApplyInferredTypeParameters(IList<IMethod> list, IReturnType[][] inferredTypeParameters)
{
if (inferredTypeParameters == null)
return;
for (int i = 0; i < list.Count; i++) {
IReturnType[] inferred = inferredTypeParameters[i];
if (inferred != null && inferred.Length > 0) {
IMethod m = (IMethod)list[i].Clone();
m.ReturnType = ConstructedReturnType.TranslateType(m.ReturnType, inferred, true);
for (int j = 0; j < m.Parameters.Count; ++j) {
m.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(m.Parameters[j].ReturnType, inferred, true);
}
list[i] = m;
}
}
}
#endregion
@ -133,9 +152,11 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -133,9 +152,11 @@ namespace ICSharpCode.SharpDevelop.Dom
public static int[] RankOverloads(IList<IMethodOrProperty> list,
IReturnType[] arguments,
bool allowAdditionalArguments,
out bool acceptableMatch)
out bool acceptableMatch,
out IReturnType[][] inferredTypeParameters)
{
acceptableMatch = false;
inferredTypeParameters = null;
if (list.Count == 0) return new int[] {};
int[] ranking = new int[list.Count];
@ -161,11 +182,124 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -161,11 +182,124 @@ namespace ICSharpCode.SharpDevelop.Dom
// a higher score than non-applicable members
// The first step is to expand the methods and do type argument substitution
IReturnType[][] expandedParameters = ExpandParametersAndSubstitute(list, arguments, maxScore, ranking, needToExpand, out inferredTypeParameters);
// § 14.4.2.2 Better function member
// TODO: Continue writing the member lookup stuff!
return ranking;
}
static IReturnType[][] ExpandParametersAndSubstitute(IList<IMethodOrProperty> list,
IReturnType[] arguments,
int maxScore, int[] ranking, bool[] needToExpand,
out IReturnType[][] inferredTypeParameters)
{
IReturnType[][] expandedParameters = new IReturnType[list.Count][];
inferredTypeParameters = new IReturnType[list.Count][];
for (int i = 0; i < ranking.Length; i++) {
if (ranking[i] == maxScore) {
List<IParameter> parameters = list[i].Parameters;
IReturnType[] typeParameters = (list[i] is IMethod) ? InferTypeArguments((IMethod)list[i], arguments) : null;
inferredTypeParameters[i] = typeParameters;
IReturnType paramsType = null;
expandedParameters[i] = new IReturnType[arguments.Length];
for (int j = 0; j < arguments.Length; j++) {
if (j < parameters.Count) {
IParameter parameter = parameters[j];
if (parameter.IsParams && needToExpand[i]) {
if (parameter.ReturnType is ArrayReturnType) {
paramsType = ((ArrayReturnType)parameter.ReturnType).ElementType;
paramsType = ConstructedReturnType.TranslateType(paramsType, typeParameters, true);
}
expandedParameters[i][j] = paramsType;
} else {
expandedParameters[i][j] = ConstructedReturnType.TranslateType(parameter.ReturnType, typeParameters, true);
}
} else {
expandedParameters[i][j] = paramsType;
}
}
}
}
return expandedParameters;
}
static bool IsBetterFunctionMember()
{
return false;
}
#endregion
#region Type Argument Inference
static IReturnType[] InferTypeArguments(IMethod method, IReturnType[] arguments)
{
// §25.6.4 Inference of type arguments
int count = method.TypeParameters.Count;
if (count == 0) return null;
IReturnType[] result = new IReturnType[count];
List<IParameter> parameters = method.Parameters;
for (int i = 0; i < arguments.Length; i++) {
if (i >= parameters.Count)
break;
if (!InferTypeArgument(parameters[i].ReturnType, arguments[i], result)) {
// inferring failed: maybe this is a params parameter that must be expanded?
if (parameters[i].IsParams) {
ArrayReturnType art = parameters[i].ReturnType as ArrayReturnType;
if (art != null) {
InferTypeArgument(art.ElementType, arguments[i], result);
}
}
}
}
// only return the result array when there something was inferred
for (int i = 0; i < result.Length; i++) {
if (result[i] != null) {
return result;
}
}
return null;
}
static bool InferTypeArgument(IReturnType expectedArgument, IReturnType passedArgument, IReturnType[] outputArray)
{
if (passedArgument == null) return true; // TODO: NullTypeReference
ArrayReturnType ea = expectedArgument as ArrayReturnType;
if (ea != null) {
if (passedArgument is ArrayReturnType && ea.ArrayDimensions == passedArgument.ArrayDimensions) {
return InferTypeArgument(ea.ElementType, ((ArrayReturnType)passedArgument).ElementType, outputArray);
} else if (passedArgument is ConstructedReturnType) {
switch (passedArgument.FullyQualifiedName) {
case "System.Collections.Generic.IList":
case "System.Collections.Generic.ICollection":
case "System.Collections.Generic.IEnumerable":
return InferTypeArgument(ea.ElementType, ((ConstructedReturnType)passedArgument).TypeParameters[0], outputArray);
}
}
// If P is an array type, and A is not an array type of the same rank,
// or an instantiation of IList<>, ICollection<>, or IEnumerable<>, then
// type inference fails for the generic method.
return false;
}
GenericReturnType methodTP = expectedArgument as GenericReturnType;
if (methodTP != null && methodTP.TypeParameter.Method != null) {
if (methodTP.TypeParameter.Index < outputArray.Length) {
outputArray[methodTP.TypeParameter.Index] = passedArgument;
}
return true;
}
ConstructedReturnType constructed = expectedArgument as ConstructedReturnType;
if (constructed != null) {
// The spec for this case is quite complex.
// For our purposes, we can simplify enourmously:
ConstructedReturnType passed = passedArgument as ConstructedReturnType;
if (passed == null) return false;
int count = Math.Min(constructed.TypeParameters.Count, passed.TypeParameters.Count);
for (int i = 0; i < count; i++) {
InferTypeArgument(constructed.TypeParameters[i], passed.TypeParameters[i], outputArray);
}
}
return true;
}
#endregion
#region IsApplicable

2
src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs

@ -541,7 +541,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -541,7 +541,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
for (int i = 0; i < reference.GenericTypes.Count; ++i) {
para.Add(CreateReturnType(reference.GenericTypes[i], callingClass, callingMember, caretLine, caretColumn, projectContent, useLazyReturnType));
}
t = new SpecificReturnType(t, para);
t = new ConstructedReturnType(t, para);
}
return WrapArray(t, reference);
}

2
src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs

@ -165,7 +165,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -165,7 +165,7 @@ namespace ICSharpCode.SharpDevelop.Dom
for (int i = 0; i < args.Length; ++i) {
para.Add(Create(member, args[i], createLazyReturnType));
}
return new SpecificReturnType(Create(member, type.GetGenericTypeDefinition(), createLazyReturnType), para);
return new ConstructedReturnType(Create(member, type.GetGenericTypeDefinition(), createLazyReturnType), para);
} else if (type.IsGenericParameter) {
IClass c = member.DeclaringType;
if (type.GenericParameterPosition < c.TypeParameters.Count) {

6
src/Main/Base/Project/Src/Services/ParserService/ParserService.cs

@ -265,6 +265,12 @@ namespace ICSharpCode.Core @@ -265,6 +265,12 @@ namespace ICSharpCode.Core
}
}
/// <summary>
/// <para>This event is called every two seconds. It is called directly after the parser has updated the
/// project content and it is called after the parser noticed that there is nothing to update.</para>
/// <para><b>WARNING: This event is called on the parser thread - You need to use Invoke if you do
/// anything in your event handler that could touch the GUI.</b></para>
/// </summary>
public static event ParserUpdateStepEventHandler ParserUpdateStepFinished;
static void OnParserUpdateStepFinished(ParserUpdateStepEventArgs e)

8
src/Main/Base/Test/GenericResolverTests.cs

@ -68,9 +68,9 @@ class TestClass { @@ -68,9 +68,9 @@ class TestClass {
Assert.IsTrue(result is MemberResolveResult);
IMethod m = (IMethod)((MemberResolveResult)result).ResolvedMember;
Assert.AreEqual(1, m.Parameters.Count);
Assert.IsTrue(m.Parameters[0].ReturnType is SpecificReturnType);
Assert.IsTrue(m.Parameters[0].ReturnType is ConstructedReturnType);
Assert.AreEqual("System.Collections.Generic.IEnumerable", m.Parameters[0].ReturnType.FullyQualifiedName);
Assert.AreEqual("TestClass", ((SpecificReturnType)m.Parameters[0].ReturnType).TypeParameters[0].FullyQualifiedName);
Assert.AreEqual("TestClass", ((ConstructedReturnType)m.Parameters[0].ReturnType).TypeParameters[0].FullyQualifiedName);
}
[Test]
@ -91,8 +91,8 @@ class TestClass { @@ -91,8 +91,8 @@ class TestClass {
Assert.IsNotNull(result);
Assert.IsTrue(result is TypeResolveResult);
Assert.AreEqual("System.Collections.Generic.List", ((TypeResolveResult)result).ResolvedClass.FullyQualifiedName);
Assert.IsTrue(result.ResolvedType is SpecificReturnType);
Assert.AreEqual("System.String", ((SpecificReturnType)result.ResolvedType).TypeParameters[0].FullyQualifiedName);
Assert.IsTrue(result.ResolvedType is ConstructedReturnType);
Assert.AreEqual("System.String", ((ConstructedReturnType)result.ResolvedType).TypeParameters[0].FullyQualifiedName);
}
[Test]

Loading…
Cancel
Save