From 395257996dff6e89ab11d28cfca499634d11ead3 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Aug 2005 21:30:12 +0000 Subject: [PATCH] Implement type argument inference for generic method calls. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@449 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/CSharpAmbience.cs | 4 +- .../Project/Src/CSharpCompletionBinding.cs | 3 +- .../VBNetBinding/Project/Src/VBNetAmbience.cs | 4 +- .../Project/Src/VBNetCompletionBinding.cs | 3 +- .../ParameterDeclarationExpression.cs | 9 +- .../Project/ICSharpCode.SharpDevelop.csproj | 2 +- ...ReturnType.cs => ConstructedReturnType.cs} | 26 +-- .../Project/Src/Dom/MemberLookupHelper.cs | 152 ++++++++++++++++-- .../Src/Dom/NRefactoryResolver/TypeVisitor.cs | 2 +- .../ReflectionLayer/ReflectionReturnType.cs | 2 +- .../Services/ParserService/ParserService.cs | 6 + src/Main/Base/Test/GenericResolverTests.cs | 8 +- 12 files changed, 184 insertions(+), 37 deletions(-) rename src/Main/Base/Project/Src/Dom/Implementations/{SpecificReturnType.cs => ConstructedReturnType.cs} (87%) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs index de72805f21..76f48cacc9 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs @@ -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) { diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs index 184f1c5ee5..77a443413a 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs @@ -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; diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs index b5eab67c66..e4471dafde 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs @@ -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) { diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs index a7e8b65e81..ff6c85ca9f 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs @@ -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; diff --git a/src/Libraries/NRefactory/Project/Src/Parser/AST/General/TypeLevel/ParameterDeclarationExpression.cs b/src/Libraries/NRefactory/Project/Src/Parser/AST/General/TypeLevel/ParameterDeclarationExpression.cs index 642a13775c..db0accecd4 100644 --- a/src/Libraries/NRefactory/Project/Src/Parser/AST/General/TypeLevel/ParameterDeclarationExpression.cs +++ b/src/Libraries/NRefactory/Project/Src/Parser/AST/General/TypeLevel/ParameterDeclarationExpression.cs @@ -1,4 +1,4 @@ -// +// // 2002-2005 AlphaSierraPapa // GNU General Public License // @@ -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 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; } diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 7515aaa6ba..940272ddb3 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -641,7 +641,7 @@ - + diff --git a/src/Main/Base/Project/Src/Dom/Implementations/SpecificReturnType.cs b/src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs similarity index 87% rename from src/Main/Base/Project/Src/Dom/Implementations/SpecificReturnType.cs rename to src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs index 45d2531d03..d3eeb5f151 100644 --- a/src/Main/Base/Project/Src/Dom/Implementations/SpecificReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs @@ -13,13 +13,13 @@ using ICSharpCode.Core; namespace ICSharpCode.SharpDevelop.Dom { /// - /// 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 /// s are replaced with the return types in the type parameters /// collection. /// Example: List<string> /// - 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 } } - public SpecificReturnType(IReturnType baseType, List typeParameters) + public ConstructedReturnType(IReturnType baseType, List typeParameters) { if (baseType == null) throw new ArgumentNullException("baseType"); @@ -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 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 public static IReturnType TranslateType(IReturnType input, IList 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 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 para = new List(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; } diff --git a/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs b/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs index 3d627c1034..89d967caf9 100644 --- a/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs +++ b/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs @@ -6,7 +6,6 @@ // using System; -using System.Collections; using System.Collections.Generic; using ICSharpCode.Core; @@ -43,10 +42,10 @@ namespace ICSharpCode.SharpDevelop.Dom { if (properties.Count == 0) return null; - bool tmp; + bool tmp1; IReturnType[][] tmp2; List newList = new List(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 if (list.Count == 0) return new int[] {}; List l2 = new List(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 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 // 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 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 public static int[] RankOverloads(IList 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 // 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 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 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 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 diff --git a/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs b/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs index 18613f7854..e96c657992 100644 --- a/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs +++ b/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs @@ -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); } diff --git a/src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs b/src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs index e9dd9996a6..3b745cfe18 100644 --- a/src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs +++ b/src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs @@ -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) { diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs index 2e07b3016b..bf02c55d2f 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs @@ -265,6 +265,12 @@ namespace ICSharpCode.Core } } + /// + /// 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. + /// 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. + /// public static event ParserUpdateStepEventHandler ParserUpdateStepFinished; static void OnParserUpdateStepFinished(ParserUpdateStepEventArgs e) diff --git a/src/Main/Base/Test/GenericResolverTests.cs b/src/Main/Base/Test/GenericResolverTests.cs index d6f7a1aaf6..eb15d9adfc 100644 --- a/src/Main/Base/Test/GenericResolverTests.cs +++ b/src/Main/Base/Test/GenericResolverTests.cs @@ -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 { 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]