1 | // Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team |
---|
2 | // |
---|
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this |
---|
4 | // software and associated documentation files (the "Software"), to deal in the Software |
---|
5 | // without restriction, including without limitation the rights to use, copy, modify, merge, |
---|
6 | // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
---|
7 | // to whom the Software is furnished to do so, subject to the following conditions: |
---|
8 | // |
---|
9 | // The above copyright notice and this permission notice shall be included in all copies or |
---|
10 | // substantial portions of the Software. |
---|
11 | // |
---|
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
---|
13 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
---|
14 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
---|
15 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
---|
16 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
17 | // DEALINGS IN THE SOFTWARE. |
---|
18 | |
---|
19 | using System; |
---|
20 | using System.Collections.Generic; |
---|
21 | using System.Diagnostics; |
---|
22 | using System.Linq; |
---|
23 | |
---|
24 | using ICSharpCode.NRefactory.Semantics; |
---|
25 | using ICSharpCode.NRefactory.TypeSystem; |
---|
26 | using ICSharpCode.NRefactory.TypeSystem.Implementation; |
---|
27 | |
---|
28 | namespace ICSharpCode.NRefactory.CSharp.Resolver |
---|
29 | { |
---|
30 | public enum TypeInferenceAlgorithm |
---|
31 | { |
---|
32 | /// <summary> |
---|
33 | /// C# 4.0 type inference. |
---|
34 | /// </summary> |
---|
35 | CSharp4, |
---|
36 | /// <summary> |
---|
37 | /// Improved algorithm (not part of any specification) using FindTypeInBounds for fixing. |
---|
38 | /// </summary> |
---|
39 | Improved, |
---|
40 | /// <summary> |
---|
41 | /// Improved algorithm (not part of any specification) using FindTypeInBounds for fixing; |
---|
42 | /// uses <see cref="IntersectionType"/> to report all results (in case of ambiguities). |
---|
43 | /// </summary> |
---|
44 | ImprovedReturnAllResults |
---|
45 | } |
---|
46 | |
---|
47 | /// <summary> |
---|
48 | /// Implements C# 4.0 Type Inference (§7.5.2). |
---|
49 | /// </summary> |
---|
50 | public sealed class TypeInference |
---|
51 | { |
---|
52 | readonly ICompilation compilation; |
---|
53 | readonly CSharpConversions conversions; |
---|
54 | TypeInferenceAlgorithm algorithm = TypeInferenceAlgorithm.CSharp4; |
---|
55 | |
---|
56 | // determines the maximum generic nesting level; necessary to avoid infinite recursion in 'Improved' mode. |
---|
57 | const int maxNestingLevel = 5; |
---|
58 | int nestingLevel; |
---|
59 | |
---|
60 | #region Constructor |
---|
61 | public TypeInference(ICompilation compilation) |
---|
62 | { |
---|
63 | if (compilation == null) |
---|
64 | throw new ArgumentNullException("compilation"); |
---|
65 | this.compilation = compilation; |
---|
66 | this.conversions = CSharpConversions.Get(compilation); |
---|
67 | } |
---|
68 | |
---|
69 | internal TypeInference(ICompilation compilation, CSharpConversions conversions) |
---|
70 | { |
---|
71 | Debug.Assert(compilation != null); |
---|
72 | Debug.Assert(conversions != null); |
---|
73 | this.compilation = compilation; |
---|
74 | this.conversions = conversions; |
---|
75 | } |
---|
76 | #endregion |
---|
77 | |
---|
78 | #region Properties |
---|
79 | /// <summary> |
---|
80 | /// Gets/Sets the type inference algorithm used. |
---|
81 | /// </summary> |
---|
82 | public TypeInferenceAlgorithm Algorithm { |
---|
83 | get { return algorithm; } |
---|
84 | set { algorithm = value; } |
---|
85 | } |
---|
86 | |
---|
87 | TypeInference CreateNestedInstance() |
---|
88 | { |
---|
89 | TypeInference c = new TypeInference(compilation, conversions); |
---|
90 | c.algorithm = algorithm; |
---|
91 | c.nestingLevel = nestingLevel + 1; |
---|
92 | return c; |
---|
93 | } |
---|
94 | #endregion |
---|
95 | |
---|
96 | TP[] typeParameters; |
---|
97 | IType[] parameterTypes; |
---|
98 | ResolveResult[] arguments; |
---|
99 | bool[,] dependencyMatrix; |
---|
100 | IList<IType> classTypeArguments; |
---|
101 | |
---|
102 | #region InferTypeArguments (main function) |
---|
103 | /// <summary> |
---|
104 | /// Performs type inference. |
---|
105 | /// </summary> |
---|
106 | /// <param name="typeParameters">The method type parameters that should be inferred.</param> |
---|
107 | /// <param name="arguments">The arguments passed to the method.</param> |
---|
108 | /// <param name="parameterTypes">The parameter types of the method.</param> |
---|
109 | /// <param name="success">Out: whether type inference was successful</param> |
---|
110 | /// <param name="classTypeArguments"> |
---|
111 | /// Class type arguments. These are substituted for class type parameters in the formal parameter types |
---|
112 | /// when inferring a method group or lambda. |
---|
113 | /// </param> |
---|
114 | /// <returns>The inferred type arguments.</returns> |
---|
115 | public IType[] InferTypeArguments(IList<ITypeParameter> typeParameters, IList<ResolveResult> arguments, IList<IType> parameterTypes, out bool success, IList<IType> classTypeArguments = null) |
---|
116 | { |
---|
117 | if (typeParameters == null) |
---|
118 | throw new ArgumentNullException("typeParameters"); |
---|
119 | if (arguments == null) |
---|
120 | throw new ArgumentNullException("arguments"); |
---|
121 | if (parameterTypes == null) |
---|
122 | throw new ArgumentNullException("parameterTypes"); |
---|
123 | try { |
---|
124 | this.typeParameters = new TP[typeParameters.Count]; |
---|
125 | for (int i = 0; i < this.typeParameters.Length; i++) { |
---|
126 | if (i != typeParameters[i].Index) |
---|
127 | throw new ArgumentException("Type parameter has wrong index"); |
---|
128 | if (typeParameters[i].OwnerType != SymbolKind.Method) |
---|
129 | throw new ArgumentException("Type parameter must be owned by a method"); |
---|
130 | this.typeParameters[i] = new TP(typeParameters[i]); |
---|
131 | } |
---|
132 | this.parameterTypes = new IType[Math.Min(arguments.Count, parameterTypes.Count)]; |
---|
133 | this.arguments = new ResolveResult[this.parameterTypes.Length]; |
---|
134 | for (int i = 0; i < this.parameterTypes.Length; i++) { |
---|
135 | if (arguments[i] == null || parameterTypes[i] == null) |
---|
136 | throw new ArgumentNullException(); |
---|
137 | this.arguments[i] = arguments[i]; |
---|
138 | this.parameterTypes[i] = parameterTypes[i]; |
---|
139 | } |
---|
140 | this.classTypeArguments = classTypeArguments; |
---|
141 | Log.WriteLine("Type Inference"); |
---|
142 | Log.WriteLine(" Signature: M<" + string.Join<TP>(", ", this.typeParameters) + ">" |
---|
143 | + "(" + string.Join<IType>(", ", this.parameterTypes) + ")"); |
---|
144 | Log.WriteCollection(" Arguments: ", arguments); |
---|
145 | Log.Indent(); |
---|
146 | |
---|
147 | PhaseOne(); |
---|
148 | success = PhaseTwo(); |
---|
149 | |
---|
150 | Log.Unindent(); |
---|
151 | Log.WriteLine(" Type inference finished " + (success ? "successfully" : "with errors") + ": " + |
---|
152 | "M<" + string.Join(", ", this.typeParameters.Select(tp => tp.FixedTo ?? SpecialType.UnknownType)) + ">"); |
---|
153 | return this.typeParameters.Select(tp => tp.FixedTo ?? SpecialType.UnknownType).ToArray(); |
---|
154 | } finally { |
---|
155 | Reset(); |
---|
156 | } |
---|
157 | } |
---|
158 | |
---|
159 | void Reset() |
---|
160 | { |
---|
161 | // clean up so that memory used by the operation can be garbage collected as soon as possible |
---|
162 | this.typeParameters = null; |
---|
163 | this.parameterTypes = null; |
---|
164 | this.arguments = null; |
---|
165 | this.dependencyMatrix = null; |
---|
166 | this.classTypeArguments = null; |
---|
167 | } |
---|
168 | |
---|
169 | /// <summary> |
---|
170 | /// Infers type arguments for the <paramref name="typeParameters"/> occurring in the <paramref name="targetType"/> |
---|
171 | /// so that the resulting type (after substition) satisfies the given bounds. |
---|
172 | /// </summary> |
---|
173 | public IType[] InferTypeArgumentsFromBounds(IList<ITypeParameter> typeParameters, IType targetType, IList<IType> lowerBounds, IList<IType> upperBounds, out bool success) |
---|
174 | { |
---|
175 | if (typeParameters == null) |
---|
176 | throw new ArgumentNullException("typeParameters"); |
---|
177 | if (targetType == null) |
---|
178 | throw new ArgumentNullException("targetType"); |
---|
179 | if (lowerBounds == null) |
---|
180 | throw new ArgumentNullException("lowerBounds"); |
---|
181 | if (upperBounds == null) |
---|
182 | throw new ArgumentNullException("upperBounds"); |
---|
183 | this.typeParameters = new TP[typeParameters.Count]; |
---|
184 | for (int i = 0; i < this.typeParameters.Length; i++) { |
---|
185 | if (i != typeParameters[i].Index) |
---|
186 | throw new ArgumentException("Type parameter has wrong index"); |
---|
187 | this.typeParameters[i] = new TP(typeParameters[i]); |
---|
188 | } |
---|
189 | foreach (IType b in lowerBounds) { |
---|
190 | MakeLowerBoundInference(b, targetType); |
---|
191 | } |
---|
192 | foreach (IType b in upperBounds) { |
---|
193 | MakeUpperBoundInference(b, targetType); |
---|
194 | } |
---|
195 | IType[] result = new IType[this.typeParameters.Length]; |
---|
196 | success = true; |
---|
197 | for (int i = 0; i < result.Length; i++) { |
---|
198 | success &= Fix(this.typeParameters[i]); |
---|
199 | result[i] = this.typeParameters[i].FixedTo ?? SpecialType.UnknownType; |
---|
200 | } |
---|
201 | Reset(); |
---|
202 | return result; |
---|
203 | } |
---|
204 | #endregion |
---|
205 | |
---|
206 | sealed class TP |
---|
207 | { |
---|
208 | public readonly HashSet<IType> LowerBounds = new HashSet<IType>(); |
---|
209 | public readonly HashSet<IType> UpperBounds = new HashSet<IType>(); |
---|
210 | public IType ExactBound; |
---|
211 | public bool MultipleDifferentExactBounds; |
---|
212 | public readonly ITypeParameter TypeParameter; |
---|
213 | public IType FixedTo; |
---|
214 | |
---|
215 | public bool IsFixed { |
---|
216 | get { return FixedTo != null; } |
---|
217 | } |
---|
218 | |
---|
219 | public bool HasBounds { |
---|
220 | get { return LowerBounds.Count > 0 || UpperBounds.Count > 0 || ExactBound != null; } |
---|
221 | } |
---|
222 | |
---|
223 | public TP(ITypeParameter typeParameter) |
---|
224 | { |
---|
225 | if (typeParameter == null) |
---|
226 | throw new ArgumentNullException("typeParameter"); |
---|
227 | this.TypeParameter = typeParameter; |
---|
228 | } |
---|
229 | |
---|
230 | public void AddExactBound(IType type) |
---|
231 | { |
---|
232 | // Exact bounds need to stored separately, not just as Lower+Upper bounds, |
---|
233 | // due to TypeInferenceTests.GenericArgumentImplicitlyConvertibleToAndFromAnotherTypeList (see #281) |
---|
234 | if (ExactBound == null) |
---|
235 | ExactBound = type; |
---|
236 | else if (!ExactBound.Equals(type)) |
---|
237 | MultipleDifferentExactBounds = true; |
---|
238 | } |
---|
239 | |
---|
240 | public override string ToString() |
---|
241 | { |
---|
242 | return TypeParameter.Name; |
---|
243 | } |
---|
244 | } |
---|
245 | |
---|
246 | sealed class OccursInVisitor : TypeVisitor |
---|
247 | { |
---|
248 | readonly TP[] tp; |
---|
249 | public readonly bool[] Occurs; |
---|
250 | |
---|
251 | public OccursInVisitor(TypeInference typeInference) |
---|
252 | { |
---|
253 | this.tp = typeInference.typeParameters; |
---|
254 | this.Occurs = new bool[tp.Length]; |
---|
255 | } |
---|
256 | |
---|
257 | public override IType VisitTypeParameter(ITypeParameter type) |
---|
258 | { |
---|
259 | int index = type.Index; |
---|
260 | if (index < tp.Length && tp[index].TypeParameter == type) |
---|
261 | Occurs[index] = true; |
---|
262 | return base.VisitTypeParameter(type); |
---|
263 | } |
---|
264 | } |
---|
265 | |
---|
266 | #region Inference Phases |
---|
267 | void PhaseOne() |
---|
268 | { |
---|
269 | // C# 4.0 spec: §7.5.2.1 The first phase |
---|
270 | Log.WriteLine("Phase One"); |
---|
271 | for (int i = 0; i < arguments.Length; i++) { |
---|
272 | ResolveResult Ei = arguments[i]; |
---|
273 | IType Ti = parameterTypes[i]; |
---|
274 | |
---|
275 | LambdaResolveResult lrr = Ei as LambdaResolveResult; |
---|
276 | if (lrr != null) { |
---|
277 | MakeExplicitParameterTypeInference(lrr, Ti); |
---|
278 | } |
---|
279 | if (lrr != null || Ei is MethodGroupResolveResult) { |
---|
280 | // this is not in the spec??? |
---|
281 | if (OutputTypeContainsUnfixed(Ei, Ti) && !InputTypesContainsUnfixed(Ei, Ti)) { |
---|
282 | MakeOutputTypeInference(Ei, Ti); |
---|
283 | } |
---|
284 | } |
---|
285 | |
---|
286 | if (IsValidType(Ei.Type)) { |
---|
287 | if (Ti is ByReferenceType) { |
---|
288 | MakeExactInference(Ei.Type, Ti); |
---|
289 | } else { |
---|
290 | MakeLowerBoundInference(Ei.Type, Ti); |
---|
291 | } |
---|
292 | } |
---|
293 | } |
---|
294 | } |
---|
295 | |
---|
296 | static bool IsValidType(IType type) |
---|
297 | { |
---|
298 | return type.Kind != TypeKind.Unknown && type.Kind != TypeKind.Null; |
---|
299 | } |
---|
300 | |
---|
301 | bool PhaseTwo() |
---|
302 | { |
---|
303 | // C# 4.0 spec: §7.5.2.2 The second phase |
---|
304 | Log.WriteLine("Phase Two"); |
---|
305 | // All unfixed type variables Xi which do not depend on any Xj are fixed. |
---|
306 | List<TP> typeParametersToFix = new List<TP>(); |
---|
307 | foreach (TP Xi in typeParameters) { |
---|
308 | if (Xi.IsFixed == false) { |
---|
309 | if (!typeParameters.Any((TP Xj) => !Xj.IsFixed && DependsOn(Xi, Xj))) { |
---|
310 | typeParametersToFix.Add(Xi); |
---|
311 | } |
---|
312 | } |
---|
313 | } |
---|
314 | // If no such type variables exist, all unfixed type variables Xi are fixed for which all of the following hold: |
---|
315 | if (typeParametersToFix.Count == 0) { |
---|
316 | Log.WriteLine("Type parameters cannot be fixed due to dependency cycles"); |
---|
317 | Log.WriteLine("Trying to break the cycle by fixing any TPs that have non-empty bounds..."); |
---|
318 | foreach (TP Xi in typeParameters) { |
---|
319 | // Xi has a nonempty set of bounds |
---|
320 | if (!Xi.IsFixed && Xi.HasBounds) { |
---|
321 | // There is at least one type variable Xj that depends on Xi |
---|
322 | if (typeParameters.Any((TP Xj) => DependsOn(Xj, Xi))) { |
---|
323 | typeParametersToFix.Add(Xi); |
---|
324 | } |
---|
325 | } |
---|
326 | } |
---|
327 | } |
---|
328 | // now fix 'em |
---|
329 | bool errorDuringFix = false; |
---|
330 | foreach (TP tp in typeParametersToFix) { |
---|
331 | if (!Fix(tp)) |
---|
332 | errorDuringFix = true; |
---|
333 | } |
---|
334 | if (errorDuringFix) |
---|
335 | return false; |
---|
336 | bool unfixedTypeVariablesExist = typeParameters.Any((TP X) => X.IsFixed == false); |
---|
337 | if (typeParametersToFix.Count == 0 && unfixedTypeVariablesExist) { |
---|
338 | // If no such type variables exist and there are still unfixed type variables, type inference fails. |
---|
339 | Log.WriteLine("Type inference fails: there are still unfixed TPs remaining"); |
---|
340 | return false; |
---|
341 | } else if (!unfixedTypeVariablesExist) { |
---|
342 | // Otherwise, if no further unfixed type variables exist, type inference succeeds. |
---|
343 | return true; |
---|
344 | } else { |
---|
345 | // Otherwise, for all arguments ei with corresponding parameter type Ti |
---|
346 | for (int i = 0; i < arguments.Length; i++) { |
---|
347 | ResolveResult Ei = arguments[i]; |
---|
348 | IType Ti = parameterTypes[i]; |
---|
349 | // where the output types (§7.4.2.4) contain unfixed type variables Xj |
---|
350 | // but the input types (§7.4.2.3) do not |
---|
351 | if (OutputTypeContainsUnfixed(Ei, Ti) && !InputTypesContainsUnfixed(Ei, Ti)) { |
---|
352 | // an output type inference (§7.4.2.6) is made for ei with type Ti. |
---|
353 | Log.WriteLine("MakeOutputTypeInference for argument #" + i); |
---|
354 | MakeOutputTypeInference(Ei, Ti); |
---|
355 | } |
---|
356 | } |
---|
357 | // Then the second phase is repeated. |
---|
358 | return PhaseTwo(); |
---|
359 | } |
---|
360 | } |
---|
361 | #endregion |
---|
362 | |
---|
363 | #region Input Types / Output Types (§7.5.2.3 + §7.5.2.4) |
---|
364 | static readonly IType[] emptyTypeArray = new IType[0]; |
---|
365 | |
---|
366 | IType[] InputTypes(ResolveResult e, IType t) |
---|
367 | { |
---|
368 | // C# 4.0 spec: §7.5.2.3 Input types |
---|
369 | LambdaResolveResult lrr = e as LambdaResolveResult; |
---|
370 | if (lrr != null && lrr.IsImplicitlyTyped || e is MethodGroupResolveResult) { |
---|
371 | IMethod m = GetDelegateOrExpressionTreeSignature(t); |
---|
372 | if (m != null) { |
---|
373 | IType[] inputTypes = new IType[m.Parameters.Count]; |
---|
374 | for (int i = 0; i < inputTypes.Length; i++) { |
---|
375 | inputTypes[i] = m.Parameters[i].Type; |
---|
376 | } |
---|
377 | return inputTypes; |
---|
378 | } |
---|
379 | } |
---|
380 | return emptyTypeArray; |
---|
381 | } |
---|
382 | |
---|
383 | IType[] OutputTypes(ResolveResult e, IType t) |
---|
384 | { |
---|
385 | // C# 4.0 spec: §7.5.2.4 Output types |
---|
386 | LambdaResolveResult lrr = e as LambdaResolveResult; |
---|
387 | if (lrr != null || e is MethodGroupResolveResult) { |
---|
388 | IMethod m = GetDelegateOrExpressionTreeSignature(t); |
---|
389 | if (m != null) { |
---|
390 | return new[] { m.ReturnType }; |
---|
391 | } |
---|
392 | } |
---|
393 | return emptyTypeArray; |
---|
394 | } |
---|
395 | |
---|
396 | static IMethod GetDelegateOrExpressionTreeSignature(IType t) |
---|
397 | { |
---|
398 | ParameterizedType pt = t as ParameterizedType; |
---|
399 | if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Expression" |
---|
400 | && pt.Namespace == "System.Linq.Expressions") |
---|
401 | { |
---|
402 | t = pt.GetTypeArgument(0); |
---|
403 | } |
---|
404 | return t.GetDelegateInvokeMethod(); |
---|
405 | } |
---|
406 | |
---|
407 | bool InputTypesContainsUnfixed(ResolveResult argument, IType parameterType) |
---|
408 | { |
---|
409 | return AnyTypeContainsUnfixedParameter(InputTypes(argument, parameterType)); |
---|
410 | } |
---|
411 | |
---|
412 | bool OutputTypeContainsUnfixed(ResolveResult argument, IType parameterType) |
---|
413 | { |
---|
414 | return AnyTypeContainsUnfixedParameter(OutputTypes(argument, parameterType)); |
---|
415 | } |
---|
416 | |
---|
417 | bool AnyTypeContainsUnfixedParameter(IEnumerable<IType> types) |
---|
418 | { |
---|
419 | OccursInVisitor o = new OccursInVisitor(this); |
---|
420 | foreach (var type in types) { |
---|
421 | type.AcceptVisitor(o); |
---|
422 | } |
---|
423 | for (int i = 0; i < typeParameters.Length; i++) { |
---|
424 | if (!typeParameters[i].IsFixed && o.Occurs[i]) |
---|
425 | return true; |
---|
426 | } |
---|
427 | return false; |
---|
428 | } |
---|
429 | #endregion |
---|
430 | |
---|
431 | #region DependsOn (§7.5.2.5) |
---|
432 | // C# 4.0 spec: §7.5.2.5 Dependance |
---|
433 | |
---|
434 | void CalculateDependencyMatrix() |
---|
435 | { |
---|
436 | int n = typeParameters.Length; |
---|
437 | dependencyMatrix = new bool[n, n]; |
---|
438 | for (int k = 0; k < arguments.Length; k++) { |
---|
439 | OccursInVisitor input = new OccursInVisitor(this); |
---|
440 | OccursInVisitor output = new OccursInVisitor(this); |
---|
441 | foreach (var type in InputTypes(arguments[k], parameterTypes[k])) { |
---|
442 | type.AcceptVisitor(input); |
---|
443 | } |
---|
444 | foreach (var type in OutputTypes(arguments[k], parameterTypes[k])) { |
---|
445 | type.AcceptVisitor(output); |
---|
446 | } |
---|
447 | for (int i = 0; i < n; i++) { |
---|
448 | for (int j = 0; j < n; j++) { |
---|
449 | dependencyMatrix[i, j] |= input.Occurs[j] && output.Occurs[i]; |
---|
450 | } |
---|
451 | } |
---|
452 | } |
---|
453 | // calculate transitive closure using Warshall's algorithm: |
---|
454 | for (int i = 0; i < n; i++) { |
---|
455 | for (int j = 0; j < n; j++) { |
---|
456 | if (dependencyMatrix[i, j]) { |
---|
457 | for (int k = 0; k < n; k++) { |
---|
458 | if (dependencyMatrix[j, k]) |
---|
459 | dependencyMatrix[i, k] = true; |
---|
460 | } |
---|
461 | } |
---|
462 | } |
---|
463 | } |
---|
464 | } |
---|
465 | |
---|
466 | bool DependsOn(TP x, TP y) |
---|
467 | { |
---|
468 | if (dependencyMatrix == null) |
---|
469 | CalculateDependencyMatrix(); |
---|
470 | // x depends on y |
---|
471 | return dependencyMatrix[x.TypeParameter.Index, y.TypeParameter.Index]; |
---|
472 | } |
---|
473 | #endregion |
---|
474 | |
---|
475 | #region MakeOutputTypeInference (§7.5.2.6) |
---|
476 | void MakeOutputTypeInference(ResolveResult e, IType t) |
---|
477 | { |
---|
478 | Log.WriteLine(" MakeOutputTypeInference from " + e + " to " + t); |
---|
479 | // If E is an anonymous function with inferred return type U (§7.5.2.12) and T is a delegate type or expression |
---|
480 | // tree type with return type Tb, then a lower-bound inference (§7.5.2.9) is made from U to Tb. |
---|
481 | LambdaResolveResult lrr = e as LambdaResolveResult; |
---|
482 | if (lrr != null) { |
---|
483 | IMethod m = GetDelegateOrExpressionTreeSignature(t); |
---|
484 | if (m != null) { |
---|
485 | IType inferredReturnType; |
---|
486 | if (lrr.IsImplicitlyTyped) { |
---|
487 | if (m.Parameters.Count != lrr.Parameters.Count) |
---|
488 | return; // cannot infer due to mismatched parameter lists |
---|
489 | TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); |
---|
490 | IType[] inferredParameterTypes = new IType[m.Parameters.Count]; |
---|
491 | for (int i = 0; i < inferredParameterTypes.Length; i++) { |
---|
492 | IType parameterType = m.Parameters[i].Type; |
---|
493 | inferredParameterTypes[i] = parameterType.AcceptVisitor(substitution); |
---|
494 | } |
---|
495 | inferredReturnType = lrr.GetInferredReturnType(inferredParameterTypes); |
---|
496 | } else { |
---|
497 | inferredReturnType = lrr.GetInferredReturnType(null); |
---|
498 | } |
---|
499 | MakeLowerBoundInference(inferredReturnType, m.ReturnType); |
---|
500 | return; |
---|
501 | } |
---|
502 | } |
---|
503 | // Otherwise, if E is a method group and T is a delegate type or expression tree type |
---|
504 | // with parameter types T1…Tk and return type Tb, and overload resolution |
---|
505 | // of E with the types T1…Tk yields a single method with return type U, then a lower-bound |
---|
506 | // inference is made from U to Tb. |
---|
507 | MethodGroupResolveResult mgrr = e as MethodGroupResolveResult; |
---|
508 | if (mgrr != null) { |
---|
509 | IMethod m = GetDelegateOrExpressionTreeSignature(t); |
---|
510 | if (m != null) { |
---|
511 | ResolveResult[] args = new ResolveResult[m.Parameters.Count]; |
---|
512 | TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); |
---|
513 | for (int i = 0; i < args.Length; i++) { |
---|
514 | IParameter param = m.Parameters[i]; |
---|
515 | IType parameterType = param.Type.AcceptVisitor(substitution); |
---|
516 | if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { |
---|
517 | parameterType = ((ByReferenceType)parameterType).ElementType; |
---|
518 | args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); |
---|
519 | } else { |
---|
520 | args[i] = new ResolveResult(parameterType); |
---|
521 | } |
---|
522 | } |
---|
523 | var or = mgrr.PerformOverloadResolution(compilation, |
---|
524 | args, |
---|
525 | allowExpandingParams: false, allowOptionalParameters: false); |
---|
526 | if (or.FoundApplicableCandidate && or.BestCandidateAmbiguousWith == null) { |
---|
527 | IType returnType = or.GetBestCandidateWithSubstitutedTypeArguments().ReturnType; |
---|
528 | MakeLowerBoundInference(returnType, m.ReturnType); |
---|
529 | } |
---|
530 | } |
---|
531 | return; |
---|
532 | } |
---|
533 | // Otherwise, if E is an expression with type U, then a lower-bound inference is made from U to T. |
---|
534 | if (IsValidType(e.Type)) { |
---|
535 | MakeLowerBoundInference(e.Type, t); |
---|
536 | } |
---|
537 | } |
---|
538 | |
---|
539 | TypeParameterSubstitution GetSubstitutionForFixedTPs() |
---|
540 | { |
---|
541 | IType[] fixedTypes = new IType[typeParameters.Length]; |
---|
542 | for (int i = 0; i < fixedTypes.Length; i++) { |
---|
543 | fixedTypes[i] = typeParameters[i].FixedTo ?? SpecialType.UnknownType; |
---|
544 | } |
---|
545 | return new TypeParameterSubstitution(classTypeArguments, fixedTypes); |
---|
546 | } |
---|
547 | #endregion |
---|
548 | |
---|
549 | #region MakeExplicitParameterTypeInference (§7.5.2.7) |
---|
550 | void MakeExplicitParameterTypeInference(LambdaResolveResult e, IType t) |
---|
551 | { |
---|
552 | // C# 4.0 spec: §7.5.2.7 Explicit parameter type inferences |
---|
553 | if (e.IsImplicitlyTyped || !e.HasParameterList) |
---|
554 | return; |
---|
555 | Log.WriteLine(" MakeExplicitParameterTypeInference from " + e + " to " + t); |
---|
556 | IMethod m = GetDelegateOrExpressionTreeSignature(t); |
---|
557 | if (m == null) |
---|
558 | return; |
---|
559 | for (int i = 0; i < e.Parameters.Count && i < m.Parameters.Count; i++) { |
---|
560 | MakeExactInference(e.Parameters[i].Type, m.Parameters[i].Type); |
---|
561 | } |
---|
562 | } |
---|
563 | #endregion |
---|
564 | |
---|
565 | #region MakeExactInference (§7.5.2.8) |
---|
566 | /// <summary> |
---|
567 | /// Make exact inference from U to V. |
---|
568 | /// C# 4.0 spec: §7.5.2.8 Exact inferences |
---|
569 | /// </summary> |
---|
570 | void MakeExactInference(IType U, IType V) |
---|
571 | { |
---|
572 | Log.WriteLine("MakeExactInference from " + U + " to " + V); |
---|
573 | |
---|
574 | // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. |
---|
575 | TP tp = GetTPForType(V); |
---|
576 | if (tp != null && tp.IsFixed == false) { |
---|
577 | Log.WriteLine(" Add exact bound '" + U + "' to " + tp); |
---|
578 | tp.AddExactBound(U); |
---|
579 | return; |
---|
580 | } |
---|
581 | // Handle by reference types: |
---|
582 | ByReferenceType brU = U as ByReferenceType; |
---|
583 | ByReferenceType brV = V as ByReferenceType; |
---|
584 | if (brU != null && brV != null) { |
---|
585 | MakeExactInference(brU.ElementType, brV.ElementType); |
---|
586 | return; |
---|
587 | } |
---|
588 | // Handle array types: |
---|
589 | ArrayType arrU = U as ArrayType; |
---|
590 | ArrayType arrV = V as ArrayType; |
---|
591 | if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) { |
---|
592 | MakeExactInference(arrU.ElementType, arrV.ElementType); |
---|
593 | return; |
---|
594 | } |
---|
595 | // Handle parameterized type: |
---|
596 | ParameterizedType pU = U as ParameterizedType; |
---|
597 | ParameterizedType pV = V as ParameterizedType; |
---|
598 | if (pU != null && pV != null |
---|
599 | && object.Equals(pU.GetDefinition(), pV.GetDefinition()) |
---|
600 | && pU.TypeParameterCount == pV.TypeParameterCount) |
---|
601 | { |
---|
602 | Log.Indent(); |
---|
603 | for (int i = 0; i < pU.TypeParameterCount; i++) { |
---|
604 | MakeExactInference(pU.GetTypeArgument(i), pV.GetTypeArgument(i)); |
---|
605 | } |
---|
606 | Log.Unindent(); |
---|
607 | } |
---|
608 | } |
---|
609 | |
---|
610 | TP GetTPForType(IType v) |
---|
611 | { |
---|
612 | ITypeParameter p = v as ITypeParameter; |
---|
613 | if (p != null) { |
---|
614 | int index = p.Index; |
---|
615 | if (index < typeParameters.Length && typeParameters[index].TypeParameter == p) |
---|
616 | return typeParameters[index]; |
---|
617 | } |
---|
618 | return null; |
---|
619 | } |
---|
620 | #endregion |
---|
621 | |
---|
622 | #region MakeLowerBoundInference (§7.5.2.9) |
---|
623 | /// <summary> |
---|
624 | /// Make lower bound inference from U to V. |
---|
625 | /// C# 4.0 spec: §7.5.2.9 Lower-bound inferences |
---|
626 | /// </summary> |
---|
627 | void MakeLowerBoundInference(IType U, IType V) |
---|
628 | { |
---|
629 | Log.WriteLine(" MakeLowerBoundInference from " + U + " to " + V); |
---|
630 | |
---|
631 | // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. |
---|
632 | TP tp = GetTPForType(V); |
---|
633 | if (tp != null && tp.IsFixed == false) { |
---|
634 | Log.WriteLine(" Add lower bound '" + U + "' to " + tp); |
---|
635 | tp.LowerBounds.Add(U); |
---|
636 | return; |
---|
637 | } |
---|
638 | // Handle nullable covariance: |
---|
639 | if (NullableType.IsNullable(U) && NullableType.IsNullable(V)) { |
---|
640 | MakeLowerBoundInference(NullableType.GetUnderlyingType(U), NullableType.GetUnderlyingType(V)); |
---|
641 | return; |
---|
642 | } |
---|
643 | |
---|
644 | // Handle array types: |
---|
645 | ArrayType arrU = U as ArrayType; |
---|
646 | ArrayType arrV = V as ArrayType; |
---|
647 | ParameterizedType pV = V as ParameterizedType; |
---|
648 | if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) { |
---|
649 | MakeLowerBoundInference(arrU.ElementType, arrV.ElementType); |
---|
650 | return; |
---|
651 | } else if (arrU != null && IsGenericInterfaceImplementedByArray(pV) && arrU.Dimensions == 1) { |
---|
652 | MakeLowerBoundInference(arrU.ElementType, pV.GetTypeArgument(0)); |
---|
653 | return; |
---|
654 | } |
---|
655 | // Handle parameterized types: |
---|
656 | if (pV != null) { |
---|
657 | ParameterizedType uniqueBaseType = null; |
---|
658 | foreach (IType baseU in U.GetAllBaseTypes()) { |
---|
659 | ParameterizedType pU = baseU as ParameterizedType; |
---|
660 | if (pU != null && object.Equals(pU.GetDefinition(), pV.GetDefinition()) && pU.TypeParameterCount == pV.TypeParameterCount) { |
---|
661 | if (uniqueBaseType == null) |
---|
662 | uniqueBaseType = pU; |
---|
663 | else |
---|
664 | return; // cannot make an inference because it's not unique |
---|
665 | } |
---|
666 | } |
---|
667 | Log.Indent(); |
---|
668 | if (uniqueBaseType != null) { |
---|
669 | for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) { |
---|
670 | IType Ui = uniqueBaseType.GetTypeArgument(i); |
---|
671 | IType Vi = pV.GetTypeArgument(i); |
---|
672 | if (Ui.IsReferenceType == true) { |
---|
673 | // look for variance |
---|
674 | ITypeParameter Xi = pV.GetDefinition().TypeParameters[i]; |
---|
675 | switch (Xi.Variance) { |
---|
676 | case VarianceModifier.Covariant: |
---|
677 | MakeLowerBoundInference(Ui, Vi); |
---|
678 | break; |
---|
679 | case VarianceModifier.Contravariant: |
---|
680 | MakeUpperBoundInference(Ui, Vi); |
---|
681 | break; |
---|
682 | default: // invariant |
---|
683 | MakeExactInference(Ui, Vi); |
---|
684 | break; |
---|
685 | } |
---|
686 | } else { |
---|
687 | // not known to be a reference type |
---|
688 | MakeExactInference(Ui, Vi); |
---|
689 | } |
---|
690 | } |
---|
691 | } |
---|
692 | Log.Unindent(); |
---|
693 | } |
---|
694 | } |
---|
695 | |
---|
696 | static bool IsGenericInterfaceImplementedByArray(ParameterizedType rt) |
---|
697 | { |
---|
698 | if (rt == null || rt.TypeParameterCount != 1) |
---|
699 | return false; |
---|
700 | switch (rt.GetDefinition().KnownTypeCode) { |
---|
701 | case KnownTypeCode.IEnumerableOfT: |
---|
702 | case KnownTypeCode.ICollectionOfT: |
---|
703 | case KnownTypeCode.IListOfT: |
---|
704 | case KnownTypeCode.IReadOnlyCollectionOfT: |
---|
705 | case KnownTypeCode.IReadOnlyListOfT: |
---|
706 | return true; |
---|
707 | default: |
---|
708 | return false; |
---|
709 | } |
---|
710 | } |
---|
711 | #endregion |
---|
712 | |
---|
713 | #region MakeUpperBoundInference (§7.5.2.10) |
---|
714 | /// <summary> |
---|
715 | /// Make upper bound inference from U to V. |
---|
716 | /// C# 4.0 spec: §7.5.2.10 Upper-bound inferences |
---|
717 | /// </summary> |
---|
718 | void MakeUpperBoundInference(IType U, IType V) |
---|
719 | { |
---|
720 | Log.WriteLine(" MakeUpperBoundInference from " + U + " to " + V); |
---|
721 | |
---|
722 | // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. |
---|
723 | TP tp = GetTPForType(V); |
---|
724 | if (tp != null && tp.IsFixed == false) { |
---|
725 | Log.WriteLine(" Add upper bound '" + U + "' to " + tp); |
---|
726 | tp.UpperBounds.Add(U); |
---|
727 | return; |
---|
728 | } |
---|
729 | |
---|
730 | // Handle array types: |
---|
731 | ArrayType arrU = U as ArrayType; |
---|
732 | ArrayType arrV = V as ArrayType; |
---|
733 | ParameterizedType pU = U as ParameterizedType; |
---|
734 | if (arrV != null && arrU != null && arrU.Dimensions == arrV.Dimensions) { |
---|
735 | MakeUpperBoundInference(arrU.ElementType, arrV.ElementType); |
---|
736 | return; |
---|
737 | } else if (arrV != null && IsGenericInterfaceImplementedByArray(pU) && arrV.Dimensions == 1) { |
---|
738 | MakeUpperBoundInference(pU.GetTypeArgument(0), arrV.ElementType); |
---|
739 | return; |
---|
740 | } |
---|
741 | // Handle parameterized types: |
---|
742 | if (pU != null) { |
---|
743 | ParameterizedType uniqueBaseType = null; |
---|
744 | foreach (IType baseV in V.GetAllBaseTypes()) { |
---|
745 | ParameterizedType pV = baseV as ParameterizedType; |
---|
746 | if (pV != null && object.Equals(pU.GetDefinition(), pV.GetDefinition()) && pU.TypeParameterCount == pV.TypeParameterCount) { |
---|
747 | if (uniqueBaseType == null) |
---|
748 | uniqueBaseType = pV; |
---|
749 | else |
---|
750 | return; // cannot make an inference because it's not unique |
---|
751 | } |
---|
752 | } |
---|
753 | Log.Indent(); |
---|
754 | if (uniqueBaseType != null) { |
---|
755 | for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) { |
---|
756 | IType Ui = pU.GetTypeArgument(i); |
---|
757 | IType Vi = uniqueBaseType.GetTypeArgument(i); |
---|
758 | if (Ui.IsReferenceType == true) { |
---|
759 | // look for variance |
---|
760 | ITypeParameter Xi = pU.GetDefinition().TypeParameters[i]; |
---|
761 | switch (Xi.Variance) { |
---|
762 | case VarianceModifier.Covariant: |
---|
763 | MakeUpperBoundInference(Ui, Vi); |
---|
764 | break; |
---|
765 | case VarianceModifier.Contravariant: |
---|
766 | MakeLowerBoundInference(Ui, Vi); |
---|
767 | break; |
---|
768 | default: // invariant |
---|
769 | MakeExactInference(Ui, Vi); |
---|
770 | break; |
---|
771 | } |
---|
772 | } else { |
---|
773 | // not known to be a reference type |
---|
774 | MakeExactInference(Ui, Vi); |
---|
775 | } |
---|
776 | } |
---|
777 | } |
---|
778 | Log.Unindent(); |
---|
779 | } |
---|
780 | } |
---|
781 | #endregion |
---|
782 | |
---|
783 | #region Fixing (§7.5.2.11) |
---|
784 | bool Fix(TP tp) |
---|
785 | { |
---|
786 | Log.WriteLine(" Trying to fix " + tp); |
---|
787 | Debug.Assert(!tp.IsFixed); |
---|
788 | if (tp.ExactBound != null) { |
---|
789 | // the exact bound will always be the result |
---|
790 | tp.FixedTo = tp.ExactBound; |
---|
791 | // check validity |
---|
792 | if (tp.MultipleDifferentExactBounds) |
---|
793 | return false; |
---|
794 | return tp.LowerBounds.All(b => conversions.ImplicitConversion(b, tp.FixedTo).IsValid) |
---|
795 | && tp.UpperBounds.All(b => conversions.ImplicitConversion(tp.FixedTo, b).IsValid); |
---|
796 | } |
---|
797 | Log.Indent(); |
---|
798 | var types = CreateNestedInstance().FindTypesInBounds(tp.LowerBounds.ToArray(), tp.UpperBounds.ToArray()); |
---|
799 | Log.Unindent(); |
---|
800 | if (algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults) { |
---|
801 | tp.FixedTo = IntersectionType.Create(types); |
---|
802 | Log.WriteLine(" T was fixed " + (types.Count >= 1 ? "successfully" : "(with errors)") + " to " + tp.FixedTo); |
---|
803 | return types.Count >= 1; |
---|
804 | } else { |
---|
805 | tp.FixedTo = GetFirstTypePreferNonInterfaces(types); |
---|
806 | Log.WriteLine(" T was fixed " + (types.Count == 1 ? "successfully" : "(with errors)") + " to " + tp.FixedTo); |
---|
807 | return types.Count == 1; |
---|
808 | } |
---|
809 | } |
---|
810 | #endregion |
---|
811 | |
---|
812 | #region Finding the best common type of a set of expresssions |
---|
813 | /// <summary> |
---|
814 | /// Gets the best common type (C# 4.0 spec: §7.5.2.14) of a set of expressions. |
---|
815 | /// </summary> |
---|
816 | public IType GetBestCommonType(IList<ResolveResult> expressions, out bool success) |
---|
817 | { |
---|
818 | if (expressions == null) |
---|
819 | throw new ArgumentNullException("expressions"); |
---|
820 | if (expressions.Count == 1) { |
---|
821 | success = (expressions[0].Type.Kind != TypeKind.Unknown); |
---|
822 | return expressions[0].Type; |
---|
823 | } |
---|
824 | Log.WriteCollection("GetBestCommonType() for ", expressions); |
---|
825 | try { |
---|
826 | ITypeParameter tp = DummyTypeParameter.GetMethodTypeParameter(0); |
---|
827 | this.typeParameters = new TP[1] { new TP(tp) }; |
---|
828 | foreach (ResolveResult r in expressions) { |
---|
829 | MakeOutputTypeInference(r, tp); |
---|
830 | } |
---|
831 | success = Fix(typeParameters[0]); |
---|
832 | return typeParameters[0].FixedTo ?? SpecialType.UnknownType; |
---|
833 | } finally { |
---|
834 | Reset(); |
---|
835 | } |
---|
836 | } |
---|
837 | #endregion |
---|
838 | |
---|
839 | #region FindTypeInBounds |
---|
840 | /// <summary> |
---|
841 | /// Finds a type that satisfies the given lower and upper bounds. |
---|
842 | /// </summary> |
---|
843 | public IType FindTypeInBounds(IList<IType> lowerBounds, IList<IType> upperBounds) |
---|
844 | { |
---|
845 | if (lowerBounds == null) |
---|
846 | throw new ArgumentNullException("lowerBounds"); |
---|
847 | if (upperBounds == null) |
---|
848 | throw new ArgumentNullException("upperBounds"); |
---|
849 | |
---|
850 | IList<IType> result = FindTypesInBounds(lowerBounds, upperBounds); |
---|
851 | |
---|
852 | if (algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults) { |
---|
853 | return IntersectionType.Create(result); |
---|
854 | } else { |
---|
855 | // return any of the candidates (prefer non-interfaces) |
---|
856 | return GetFirstTypePreferNonInterfaces(result); |
---|
857 | } |
---|
858 | } |
---|
859 | |
---|
860 | static IType GetFirstTypePreferNonInterfaces(IList<IType> result) |
---|
861 | { |
---|
862 | return result.FirstOrDefault(c => c.Kind != TypeKind.Interface) |
---|
863 | ?? result.FirstOrDefault() ?? SpecialType.UnknownType; |
---|
864 | } |
---|
865 | |
---|
866 | IList<IType> FindTypesInBounds(IList<IType> lowerBounds, IList<IType> upperBounds) |
---|
867 | { |
---|
868 | // If there's only a single type; return that single type. |
---|
869 | // If both inputs are empty, return the empty list. |
---|
870 | if (lowerBounds.Count == 0 && upperBounds.Count <= 1) |
---|
871 | return upperBounds; |
---|
872 | if (upperBounds.Count == 0 && lowerBounds.Count <= 1) |
---|
873 | return lowerBounds; |
---|
874 | if (nestingLevel > maxNestingLevel) |
---|
875 | return EmptyList<IType>.Instance; |
---|
876 | |
---|
877 | // Finds a type X so that "LB <: X <: UB" |
---|
878 | Log.WriteCollection("FindTypesInBound, LowerBounds=", lowerBounds); |
---|
879 | Log.WriteCollection("FindTypesInBound, UpperBounds=", upperBounds); |
---|
880 | |
---|
881 | // First try the Fixing algorithm from the C# spec (§7.5.2.11) |
---|
882 | List<IType> candidateTypes = lowerBounds.Union(upperBounds) |
---|
883 | .Where(c => lowerBounds.All(b => conversions.ImplicitConversion(b, c).IsValid)) |
---|
884 | .Where(c => upperBounds.All(b => conversions.ImplicitConversion(c, b).IsValid)) |
---|
885 | .ToList(); // evaluate the query only once |
---|
886 | |
---|
887 | Log.WriteCollection("FindTypesInBound, Candidates=", candidateTypes); |
---|
888 | |
---|
889 | // According to the C# specification, we need to pick the most specific |
---|
890 | // of the candidate types. (the type which has conversions to all others) |
---|
891 | // However, csc actually seems to choose the least specific. |
---|
892 | candidateTypes = candidateTypes.Where( |
---|
893 | c => candidateTypes.All(o => conversions.ImplicitConversion(o, c).IsValid) |
---|
894 | ).ToList(); |
---|
895 | |
---|
896 | // If the specified algorithm produces a single candidate, we return |
---|
897 | // that candidate. |
---|
898 | // We also return the whole candidate list if we're not using the improved |
---|
899 | // algorithm. |
---|
900 | if (candidateTypes.Count == 1 || !(algorithm == TypeInferenceAlgorithm.Improved || algorithm == TypeInferenceAlgorithm.ImprovedReturnAllResults)) |
---|
901 | { |
---|
902 | return candidateTypes; |
---|
903 | } |
---|
904 | candidateTypes.Clear(); |
---|
905 | |
---|
906 | // Now try the improved algorithm |
---|
907 | Log.Indent(); |
---|
908 | List<ITypeDefinition> candidateTypeDefinitions; |
---|
909 | if (lowerBounds.Count > 0) { |
---|
910 | // Find candidates by using the lower bounds: |
---|
911 | var hashSet = new HashSet<ITypeDefinition>(lowerBounds[0].GetAllBaseTypeDefinitions()); |
---|
912 | for (int i = 1; i < lowerBounds.Count; i++) { |
---|
913 | hashSet.IntersectWith(lowerBounds[i].GetAllBaseTypeDefinitions()); |
---|
914 | } |
---|
915 | candidateTypeDefinitions = hashSet.ToList(); |
---|
916 | } else { |
---|
917 | // Find candidates by looking at all classes in the project: |
---|
918 | candidateTypeDefinitions = compilation.GetAllTypeDefinitions().ToList(); |
---|
919 | } |
---|
920 | |
---|
921 | // Now filter out candidates that violate the upper bounds: |
---|
922 | foreach (IType ub in upperBounds) { |
---|
923 | ITypeDefinition ubDef = ub.GetDefinition(); |
---|
924 | if (ubDef != null) { |
---|
925 | candidateTypeDefinitions.RemoveAll(c => !c.IsDerivedFrom(ubDef)); |
---|
926 | } |
---|
927 | } |
---|
928 | |
---|
929 | foreach (ITypeDefinition candidateDef in candidateTypeDefinitions) { |
---|
930 | // determine the type parameters for the candidate: |
---|
931 | IType candidate; |
---|
932 | if (candidateDef.TypeParameterCount == 0) { |
---|
933 | candidate = candidateDef; |
---|
934 | } else { |
---|
935 | Log.WriteLine("Inferring arguments for candidate type definition: " + candidateDef); |
---|
936 | bool success; |
---|
937 | IType[] result = InferTypeArgumentsFromBounds( |
---|
938 | candidateDef.TypeParameters, |
---|
939 | new ParameterizedType(candidateDef, candidateDef.TypeParameters), |
---|
940 | lowerBounds, upperBounds, |
---|
941 | out success); |
---|
942 | if (success) { |
---|
943 | candidate = new ParameterizedType(candidateDef, result); |
---|
944 | } else { |
---|
945 | Log.WriteLine("Inference failed; ignoring candidate"); |
---|
946 | continue; |
---|
947 | } |
---|
948 | } |
---|
949 | Log.WriteLine("Candidate type: " + candidate); |
---|
950 | |
---|
951 | if (upperBounds.Count == 0) { |
---|
952 | // if there were only lower bounds, we aim for the most specific candidate: |
---|
953 | |
---|
954 | // if this candidate isn't made redundant by an existing, more specific candidate: |
---|
955 | if (!candidateTypes.Any(c => c.GetDefinition().IsDerivedFrom(candidateDef))) { |
---|
956 | // remove all existing candidates made redundant by this candidate: |
---|
957 | candidateTypes.RemoveAll(c => candidateDef.IsDerivedFrom(c.GetDefinition())); |
---|
958 | // add new candidate |
---|
959 | candidateTypes.Add(candidate); |
---|
960 | } |
---|
961 | } else { |
---|
962 | // if there were upper bounds, we aim for the least specific candidate: |
---|
963 | |
---|
964 | // if this candidate isn't made redundant by an existing, less specific candidate: |
---|
965 | if (!candidateTypes.Any(c => candidateDef.IsDerivedFrom(c.GetDefinition()))) { |
---|
966 | // remove all existing candidates made redundant by this candidate: |
---|
967 | candidateTypes.RemoveAll(c => c.GetDefinition().IsDerivedFrom(candidateDef)); |
---|
968 | // add new candidate |
---|
969 | candidateTypes.Add(candidate); |
---|
970 | } |
---|
971 | } |
---|
972 | } |
---|
973 | Log.Unindent(); |
---|
974 | return candidateTypes; |
---|
975 | } |
---|
976 | #endregion |
---|
977 | } |
---|
978 | } |
---|