Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.11/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Resolver/TypeInference.cs @ 15261

Last change on this file since 15261 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 34.4 KB
Line 
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
19using System;
20using System.Collections.Generic;
21using System.Diagnostics;
22using System.Linq;
23
24using ICSharpCode.NRefactory.Semantics;
25using ICSharpCode.NRefactory.TypeSystem;
26using ICSharpCode.NRefactory.TypeSystem.Implementation;
27
28namespace 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 non­empty 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}
Note: See TracBrowser for help on using the repository browser.