Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Resolver/CSharpResolver.cs

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

#2077: created branch and added first version

File size: 104.3 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;
23using ICSharpCode.NRefactory.CSharp.TypeSystem;
24using ICSharpCode.NRefactory.Semantics;
25using ICSharpCode.NRefactory.TypeSystem;
26using ICSharpCode.NRefactory.TypeSystem.Implementation;
27using ICSharpCode.NRefactory.Utils;
28
29namespace ICSharpCode.NRefactory.CSharp.Resolver
30{
31  /// <summary>
32  /// Contains the main resolver logic.
33  /// </summary>
34  /// <remarks>
35  /// This class is thread-safe.
36  /// </remarks>
37  public class CSharpResolver : ICodeContext
38  {
39    static readonly ResolveResult ErrorResult = ErrorResolveResult.UnknownError;
40    readonly ICompilation compilation;
41    internal readonly CSharpConversions conversions;
42    readonly CSharpTypeResolveContext context;
43    readonly bool checkForOverflow;
44    readonly bool isWithinLambdaExpression;
45   
46    #region Constructor
47    public CSharpResolver(ICompilation compilation)
48    {
49      if (compilation == null)
50        throw new ArgumentNullException("compilation");
51      this.compilation = compilation;
52      this.conversions = CSharpConversions.Get(compilation);
53      this.context = new CSharpTypeResolveContext(compilation.MainAssembly);
54     
55      var pc = compilation.MainAssembly.UnresolvedAssembly as CSharpProjectContent;
56      if (pc != null) {
57        this.checkForOverflow = pc.CompilerSettings.CheckForOverflow;
58      }
59    }
60   
61    public CSharpResolver(CSharpTypeResolveContext context)
62    {
63      if (context == null)
64        throw new ArgumentNullException("context");
65      this.compilation = context.Compilation;
66      this.conversions = CSharpConversions.Get(compilation);
67      this.context = context;
68      if (context.CurrentTypeDefinition != null)
69        currentTypeDefinitionCache = new TypeDefinitionCache(context.CurrentTypeDefinition);
70    }
71   
72    private CSharpResolver(ICompilation compilation, CSharpConversions conversions, CSharpTypeResolveContext context, bool checkForOverflow, bool isWithinLambdaExpression, TypeDefinitionCache currentTypeDefinitionCache, ImmutableStack<IVariable> localVariableStack, ObjectInitializerContext objectInitializerStack)
73    {
74      this.compilation = compilation;
75      this.conversions = conversions;
76      this.context = context;
77      this.checkForOverflow = checkForOverflow;
78      this.isWithinLambdaExpression = isWithinLambdaExpression;
79      this.currentTypeDefinitionCache = currentTypeDefinitionCache;
80      this.localVariableStack = localVariableStack;
81      this.objectInitializerStack = objectInitializerStack;
82    }
83    #endregion
84   
85    #region Properties
86    /// <summary>
87    /// Gets the compilation used by the resolver.
88    /// </summary>
89    public ICompilation Compilation {
90      get { return compilation; }
91    }
92   
93    /// <summary>
94    /// Gets the current type resolve context.
95    /// </summary>
96    public CSharpTypeResolveContext CurrentTypeResolveContext {
97      get { return context; }
98    }
99
100    IAssembly ITypeResolveContext.CurrentAssembly {
101      get { return context.CurrentAssembly; }
102    }
103   
104    CSharpResolver WithContext(CSharpTypeResolveContext newContext)
105    {
106      return new CSharpResolver(compilation, conversions, newContext, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
107    }
108   
109    /// <summary>
110    /// Gets whether the current context is <c>checked</c>.
111    /// </summary>
112    public bool CheckForOverflow {
113      get { return checkForOverflow; }
114    }
115   
116    /// <summary>
117    /// Sets whether the current context is <c>checked</c>.
118    /// </summary>
119    public CSharpResolver WithCheckForOverflow(bool checkForOverflow)
120    {
121      if (checkForOverflow == this.checkForOverflow)
122        return this;
123      return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
124    }
125   
126    /// <summary>
127    /// Gets whether the resolver is currently within a lambda expression or anonymous method.
128    /// </summary>
129    public bool IsWithinLambdaExpression {
130      get { return isWithinLambdaExpression; }
131    }
132   
133    /// <summary>
134    /// Sets whether the resolver is currently within a lambda expression.
135    /// </summary>
136    public CSharpResolver WithIsWithinLambdaExpression(bool isWithinLambdaExpression)
137    {
138      return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
139    }
140   
141    /// <summary>
142    /// Gets the current member definition that is used to look up identifiers as parameters
143    /// or type parameters.
144    /// </summary>
145    public IMember CurrentMember {
146      get { return context.CurrentMember; }
147    }
148   
149    /// <summary>
150    /// Sets the current member definition.
151    /// </summary>
152    /// <remarks>Don't forget to also set CurrentTypeDefinition when setting CurrentMember;
153    /// setting one of the properties does not automatically set the other.</remarks>
154    public CSharpResolver WithCurrentMember(IMember member)
155    {
156      return WithContext(context.WithCurrentMember(member));
157    }
158   
159    ITypeResolveContext ITypeResolveContext.WithCurrentMember(IMember member)
160    {
161      return WithCurrentMember(member);
162    }
163   
164    /// <summary>
165    /// Gets the current using scope that is used to look up identifiers as class names.
166    /// </summary>
167    public ResolvedUsingScope CurrentUsingScope {
168      get { return context.CurrentUsingScope; }
169    }
170   
171    /// <summary>
172    /// Sets the current using scope that is used to look up identifiers as class names.
173    /// </summary>
174    public CSharpResolver WithCurrentUsingScope(ResolvedUsingScope usingScope)
175    {
176      return WithContext(context.WithUsingScope(usingScope));
177    }
178    #endregion
179   
180    #region Per-CurrentTypeDefinition Cache
181    readonly TypeDefinitionCache currentTypeDefinitionCache;
182   
183    /// <summary>
184    /// Gets the current type definition.
185    /// </summary>
186    public ITypeDefinition CurrentTypeDefinition {
187      get { return context.CurrentTypeDefinition; }
188    }
189   
190    /// <summary>
191    /// Sets the current type definition.
192    /// </summary>
193    public CSharpResolver WithCurrentTypeDefinition(ITypeDefinition typeDefinition)
194    {
195      if (this.CurrentTypeDefinition == typeDefinition)
196        return this;
197     
198      TypeDefinitionCache newTypeDefinitionCache;
199      if (typeDefinition != null)
200        newTypeDefinitionCache = new TypeDefinitionCache(typeDefinition);
201      else
202        newTypeDefinitionCache = null;
203     
204      return new CSharpResolver(compilation, conversions, context.WithCurrentTypeDefinition(typeDefinition),
205                                checkForOverflow, isWithinLambdaExpression, newTypeDefinitionCache, localVariableStack, objectInitializerStack);
206    }
207   
208    ITypeResolveContext ITypeResolveContext.WithCurrentTypeDefinition(ITypeDefinition typeDefinition)
209    {
210      return WithCurrentTypeDefinition(typeDefinition);
211    }
212   
213    sealed class TypeDefinitionCache
214    {
215      public readonly ITypeDefinition TypeDefinition;
216      public readonly Dictionary<string, ResolveResult> SimpleNameLookupCacheExpression = new Dictionary<string, ResolveResult>();
217      public readonly Dictionary<string, ResolveResult> SimpleNameLookupCacheInvocationTarget = new Dictionary<string, ResolveResult>();
218      public readonly Dictionary<string, ResolveResult> SimpleTypeLookupCache = new Dictionary<string, ResolveResult>();
219     
220      public TypeDefinitionCache(ITypeDefinition typeDefinition)
221      {
222        this.TypeDefinition = typeDefinition;
223      }
224    }
225    #endregion
226   
227    #region Local Variable Management
228   
229    // We store the local variables in an immutable stack.
230    // The beginning of a block is marked by a null entry.
231   
232    // This data structure is used to allow efficient cloning of the resolver with its local variable context.
233    readonly ImmutableStack<IVariable> localVariableStack = ImmutableStack<IVariable>.Empty;
234   
235    CSharpResolver WithLocalVariableStack(ImmutableStack<IVariable> stack)
236    {
237      return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, stack, objectInitializerStack);
238    }
239   
240    /// <summary>
241    /// Opens a new scope for local variables.
242    /// </summary>
243    public CSharpResolver PushBlock()
244    {
245      return WithLocalVariableStack(localVariableStack.Push(null));
246    }
247   
248    /// <summary>
249    /// Closes the current scope for local variables; removing all variables in that scope.
250    /// </summary>
251    public CSharpResolver PopBlock()
252    {
253      var stack = localVariableStack;
254      IVariable removedVar;
255      do {
256        removedVar = stack.Peek();
257        stack = stack.Pop();
258      } while (removedVar != null);
259      return WithLocalVariableStack(stack);
260    }
261   
262    /// <summary>
263    /// Adds a new variable or lambda parameter to the current block.
264    /// </summary>
265    public CSharpResolver AddVariable(IVariable variable)
266    {
267      if (variable == null)
268        throw new ArgumentNullException("variable");
269      return WithLocalVariableStack(localVariableStack.Push(variable));
270    }
271   
272    /// <summary>
273    /// Removes the variable that was just added.
274    /// </summary>
275    public CSharpResolver PopLastVariable()
276    {
277      if (localVariableStack.Peek() == null)
278        throw new InvalidOperationException("There is no variable within the current block.");
279      return WithLocalVariableStack(localVariableStack.Pop());
280    }
281   
282    /// <summary>
283    /// Gets all currently visible local variables and lambda parameters.
284    /// Does not include method parameters.
285    /// </summary>
286    public IEnumerable<IVariable> LocalVariables {
287      get {
288        return localVariableStack.Where(v => v != null);
289      }
290    }
291    #endregion
292   
293    #region Object Initializer Context
294    sealed class ObjectInitializerContext
295    {
296      internal readonly ResolveResult initializedObject;
297      internal readonly ObjectInitializerContext prev;
298     
299      public ObjectInitializerContext(ResolveResult initializedObject, CSharpResolver.ObjectInitializerContext prev)
300      {
301        this.initializedObject = initializedObject;
302        this.prev = prev;
303      }
304    }
305   
306    readonly ObjectInitializerContext objectInitializerStack;
307   
308    CSharpResolver WithObjectInitializerStack(ObjectInitializerContext stack)
309    {
310      return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, stack);
311    }
312   
313    /// <summary>
314    /// Pushes the type of the object that is currently being initialized.
315    /// </summary>
316    public CSharpResolver PushObjectInitializer(ResolveResult initializedObject)
317    {
318      if (initializedObject == null)
319        throw new ArgumentNullException("initializedObject");
320      return WithObjectInitializerStack(new ObjectInitializerContext(initializedObject, objectInitializerStack));
321    }
322   
323    public CSharpResolver PopObjectInitializer()
324    {
325      if (objectInitializerStack == null)
326        throw new InvalidOperationException();
327      return WithObjectInitializerStack(objectInitializerStack.prev);
328    }
329   
330    /// <summary>
331    /// Gets whether this context is within an object initializer.
332    /// </summary>
333    public bool IsInObjectInitializer {
334      get { return objectInitializerStack != null; }
335    }
336   
337    /// <summary>
338    /// Gets the current object initializer. This usually is an <see cref="InitializedObjectResolveResult"/>
339    /// or (for nested initializers) a semantic tree based on an <see cref="InitializedObjectResolveResult"/>.
340    /// Returns ErrorResolveResult if there is no object initializer.
341    /// </summary>
342    public ResolveResult CurrentObjectInitializer {
343      get {
344        return objectInitializerStack != null ? objectInitializerStack.initializedObject : ErrorResult;
345      }
346    }
347   
348    /// <summary>
349    /// Gets the type of the object currently being initialized.
350    /// Returns SharedTypes.Unknown if no object initializer is currently open (or if the object initializer
351    /// has unknown type).
352    /// </summary>
353    public IType CurrentObjectInitializerType {
354      get { return CurrentObjectInitializer.Type; }
355    }
356    #endregion
357   
358    #region Clone
359    /// <summary>
360    /// Creates a copy of this CSharp resolver.
361    /// </summary>
362    [Obsolete("CSharpResolver is immutable, cloning is no longer necessary")]
363    public CSharpResolver Clone()
364    {
365      return this;
366    }
367    #endregion
368   
369    #region ResolveUnaryOperator
370    #region ResolveUnaryOperator method
371    public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression)
372    {
373      if (expression.Type.Kind == TypeKind.Dynamic) {
374        if (op == UnaryOperatorType.Await) {
375          return new AwaitResolveResult(SpecialType.Dynamic, new DynamicInvocationResolveResult(new DynamicMemberResolveResult(expression, "GetAwaiter"), DynamicInvocationType.Invocation, EmptyList<ResolveResult>.Instance), SpecialType.Dynamic, null, null, null);
376        }
377        else {
378          return UnaryOperatorResolveResult(SpecialType.Dynamic, op, expression);
379        }
380      }
381     
382      // C# 4.0 spec: §7.3.3 Unary operator overload resolution
383      string overloadableOperatorName = GetOverloadableOperatorName(op);
384      if (overloadableOperatorName == null) {
385        switch (op) {
386          case UnaryOperatorType.Dereference:
387            PointerType p = expression.Type as PointerType;
388            if (p != null)
389              return UnaryOperatorResolveResult(p.ElementType, op, expression);
390            else
391              return ErrorResult;
392          case UnaryOperatorType.AddressOf:
393            return UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression);
394          case UnaryOperatorType.Await: {
395            ResolveResult getAwaiterMethodGroup = ResolveMemberAccess(expression, "GetAwaiter", EmptyList<IType>.Instance, NameLookupMode.InvocationTarget);
396            ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, new ResolveResult[0], argumentNames: null, allowOptionalParameters: false);
397
398            var lookup = CreateMemberLookup();
399            IMethod getResultMethod;
400            IType awaitResultType;
401            var getResultMethodGroup = lookup.Lookup(getAwaiterInvocation, "GetResult", EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
402            if (getResultMethodGroup != null) {
403              var getResultOR = getResultMethodGroup.PerformOverloadResolution(compilation, new ResolveResult[0], allowExtensionMethods: false, conversions: conversions);
404              getResultMethod = getResultOR.FoundApplicableCandidate ? getResultOR.GetBestCandidateWithSubstitutedTypeArguments() as IMethod : null;
405              awaitResultType = getResultMethod != null ? getResultMethod.ReturnType : SpecialType.UnknownType;
406            }
407            else {
408              getResultMethod = null;
409              awaitResultType = SpecialType.UnknownType;
410            }
411
412            var isCompletedRR = lookup.Lookup(getAwaiterInvocation, "IsCompleted", EmptyList<IType>.Instance, false);
413            var isCompletedProperty = (isCompletedRR is MemberResolveResult ? ((MemberResolveResult)isCompletedRR).Member as IProperty : null);
414            if (isCompletedProperty != null && (!isCompletedProperty.ReturnType.IsKnownType(KnownTypeCode.Boolean) || !isCompletedProperty.CanGet))
415              isCompletedProperty = null;
416
417            var interfaceOnCompleted = compilation.FindType(KnownTypeCode.INotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "OnCompleted");
418            var interfaceUnsafeOnCompleted = compilation.FindType(KnownTypeCode.ICriticalNotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "UnsafeOnCompleted");
419
420            IMethod onCompletedMethod = null;
421            var candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceUnsafeOnCompleted)).ToList();
422            if (candidates.Count == 0) {
423              candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceOnCompleted)).ToList();
424              if (candidates.Count == 1)
425                onCompletedMethod = candidates[0];
426            }
427            else if (candidates.Count == 1) {
428              onCompletedMethod = candidates[0];
429            }
430
431            return new AwaitResolveResult(awaitResultType, getAwaiterInvocation, getAwaiterInvocation.Type, isCompletedProperty, onCompletedMethod, getResultMethod);
432          }
433
434          default:
435            return ErrorResolveResult.UnknownError;
436        }
437      }
438      // If the type is nullable, get the underlying type:
439      IType type = NullableType.GetUnderlyingType(expression.Type);
440      bool isNullable = NullableType.IsNullable(expression.Type);
441     
442      // the operator is overloadable:
443      OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { expression });
444      foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) {
445        userDefinedOperatorOR.AddCandidate(candidate);
446      }
447      if (userDefinedOperatorOR.FoundApplicableCandidate) {
448        return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
449      }
450     
451      expression = UnaryNumericPromotion(op, ref type, isNullable, expression);
452      CSharpOperators.OperatorMethod[] methodGroup;
453      CSharpOperators operators = CSharpOperators.Get(compilation);
454      switch (op) {
455        case UnaryOperatorType.Increment:
456        case UnaryOperatorType.Decrement:
457        case UnaryOperatorType.PostIncrement:
458        case UnaryOperatorType.PostDecrement:
459          // C# 4.0 spec: §7.6.9 Postfix increment and decrement operators
460          // C# 4.0 spec: §7.7.5 Prefix increment and decrement operators
461          TypeCode code = ReflectionHelper.GetTypeCode(type);
462          if ((code >= TypeCode.Char && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer)
463            return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
464          else
465            return new ErrorResolveResult(expression.Type);
466        case UnaryOperatorType.Plus:
467          methodGroup = operators.UnaryPlusOperators;
468          break;
469        case UnaryOperatorType.Minus:
470          methodGroup = CheckForOverflow ? operators.CheckedUnaryMinusOperators : operators.UncheckedUnaryMinusOperators;
471          break;
472        case UnaryOperatorType.Not:
473          methodGroup = operators.LogicalNegationOperators;
474          break;
475        case UnaryOperatorType.BitNot:
476          if (type.Kind == TypeKind.Enum) {
477            if (expression.IsCompileTimeConstant && !isNullable && expression.ConstantValue != null) {
478              // evaluate as (E)(~(U)x);
479              var U = compilation.FindType(expression.ConstantValue.GetType());
480              var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue);
481              var rr = ResolveUnaryOperator(op, unpackedEnum);
482              rr = WithCheckForOverflow(false).ResolveCast(type, rr);
483              if (rr.IsCompileTimeConstant)
484                return rr;
485            }
486            return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
487          } else {
488            methodGroup = operators.BitwiseComplementOperators;
489            break;
490          }
491        default:
492          throw new InvalidOperationException();
493      }
494      OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { expression });
495      foreach (var candidate in methodGroup) {
496        builtinOperatorOR.AddCandidate(candidate);
497      }
498      CSharpOperators.UnaryOperatorMethod m = (CSharpOperators.UnaryOperatorMethod)builtinOperatorOR.BestCandidate;
499      IType resultType = m.ReturnType;
500      if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
501        if (userDefinedOperatorOR.BestCandidate != null) {
502          // If there are any user-defined operators, prefer those over the built-in operators.
503          // It'll be a more informative error.
504          return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
505        } else if (builtinOperatorOR.BestCandidateAmbiguousWith != null) {
506          // If the best candidate is ambiguous, just use the input type instead
507          // of picking one of the ambiguous overloads.
508          return new ErrorResolveResult(expression.Type);
509        } else {
510          return new ErrorResolveResult(resultType);
511        }
512      } else if (expression.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
513        object val;
514        try {
515          val = m.Invoke(this, expression.ConstantValue);
516        } catch (ArithmeticException) {
517          return new ErrorResolveResult(resultType);
518        }
519        return new ConstantResolveResult(resultType, val);
520      } else {
521        expression = Convert(expression, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
522        return UnaryOperatorResolveResult(resultType, op, expression,
523                                          builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
524      }
525    }
526   
527    OperatorResolveResult UnaryOperatorResolveResult(IType resultType, UnaryOperatorType op, ResolveResult expression, bool isLifted = false)
528    {
529      return new OperatorResolveResult(
530        resultType, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow),
531        null, isLifted, new[] { expression });
532    }
533    #endregion
534   
535    #region UnaryNumericPromotion
536    ResolveResult UnaryNumericPromotion(UnaryOperatorType op, ref IType type, bool isNullable, ResolveResult expression)
537    {
538      // C# 4.0 spec: §7.3.6.1
539      TypeCode code = ReflectionHelper.GetTypeCode(type);
540      if (isNullable && type.Kind == TypeKind.Null)
541        code = TypeCode.SByte; // cause promotion of null to int32
542      switch (op) {
543        case UnaryOperatorType.Minus:
544          if (code == TypeCode.UInt32) {
545            type = compilation.FindType(KnownTypeCode.Int64);
546            return Convert(expression, MakeNullable(type, isNullable),
547                           isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
548          }
549          goto case UnaryOperatorType.Plus;
550        case UnaryOperatorType.Plus:
551        case UnaryOperatorType.BitNot:
552          if (code >= TypeCode.Char && code <= TypeCode.UInt16) {
553            type = compilation.FindType(KnownTypeCode.Int32);
554            return Convert(expression, MakeNullable(type, isNullable),
555                           isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
556          }
557          break;
558      }
559      return expression;
560    }
561    #endregion
562   
563    #region GetOverloadableOperatorName
564    static string GetOverloadableOperatorName(UnaryOperatorType op)
565    {
566      switch (op) {
567        case UnaryOperatorType.Not:
568          return "op_LogicalNot";
569        case UnaryOperatorType.BitNot:
570          return "op_OnesComplement";
571        case UnaryOperatorType.Minus:
572          return "op_UnaryNegation";
573        case UnaryOperatorType.Plus:
574          return "op_UnaryPlus";
575        case UnaryOperatorType.Increment:
576        case UnaryOperatorType.PostIncrement:
577          return "op_Increment";
578        case UnaryOperatorType.Decrement:
579        case UnaryOperatorType.PostDecrement:
580          return "op_Decrement";
581        default:
582          return null;
583      }
584    }
585    #endregion
586    #endregion
587   
588    #region ResolveBinaryOperator
589    #region ResolveBinaryOperator method
590    public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
591    {
592      if (lhs.Type.Kind == TypeKind.Dynamic || rhs.Type.Kind == TypeKind.Dynamic) {
593        lhs = Convert(lhs, SpecialType.Dynamic);
594        rhs = Convert(rhs, SpecialType.Dynamic);
595        return BinaryOperatorResolveResult(SpecialType.Dynamic, lhs, op, rhs);
596      }
597     
598      // C# 4.0 spec: §7.3.4 Binary operator overload resolution
599      string overloadableOperatorName = GetOverloadableOperatorName(op);
600      if (overloadableOperatorName == null) {
601       
602        // Handle logical and/or exactly as bitwise and/or:
603        // - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
604        // - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
605        // - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
606        if (op == BinaryOperatorType.ConditionalAnd) {
607          overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd);
608        } else if (op == BinaryOperatorType.ConditionalOr) {
609          overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr);
610        } else if (op == BinaryOperatorType.NullCoalescing) {
611          // null coalescing operator is not overloadable and needs to be handled separately
612          return ResolveNullCoalescingOperator(lhs, rhs);
613        } else {
614          return ErrorResolveResult.UnknownError;
615        }
616      }
617     
618      // If the type is nullable, get the underlying type:
619      bool isNullable = NullableType.IsNullable(lhs.Type) || NullableType.IsNullable(rhs.Type);
620      IType lhsType = NullableType.GetUnderlyingType(lhs.Type);
621      IType rhsType = NullableType.GetUnderlyingType(rhs.Type);
622     
623      // the operator is overloadable:
624      OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { lhs, rhs });
625      HashSet<IParameterizedMember> userOperatorCandidates = new HashSet<IParameterizedMember>();
626      userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName));
627      userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName));
628      foreach (var candidate in userOperatorCandidates) {
629        userDefinedOperatorOR.AddCandidate(candidate);
630      }
631      if (userDefinedOperatorOR.FoundApplicableCandidate) {
632        return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
633      }
634     
635      if (lhsType.Kind == TypeKind.Null && rhsType.IsReferenceType == false
636          || lhsType.IsReferenceType == false && rhsType.Kind == TypeKind.Null)
637      {
638        isNullable = true;
639      }
640      if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) {
641        // special case: the shift operators allow "var x = null << null", producing int?.
642        if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
643          isNullable = true;
644        // for shift operators, do unary promotion independently on both arguments
645        lhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref lhsType, isNullable, lhs);
646        rhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref rhsType, isNullable, rhs);
647      } else {
648        bool allowNullableConstants = op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality;
649        if (!BinaryNumericPromotion(isNullable, ref lhs, ref rhs, allowNullableConstants))
650          return new ErrorResolveResult(lhs.Type);
651      }
652      // re-read underlying types after numeric promotion
653      lhsType = NullableType.GetUnderlyingType(lhs.Type);
654      rhsType = NullableType.GetUnderlyingType(rhs.Type);
655     
656      IEnumerable<CSharpOperators.OperatorMethod> methodGroup;
657      CSharpOperators operators = CSharpOperators.Get(compilation);
658      switch (op) {
659        case BinaryOperatorType.Multiply:
660          methodGroup = operators.MultiplicationOperators;
661          break;
662        case BinaryOperatorType.Divide:
663          methodGroup = operators.DivisionOperators;
664          break;
665        case BinaryOperatorType.Modulus:
666          methodGroup = operators.RemainderOperators;
667          break;
668        case BinaryOperatorType.Add:
669          methodGroup = operators.AdditionOperators;
670          {
671            if (lhsType.Kind == TypeKind.Enum) {
672              // E operator +(E x, U y);
673              IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
674              if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) {
675                return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
676              }
677            }
678            if (rhsType.Kind == TypeKind.Enum) {
679              // E operator +(U x, E y);
680              IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable);
681              if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) {
682                return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
683              }
684            }
685           
686            if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
687              return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
688            } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
689              return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
690            }
691           
692            if (lhsType is PointerType) {
693              methodGroup = new [] {
694                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
695                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
696                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
697                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
698              };
699            } else if (rhsType is PointerType) {
700              methodGroup = new [] {
701                PointerArithmeticOperator(rhsType, KnownTypeCode.Int32, rhsType),
702                PointerArithmeticOperator(rhsType, KnownTypeCode.UInt32, rhsType),
703                PointerArithmeticOperator(rhsType, KnownTypeCode.Int64, rhsType),
704                PointerArithmeticOperator(rhsType, KnownTypeCode.UInt64, rhsType)
705              };
706            }
707            if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
708              return new ErrorResolveResult(SpecialType.NullType);
709          }
710          break;
711        case BinaryOperatorType.Subtract:
712          methodGroup = operators.SubtractionOperators;
713          {
714            if (lhsType.Kind == TypeKind.Enum) {
715              // U operator –(E x, E y);
716              if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs, allowConversionFromConstantZero: false)) {
717                return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs);
718              }
719
720              // E operator –(E x, U y);
721              IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
722              if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) {
723                return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
724              }
725            }
726            if (rhsType.Kind == TypeKind.Enum) {
727              // U operator –(E x, E y);
728              if (TryConvertEnum(ref lhs, rhs.Type, ref isNullable, ref rhs, allowConversionFromConstantZero: false)) {
729                return HandleEnumSubtraction(isNullable, rhsType, lhs, rhs);
730              }
731
732              // E operator -(U x, E y);
733              IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable);
734              if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) {
735                return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
736              }
737            }
738           
739            if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
740              return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
741            } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
742              return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
743            }
744           
745            if (lhsType is PointerType) {
746              if (rhsType is PointerType) {
747                IType int64 = compilation.FindType(KnownTypeCode.Int64);
748                if (lhsType.Equals(rhsType)) {
749                  return BinaryOperatorResolveResult(int64, lhs, op, rhs);
750                } else {
751                  return new ErrorResolveResult(int64);
752                }
753              }
754              methodGroup = new [] {
755                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
756                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
757                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
758                PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
759              };
760            }
761           
762            if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
763              return new ErrorResolveResult(SpecialType.NullType);
764          }
765          break;
766        case BinaryOperatorType.ShiftLeft:
767          methodGroup = operators.ShiftLeftOperators;
768          break;
769        case BinaryOperatorType.ShiftRight:
770          methodGroup = operators.ShiftRightOperators;
771          break;
772        case BinaryOperatorType.Equality:
773        case BinaryOperatorType.InEquality:
774        case BinaryOperatorType.LessThan:
775        case BinaryOperatorType.GreaterThan:
776        case BinaryOperatorType.LessThanOrEqual:
777        case BinaryOperatorType.GreaterThanOrEqual:
778          {
779            if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) {
780              // bool operator op(E x, E y);
781              return HandleEnumComparison(op, lhsType, isNullable, lhs, rhs);
782            } else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) {
783              // bool operator op(E x, E y);
784              return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs);
785            } else if (lhsType is PointerType && rhsType is PointerType) {
786              return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
787            }
788            if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality) {
789              if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true) {
790                // If it's a reference comparison
791                if (op == BinaryOperatorType.Equality)
792                  methodGroup = operators.ReferenceEqualityOperators;
793                else
794                  methodGroup = operators.ReferenceInequalityOperators;
795                break;
796              } else if (lhsType.Kind == TypeKind.Null && IsNullableTypeOrNonValueType(rhs.Type)
797                         || IsNullableTypeOrNonValueType(lhs.Type) && rhsType.Kind == TypeKind.Null) {
798                // compare type parameter or nullable type with the null literal
799                return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
800              }
801            }
802            switch (op) {
803              case BinaryOperatorType.Equality:
804                methodGroup = operators.ValueEqualityOperators;
805                break;
806              case BinaryOperatorType.InEquality:
807                methodGroup = operators.ValueInequalityOperators;
808                break;
809              case BinaryOperatorType.LessThan:
810                methodGroup = operators.LessThanOperators;
811                break;
812              case BinaryOperatorType.GreaterThan:
813                methodGroup = operators.GreaterThanOperators;
814                break;
815              case BinaryOperatorType.LessThanOrEqual:
816                methodGroup = operators.LessThanOrEqualOperators;
817                break;
818              case BinaryOperatorType.GreaterThanOrEqual:
819                methodGroup = operators.GreaterThanOrEqualOperators;
820                break;
821              default:
822                throw new InvalidOperationException();
823            }
824          }
825          break;
826        case BinaryOperatorType.BitwiseAnd:
827        case BinaryOperatorType.BitwiseOr:
828        case BinaryOperatorType.ExclusiveOr:
829          {
830            if (lhsType.Kind == TypeKind.Enum) {
831              // bool operator op(E x, E y);
832              if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs)) {
833                return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
834              }
835            }
836
837            if (rhsType.Kind == TypeKind.Enum) {
838              // bool operator op(E x, E y);
839              if (TryConvertEnum (ref lhs, rhs.Type, ref isNullable, ref rhs)) {
840                return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
841              }
842            }
843           
844            switch (op) {
845              case BinaryOperatorType.BitwiseAnd:
846                methodGroup = operators.BitwiseAndOperators;
847                break;
848              case BinaryOperatorType.BitwiseOr:
849                methodGroup = operators.BitwiseOrOperators;
850                break;
851              case BinaryOperatorType.ExclusiveOr:
852                methodGroup = operators.BitwiseXorOperators;
853                break;
854              default:
855                throw new InvalidOperationException();
856            }
857          }
858          break;
859        case BinaryOperatorType.ConditionalAnd:
860          methodGroup = operators.LogicalAndOperators;
861          break;
862        case BinaryOperatorType.ConditionalOr:
863          methodGroup = operators.LogicalOrOperators;
864          break;
865        default:
866          throw new InvalidOperationException();
867      }
868      OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { lhs, rhs });
869      foreach (var candidate in methodGroup) {
870        builtinOperatorOR.AddCandidate(candidate);
871      }
872      CSharpOperators.BinaryOperatorMethod m = (CSharpOperators.BinaryOperatorMethod)builtinOperatorOR.BestCandidate;
873      IType resultType = m.ReturnType;
874      if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
875        // If there are any user-defined operators, prefer those over the built-in operators.
876        // It'll be a more informative error.
877        if (userDefinedOperatorOR.BestCandidate != null)
878          return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
879        else
880          return new ErrorResolveResult(resultType);
881      } else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
882        object val;
883        try {
884          val = m.Invoke(this, lhs.ConstantValue, rhs.ConstantValue);
885        } catch (ArithmeticException) {
886          return new ErrorResolveResult(resultType);
887        }
888        return new ConstantResolveResult(resultType, val);
889      } else {
890        lhs = Convert(lhs, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
891        rhs = Convert(rhs, m.Parameters[1].Type, builtinOperatorOR.ArgumentConversions[1]);
892        return BinaryOperatorResolveResult(resultType, lhs, op, rhs,
893                                           builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
894      }
895    }
896   
897    bool IsNullableTypeOrNonValueType(IType type)
898    {
899      return NullableType.IsNullable(type) || type.IsReferenceType != false;
900    }
901   
902    ResolveResult BinaryOperatorResolveResult(IType resultType, ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs, bool isLifted = false)
903    {
904      return new OperatorResolveResult(
905        resultType, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow),
906        null, isLifted, new[] { lhs, rhs });
907    }
908    #endregion
909   
910    #region Pointer arithmetic
911    CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, KnownTypeCode inputType2)
912    {
913      return PointerArithmeticOperator(resultType, inputType1, compilation.FindType(inputType2));
914    }
915   
916    CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, KnownTypeCode inputType1, IType inputType2)
917    {
918      return PointerArithmeticOperator(resultType, compilation.FindType(inputType1), inputType2);
919    }
920   
921    CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, IType inputType2)
922    {
923      return new CSharpOperators.BinaryOperatorMethod(compilation) {
924        ReturnType = resultType,
925        Parameters = {
926          new DefaultParameter(inputType1, string.Empty),
927          new DefaultParameter(inputType2, string.Empty)
928        }
929      };
930    }
931    #endregion
932   
933    #region Enum helper methods
934    IType GetEnumUnderlyingType(IType enumType)
935    {
936      ITypeDefinition def = enumType.GetDefinition();
937      return def != null ? def.EnumUnderlyingType : SpecialType.UnknownType;
938    }
939   
940    /// <summary>
941    /// Handle the case where an enum value is compared with another enum value
942    /// bool operator op(E x, E y);
943    /// </summary>
944    ResolveResult HandleEnumComparison(BinaryOperatorType op, IType enumType, bool isNullable, ResolveResult lhs, ResolveResult rhs)
945    {
946      // evaluate as ((U)x op (U)y)
947      IType elementType = GetEnumUnderlyingType(enumType);
948      if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum) {
949        var rr = ResolveBinaryOperator(op, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs));
950        if (rr.IsCompileTimeConstant)
951          return rr;
952      }
953      IType resultType = compilation.FindType(KnownTypeCode.Boolean);
954      return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable);
955    }
956   
957    /// <summary>
958    /// Handle the case where an enum value is subtracted from another enum value
959    /// U operator –(E x, E y);
960    /// </summary>
961    ResolveResult HandleEnumSubtraction(bool isNullable, IType enumType, ResolveResult lhs, ResolveResult rhs)
962    {
963      // evaluate as (U)((U)x – (U)y)
964      IType elementType = GetEnumUnderlyingType(enumType);
965      if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum) {
966        var rr = ResolveBinaryOperator(BinaryOperatorType.Subtract, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs));
967        rr = WithCheckForOverflow(false).ResolveCast(elementType, rr);
968        if (rr.IsCompileTimeConstant)
969          return rr;
970      }
971      IType resultType = MakeNullable(elementType, isNullable);
972      return BinaryOperatorResolveResult(resultType, lhs, BinaryOperatorType.Subtract, rhs, isNullable);
973    }
974   
975    /// <summary>
976    /// Handle the following enum operators:
977    /// E operator +(E x, U y);
978    /// E operator +(U x, E y);
979    /// E operator –(E x, U y);
980    /// E operator &amp;(E x, E y);
981    /// E operator |(E x, E y);
982    /// E operator ^(E x, E y);
983    /// </summary>
984    ResolveResult HandleEnumOperator(bool isNullable, IType enumType, BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
985    {
986      // evaluate as (E)((U)x op (U)y)
987      if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) {
988        IType elementType = GetEnumUnderlyingType(enumType);
989        if (elementType.Kind != TypeKind.Enum) {
990          var rr = ResolveBinaryOperator(op, ResolveCast(elementType, lhs), ResolveCast(elementType, rhs));
991          rr = WithCheckForOverflow(false).ResolveCast(enumType, rr);
992          if (rr.IsCompileTimeConstant) // only report result if it's a constant; use the regular OperatorResolveResult codepath otherwise
993            return rr;
994        }
995      }
996      IType resultType = MakeNullable(enumType, isNullable);
997      return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable);
998    }
999   
1000    IType MakeNullable(IType type, bool isNullable)
1001    {
1002      if (isNullable)
1003        return NullableType.Create(compilation, type);
1004      else
1005        return type;
1006    }
1007    #endregion
1008   
1009    #region BinaryNumericPromotion
1010    bool BinaryNumericPromotion(bool isNullable, ref ResolveResult lhs, ref ResolveResult rhs, bool allowNullableConstants)
1011    {
1012      // C# 4.0 spec: §7.3.6.2
1013      TypeCode lhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(lhs.Type));
1014      TypeCode rhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rhs.Type));
1015      // if one of the inputs is the null literal, promote that to the type of the other operand
1016      if (isNullable && lhs.Type.Kind == TypeKind.Null && rhsCode >= TypeCode.Boolean && rhsCode <= TypeCode.Decimal) {
1017        lhs = CastTo(rhsCode, isNullable, lhs, allowNullableConstants);
1018        lhsCode = rhsCode;
1019      } else if (isNullable && rhs.Type.Kind == TypeKind.Null && lhsCode >= TypeCode.Boolean && lhsCode <= TypeCode.Decimal) {
1020        rhs = CastTo(lhsCode, isNullable, rhs, allowNullableConstants);
1021        rhsCode = lhsCode;
1022      }
1023      bool bindingError = false;
1024      if (lhsCode >= TypeCode.Char && lhsCode <= TypeCode.Decimal
1025          && rhsCode >= TypeCode.Char && rhsCode <= TypeCode.Decimal)
1026      {
1027        TypeCode targetType;
1028        if (lhsCode == TypeCode.Decimal || rhsCode == TypeCode.Decimal) {
1029          targetType = TypeCode.Decimal;
1030          bindingError = (lhsCode == TypeCode.Single || lhsCode == TypeCode.Double
1031                          || rhsCode == TypeCode.Single || rhsCode == TypeCode.Double);
1032        } else if (lhsCode == TypeCode.Double || rhsCode == TypeCode.Double) {
1033          targetType = TypeCode.Double;
1034        } else if (lhsCode == TypeCode.Single || rhsCode == TypeCode.Single) {
1035          targetType = TypeCode.Single;
1036        } else if (lhsCode == TypeCode.UInt64 || rhsCode == TypeCode.UInt64) {
1037          targetType = TypeCode.UInt64;
1038          bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs);
1039        } else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) {
1040          targetType = TypeCode.Int64;
1041        } else if (lhsCode == TypeCode.UInt32 || rhsCode == TypeCode.UInt32) {
1042          targetType = (IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs)) ? TypeCode.Int64 : TypeCode.UInt32;
1043        } else {
1044          targetType = TypeCode.Int32;
1045        }
1046        lhs = CastTo(targetType, isNullable, lhs, allowNullableConstants);
1047        rhs = CastTo(targetType, isNullable, rhs, allowNullableConstants);
1048      }
1049      return !bindingError;
1050    }
1051   
1052    bool IsSigned(TypeCode code, ResolveResult rr)
1053    {
1054      // Determine whether the rr with code==ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rr.Type))
1055      // is a signed primitive type.
1056      switch (code) {
1057        case TypeCode.SByte:
1058        case TypeCode.Int16:
1059          return true;
1060        case TypeCode.Int32:
1061          // for int, consider implicit constant expression conversion
1062          if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (int)rr.ConstantValue >= 0)
1063            return false;
1064          else
1065            return true;
1066        case TypeCode.Int64:
1067          // for long, consider implicit constant expression conversion
1068          if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (long)rr.ConstantValue >= 0)
1069            return false;
1070          else
1071            return true;
1072        default:
1073          return false;
1074      }
1075    }
1076   
1077    ResolveResult CastTo(TypeCode targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants)
1078    {
1079      IType elementType = compilation.FindType(targetType);
1080      IType nullableType = MakeNullable(elementType, isNullable);
1081      if (nullableType.Equals(expression.Type))
1082        return expression;
1083      if (allowNullableConstants && expression.IsCompileTimeConstant) {
1084        if (expression.ConstantValue == null)
1085          return new ConstantResolveResult(nullableType, null);
1086        ResolveResult rr = ResolveCast(elementType, expression);
1087        if (rr.IsError)
1088          return rr;
1089        Debug.Assert(rr.IsCompileTimeConstant);
1090        return new ConstantResolveResult(nullableType, rr.ConstantValue);
1091      } else {
1092        return Convert(expression, nullableType,
1093                       isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
1094      }
1095    }
1096    #endregion
1097   
1098    #region GetOverloadableOperatorName
1099    static string GetOverloadableOperatorName(BinaryOperatorType op)
1100    {
1101      switch (op) {
1102        case BinaryOperatorType.Add:
1103          return "op_Addition";
1104        case BinaryOperatorType.Subtract:
1105          return "op_Subtraction";
1106        case BinaryOperatorType.Multiply:
1107          return "op_Multiply";
1108        case BinaryOperatorType.Divide:
1109          return "op_Division";
1110        case BinaryOperatorType.Modulus:
1111          return "op_Modulus";
1112        case BinaryOperatorType.BitwiseAnd:
1113          return "op_BitwiseAnd";
1114        case BinaryOperatorType.BitwiseOr:
1115          return "op_BitwiseOr";
1116        case BinaryOperatorType.ExclusiveOr:
1117          return "op_ExclusiveOr";
1118        case BinaryOperatorType.ShiftLeft:
1119          return "op_LeftShift";
1120        case BinaryOperatorType.ShiftRight:
1121          return "op_RightShift";
1122        case BinaryOperatorType.Equality:
1123          return "op_Equality";
1124        case BinaryOperatorType.InEquality:
1125          return "op_Inequality";
1126        case BinaryOperatorType.GreaterThan:
1127          return "op_GreaterThan";
1128        case BinaryOperatorType.LessThan:
1129          return "op_LessThan";
1130        case BinaryOperatorType.GreaterThanOrEqual:
1131          return "op_GreaterThanOrEqual";
1132        case BinaryOperatorType.LessThanOrEqual:
1133          return "op_LessThanOrEqual";
1134        default:
1135          return null;
1136      }
1137    }
1138    #endregion
1139   
1140    #region Null coalescing operator
1141    ResolveResult ResolveNullCoalescingOperator(ResolveResult lhs, ResolveResult rhs)
1142    {
1143      if (NullableType.IsNullable(lhs.Type)) {
1144        IType a0 = NullableType.GetUnderlyingType(lhs.Type);
1145        if (TryConvert(ref rhs, a0)) {
1146          return BinaryOperatorResolveResult(a0, lhs, BinaryOperatorType.NullCoalescing, rhs);
1147        }
1148      }
1149      if (TryConvert(ref rhs, lhs.Type)) {
1150        return BinaryOperatorResolveResult(lhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs);
1151      }
1152      if (TryConvert(ref lhs, rhs.Type)) {
1153        return BinaryOperatorResolveResult(rhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs);
1154      } else {
1155        return new ErrorResolveResult(lhs.Type);
1156      }
1157    }
1158    #endregion
1159    #endregion
1160   
1161    #region Get user-defined operator candidates
1162    IEnumerable<IParameterizedMember> GetUserDefinedOperatorCandidates(IType type, string operatorName)
1163    {
1164      if (operatorName == null)
1165        return EmptyList<IMethod>.Instance;
1166      TypeCode c = ReflectionHelper.GetTypeCode(type);
1167      if (TypeCode.Boolean <= c && c <= TypeCode.Decimal || c == TypeCode.String) {
1168        // The .NET framework contains some of C#'s built-in operators as user-defined operators.
1169        // However, we must not use those as user-defined operators (we would skip numeric promotion).
1170        return EmptyList<IMethod>.Instance;
1171      }
1172      // C# 4.0 spec: §7.3.5 Candidate user-defined operators
1173      var operators = type.GetMethods(m => m.IsOperator && m.Name == operatorName).ToList();
1174      LiftUserDefinedOperators(operators);
1175      return operators;
1176    }
1177   
1178    void LiftUserDefinedOperators(List<IMethod> operators)
1179    {
1180      int nonLiftedMethodCount = operators.Count;
1181      // Construct lifted operators
1182      for (int i = 0; i < nonLiftedMethodCount; i++) {
1183        var liftedMethod = LiftUserDefinedOperator(operators[i]);
1184        if (liftedMethod != null)
1185          operators.Add(liftedMethod);
1186      }
1187    }
1188   
1189    LiftedUserDefinedOperator LiftUserDefinedOperator(IMethod m)
1190    {
1191      if (IsComparisonOperator(m)) {
1192        if (!m.ReturnType.Equals(compilation.FindType(KnownTypeCode.Boolean)))
1193          return null; // cannot lift this operator
1194      } else {
1195        if (!NullableType.IsNonNullableValueType(m.ReturnType))
1196          return null; // cannot lift this operator
1197      }
1198      for (int i = 0; i < m.Parameters.Count; i++) {
1199        if (!NullableType.IsNonNullableValueType(m.Parameters[i].Type))
1200          return null; // cannot lift this operator
1201      }
1202      return new LiftedUserDefinedOperator(m);
1203    }
1204   
1205    static bool IsComparisonOperator(IMethod m)
1206    {
1207      var type = OperatorDeclaration.GetOperatorType(m.Name);
1208      return type.HasValue && type.Value.IsComparisonOperator();
1209    }
1210   
1211    sealed class LiftedUserDefinedOperator : SpecializedMethod, OverloadResolution.ILiftedOperator
1212    {
1213      internal readonly IParameterizedMember nonLiftedOperator;
1214     
1215      public LiftedUserDefinedOperator(IMethod nonLiftedMethod)
1216        : base((IMethod)nonLiftedMethod.MemberDefinition, nonLiftedMethod.Substitution)
1217      {
1218        this.nonLiftedOperator = nonLiftedMethod;
1219        var substitution = new MakeNullableVisitor(nonLiftedMethod.Compilation, nonLiftedMethod.Substitution);
1220        this.Parameters = base.CreateParameters(substitution);
1221        // Comparison operators keep the 'bool' return type even when lifted.
1222        if (IsComparisonOperator(nonLiftedMethod))
1223          this.ReturnType = nonLiftedMethod.ReturnType;
1224        else
1225          this.ReturnType = nonLiftedMethod.ReturnType.AcceptVisitor(substitution);
1226      }
1227     
1228      public IList<IParameter> NonLiftedParameters {
1229        get { return nonLiftedOperator.Parameters; }
1230      }
1231     
1232      public override bool Equals(object obj)
1233      {
1234        LiftedUserDefinedOperator op = obj as LiftedUserDefinedOperator;
1235        return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator);
1236      }
1237     
1238      public override int GetHashCode()
1239      {
1240        return nonLiftedOperator.GetHashCode() ^ 0x7191254;
1241      }
1242    }
1243   
1244    sealed class MakeNullableVisitor : TypeVisitor
1245    {
1246      readonly ICompilation compilation;
1247      readonly TypeParameterSubstitution typeParameterSubstitution;
1248     
1249      public MakeNullableVisitor(ICompilation compilation, TypeParameterSubstitution typeParameterSubstitution)
1250      {
1251        this.compilation = compilation;
1252        this.typeParameterSubstitution = typeParameterSubstitution;
1253      }
1254     
1255      public override IType VisitTypeDefinition(ITypeDefinition type)
1256      {
1257        return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution));
1258      }
1259     
1260      public override IType VisitTypeParameter(ITypeParameter type)
1261      {
1262        return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution));
1263      }
1264     
1265      public override IType VisitParameterizedType(ParameterizedType type)
1266      {
1267        return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution));
1268      }
1269     
1270      public override IType VisitOtherType(IType type)
1271      {
1272        return NullableType.Create(compilation, type.AcceptVisitor(typeParameterSubstitution));
1273      }
1274    }
1275   
1276    ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r, System.Linq.Expressions.ExpressionType operatorType)
1277    {
1278      if (r.BestCandidateErrors != OverloadResolutionErrors.None)
1279        return r.CreateResolveResult(null);
1280      IMethod method = (IMethod)r.BestCandidate;
1281      return new OperatorResolveResult(method.ReturnType, operatorType, method,
1282                                       isLiftedOperator: method is OverloadResolution.ILiftedOperator,
1283                                       operands: r.GetArgumentsWithConversions());
1284    }
1285    #endregion
1286   
1287    #region ResolveCast
1288    bool TryConvert(ref ResolveResult rr, IType targetType)
1289    {
1290      Conversion c = conversions.ImplicitConversion(rr, targetType);
1291      if (c.IsValid) {
1292        rr = Convert(rr, targetType, c);
1293        return true;
1294      } else {
1295        return false;
1296      }
1297    }
1298
1299    /// <summary>
1300    ///
1301    /// </summary>
1302    /// <param name="rr">The input resolve result that should be converted.
1303    /// If a conversion exists, it is applied to the resolve result</param>
1304    /// <param name="targetType">The target type that we should convert to</param>
1305    /// <param name="isNullable">Whether we are dealing with a lifted operator</param>
1306    /// <param name="enumRR">The resolve result that is enum-typed.
1307    /// If necessary, a nullable conversion is applied.</param>
1308    /// <param name="allowConversionFromConstantZero">
1309    /// Whether the conversion from the constant zero is allowed.
1310    /// </param>
1311    /// <returns>True if the conversion is successful; false otherwise.
1312    /// If the conversion is not successful, the ref parameters will not be modified.</returns>
1313    bool TryConvertEnum(ref ResolveResult rr, IType targetType, ref bool isNullable, ref ResolveResult enumRR, bool allowConversionFromConstantZero = true)
1314    {
1315      Conversion c;
1316      if (!isNullable) {
1317        // Try non-nullable
1318        c = conversions.ImplicitConversion(rr, targetType);
1319        if (c.IsValid && (allowConversionFromConstantZero || !c.IsEnumerationConversion)) {
1320          rr = Convert(rr, targetType, c);
1321          return true;
1322        }
1323      }
1324      // make targetType nullable if it isn't already:
1325      if (!targetType.IsKnownType(KnownTypeCode.NullableOfT))
1326        targetType = NullableType.Create(compilation, targetType);
1327     
1328      c = conversions.ImplicitConversion(rr, targetType);
1329      if (c.IsValid && (allowConversionFromConstantZero || !c.IsEnumerationConversion)) {
1330        rr = Convert(rr, targetType, c);
1331        isNullable = true;
1332        // Also convert the enum-typed RR to nullable, if it isn't already
1333        if (!enumRR.Type.IsKnownType(KnownTypeCode.NullableOfT)) {
1334          var nullableType = NullableType.Create(compilation, enumRR.Type);
1335          enumRR = new ConversionResolveResult(nullableType, enumRR, Conversion.ImplicitNullableConversion);
1336        }
1337        return true;
1338      }
1339      return false;
1340    }
1341   
1342    ResolveResult Convert(ResolveResult rr, IType targetType)
1343    {
1344      return Convert(rr, targetType, conversions.ImplicitConversion(rr, targetType));
1345    }
1346   
1347    ResolveResult Convert(ResolveResult rr, IType targetType, Conversion c)
1348    {
1349      if (c == Conversion.IdentityConversion)
1350        return rr;
1351      else if (rr.IsCompileTimeConstant && c != Conversion.None && !c.IsUserDefined)
1352        return ResolveCast(targetType, rr);
1353      else
1354        return new ConversionResolveResult(targetType, rr, c, checkForOverflow);
1355    }
1356   
1357    public ResolveResult ResolveCast(IType targetType, ResolveResult expression)
1358    {
1359      // C# 4.0 spec: §7.7.6 Cast expressions
1360      Conversion c = conversions.ExplicitConversion(expression, targetType);
1361      if (expression.IsCompileTimeConstant && !c.IsUserDefined) {
1362        TypeCode code = ReflectionHelper.GetTypeCode(targetType);
1363        if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) {
1364          try {
1365            return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
1366          } catch (OverflowException) {
1367            return new ErrorResolveResult(targetType);
1368          } catch (InvalidCastException) {
1369            return new ErrorResolveResult(targetType);
1370          }
1371        } else if (code == TypeCode.String) {
1372          if (expression.ConstantValue == null || expression.ConstantValue is string)
1373            return new ConstantResolveResult(targetType, expression.ConstantValue);
1374          else
1375            return new ErrorResolveResult(targetType);
1376        } else if (targetType.Kind == TypeKind.Enum) {
1377          code = ReflectionHelper.GetTypeCode(GetEnumUnderlyingType(targetType));
1378          if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expression.ConstantValue != null) {
1379            try {
1380              return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
1381            } catch (OverflowException) {
1382              return new ErrorResolveResult(targetType);
1383            } catch (InvalidCastException) {
1384              return new ErrorResolveResult(targetType);
1385            }
1386          }
1387        }
1388      }
1389      return new ConversionResolveResult(targetType, expression, c, checkForOverflow);
1390    }
1391   
1392    internal object CSharpPrimitiveCast(TypeCode targetType, object input)
1393    {
1394      return Utils.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow);
1395    }
1396    #endregion
1397   
1398    #region ResolveSimpleName
1399    public ResolveResult ResolveSimpleName(string identifier, IList<IType> typeArguments, bool isInvocationTarget = false)
1400    {
1401      // C# 4.0 spec: §7.6.2 Simple Names
1402     
1403      return LookupSimpleNameOrTypeName(
1404        identifier, typeArguments,
1405        isInvocationTarget ? NameLookupMode.InvocationTarget : NameLookupMode.Expression);
1406    }
1407   
1408    public ResolveResult LookupSimpleNameOrTypeName(string identifier, IList<IType> typeArguments, NameLookupMode lookupMode)
1409    {
1410      // C# 4.0 spec: §3.8 Namespace and type names; §7.6.2 Simple Names
1411     
1412      if (identifier == null)
1413        throw new ArgumentNullException("identifier");
1414      if (typeArguments == null)
1415        throw new ArgumentNullException("typeArguments");
1416     
1417      int k = typeArguments.Count;
1418     
1419      if (k == 0) {
1420        if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) {
1421          // Look in local variables
1422          foreach (IVariable v in this.LocalVariables) {
1423            if (v.Name == identifier) {
1424              return new LocalResolveResult(v);
1425            }
1426          }
1427          // Look in parameters of current method
1428          IParameterizedMember parameterizedMember = this.CurrentMember as IParameterizedMember;
1429          if (parameterizedMember != null) {
1430            foreach (IParameter p in parameterizedMember.Parameters) {
1431              if (p.Name == identifier) {
1432                return new LocalResolveResult(p);
1433              }
1434            }
1435          }
1436        }
1437       
1438        // look in type parameters of current method
1439        IMethod m = this.CurrentMember as IMethod;
1440        if (m != null) {
1441          foreach (ITypeParameter tp in m.TypeParameters) {
1442            if (tp.Name == identifier)
1443              return new TypeResolveResult(tp);
1444          }
1445        }
1446      }
1447     
1448      bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument));
1449     
1450      ResolveResult r = null;
1451      if (currentTypeDefinitionCache != null) {
1452        Dictionary<string, ResolveResult> cache = null;
1453        bool foundInCache = false;
1454        if (k == 0) {
1455          switch (lookupMode) {
1456            case NameLookupMode.Expression:
1457              cache = currentTypeDefinitionCache.SimpleNameLookupCacheExpression;
1458              break;
1459            case NameLookupMode.InvocationTarget:
1460              cache = currentTypeDefinitionCache.SimpleNameLookupCacheInvocationTarget;
1461              break;
1462            case NameLookupMode.Type:
1463              cache = currentTypeDefinitionCache.SimpleTypeLookupCache;
1464              break;
1465          }
1466          if (cache != null) {
1467            lock (cache)
1468              foundInCache = cache.TryGetValue(identifier, out r);
1469          }
1470        }
1471        if (foundInCache) {
1472          r = (r != null ? r.ShallowClone() : null);
1473        } else {
1474          r = LookInCurrentType(identifier, typeArguments, lookupMode, parameterizeResultType);
1475          if (cache != null) {
1476            // also cache missing members (r==null)
1477            lock (cache)
1478              cache[identifier] = r;
1479          }
1480        }
1481        if (r != null)
1482          return r;
1483      }
1484     
1485      if (context.CurrentUsingScope == null) {
1486        // If no using scope was specified, we still need to look in the global namespace:
1487        r = LookInUsingScopeNamespace(null, compilation.RootNamespace, identifier, typeArguments, parameterizeResultType);
1488      } else {
1489        if (k == 0 && lookupMode != NameLookupMode.TypeInUsingDeclaration) {
1490          if (context.CurrentUsingScope.ResolveCache.TryGetValue(identifier, out r)) {
1491            r = (r != null ? r.ShallowClone() : null);
1492          } else {
1493            r = LookInCurrentUsingScope(identifier, typeArguments, false, false);
1494            context.CurrentUsingScope.ResolveCache.TryAdd(identifier, r);
1495          }
1496        } else {
1497          r = LookInCurrentUsingScope(identifier, typeArguments, lookupMode == NameLookupMode.TypeInUsingDeclaration, parameterizeResultType);
1498        }
1499      }
1500      if (r != null)
1501        return r;
1502     
1503      if (typeArguments.Count == 0 && identifier == "dynamic") {
1504        return new TypeResolveResult(SpecialType.Dynamic);
1505      } else {
1506        return new UnknownIdentifierResolveResult(identifier, typeArguments.Count);
1507      }
1508    }
1509   
1510    public bool IsVariableReferenceWithSameType (ResolveResult rr, string identifier, out TypeResolveResult trr)
1511    {
1512      if (!(rr is MemberResolveResult || rr is LocalResolveResult)) {
1513        trr = null;
1514        return false;
1515      }
1516      trr = LookupSimpleNameOrTypeName (identifier, EmptyList<IType>.Instance, NameLookupMode.Type) as TypeResolveResult;
1517      return trr != null && trr.Type.Equals (rr.Type);
1518    }
1519   
1520    ResolveResult LookInCurrentType(string identifier, IList<IType> typeArguments, NameLookupMode lookupMode, bool parameterizeResultType)
1521    {
1522      int k = typeArguments.Count;
1523      MemberLookup lookup = CreateMemberLookup(lookupMode);
1524      // look in current type definitions
1525      for (ITypeDefinition t = this.CurrentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) {
1526        if (k == 0) {
1527          // Look for type parameter with that name
1528          var typeParameters = t.TypeParameters;
1529          // Look at all type parameters, including those copied from outer classes,
1530          // so that we can fetch the version with the correct owner.
1531          for (int i = 0; i < typeParameters.Count; i++) {
1532            if (typeParameters[i].Name == identifier)
1533              return new TypeResolveResult(typeParameters[i]);
1534          }
1535        }
1536       
1537        if (lookupMode == NameLookupMode.BaseTypeReference && t == this.CurrentTypeDefinition) {
1538          // don't look in current type when resolving a base type reference
1539          continue;
1540        }
1541       
1542        ResolveResult r;
1543        if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) {
1544          var targetResolveResult = (t == this.CurrentTypeDefinition ? ResolveThisReference() : new TypeResolveResult(t));
1545          r = lookup.Lookup(targetResolveResult, identifier, typeArguments, lookupMode == NameLookupMode.InvocationTarget);
1546        } else {
1547          r = lookup.LookupType(t, identifier, typeArguments, parameterizeResultType);
1548        }
1549        if (!(r is UnknownMemberResolveResult)) // but do return AmbiguousMemberResolveResult
1550          return r;
1551      }
1552      return null;
1553    }
1554   
1555    ResolveResult LookInCurrentUsingScope(string identifier, IList<IType> typeArguments, bool isInUsingDeclaration, bool parameterizeResultType)
1556    {
1557      // look in current namespace definitions
1558      ResolvedUsingScope currentUsingScope = this.CurrentUsingScope;
1559      for (ResolvedUsingScope u = currentUsingScope; u != null; u = u.Parent) {
1560        var resultInNamespace = LookInUsingScopeNamespace(u, u.Namespace, identifier, typeArguments, parameterizeResultType);
1561        if (resultInNamespace != null)
1562          return resultInNamespace;
1563        // then look for aliases:
1564        if (typeArguments.Count == 0) {
1565          if (u.ExternAliases.Contains(identifier)) {
1566            return ResolveExternAlias(identifier);
1567          }
1568          if (!(isInUsingDeclaration && u == currentUsingScope)) {
1569            foreach (var pair in u.UsingAliases) {
1570              if (pair.Key == identifier) {
1571                return pair.Value.ShallowClone();
1572              }
1573            }
1574          }
1575        }
1576        // finally, look in the imported namespaces:
1577        if (!(isInUsingDeclaration && u == currentUsingScope)) {
1578          IType firstResult = null;
1579          foreach (var importedNamespace in u.Usings) {
1580            ITypeDefinition def = importedNamespace.GetTypeDefinition(identifier, typeArguments.Count);
1581            if (def != null) {
1582              IType resultType;
1583              if (parameterizeResultType && typeArguments.Count > 0)
1584                resultType = new ParameterizedType(def, typeArguments);
1585              else
1586                resultType = def;
1587             
1588              if (firstResult == null || !TopLevelTypeDefinitionIsAccessible(firstResult.GetDefinition())) {
1589                if (TopLevelTypeDefinitionIsAccessible(resultType.GetDefinition()))
1590                  firstResult = resultType;
1591              } else if (TopLevelTypeDefinitionIsAccessible(def)) {
1592                return new AmbiguousTypeResolveResult(firstResult);
1593              }
1594            }
1595          }
1596          if (firstResult != null)
1597            return new TypeResolveResult(firstResult);
1598        }
1599        // if we didn't find anything: repeat lookup with parent namespace
1600      }
1601      return null;
1602    }
1603
1604    ResolveResult LookInUsingScopeNamespace(ResolvedUsingScope usingScope, INamespace n, string identifier, IList<IType> typeArguments, bool parameterizeResultType)
1605    {
1606      if (n == null)
1607        return null;
1608      // first look for a namespace
1609      int k = typeArguments.Count;
1610      if (k == 0) {
1611        INamespace childNamespace = n.GetChildNamespace(identifier);
1612        if (childNamespace != null) {
1613          if (usingScope != null && usingScope.HasAlias(identifier))
1614            return new AmbiguousTypeResolveResult(new UnknownType(null, identifier));
1615          return new NamespaceResolveResult(childNamespace);
1616        }
1617      }
1618      // then look for a type
1619      ITypeDefinition def = n.GetTypeDefinition(identifier, k);
1620      if (def != null) {
1621        IType result = def;
1622        if (parameterizeResultType && k > 0) {
1623          result = new ParameterizedType(def, typeArguments);
1624        }
1625        if (usingScope != null && usingScope.HasAlias(identifier))
1626          return new AmbiguousTypeResolveResult(result);
1627        else
1628          return new TypeResolveResult(result);
1629      }
1630      return null;
1631    }
1632   
1633    bool TopLevelTypeDefinitionIsAccessible(ITypeDefinition typeDef)
1634    {
1635      if (typeDef.IsInternal) {
1636        return typeDef.ParentAssembly.InternalsVisibleTo(compilation.MainAssembly);
1637      }
1638      return true;
1639    }
1640   
1641    /// <summary>
1642    /// Looks up an alias (identifier in front of :: operator)
1643    /// </summary>
1644    public ResolveResult ResolveAlias(string identifier)
1645    {
1646      if (identifier == "global")
1647        return new NamespaceResolveResult(compilation.RootNamespace);
1648     
1649      for (ResolvedUsingScope n = this.CurrentUsingScope; n != null; n = n.Parent) {
1650        if (n.ExternAliases.Contains(identifier)) {
1651          return ResolveExternAlias(identifier);
1652        }
1653        foreach (var pair in n.UsingAliases) {
1654          if (pair.Key == identifier) {
1655            return (pair.Value as NamespaceResolveResult) ?? ErrorResult;
1656          }
1657        }
1658      }
1659      return ErrorResult;
1660    }
1661   
1662    ResolveResult ResolveExternAlias(string alias)
1663    {
1664      INamespace ns = compilation.GetNamespaceForExternAlias(alias);
1665      if (ns != null)
1666        return new NamespaceResolveResult(ns);
1667      else
1668        return ErrorResult;
1669    }
1670    #endregion
1671   
1672    #region ResolveMemberAccess
1673    public ResolveResult ResolveMemberAccess(ResolveResult target, string identifier, IList<IType> typeArguments, NameLookupMode lookupMode = NameLookupMode.Expression)
1674    {
1675      // C# 4.0 spec: §7.6.4
1676     
1677      bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument));
1678      NamespaceResolveResult nrr = target as NamespaceResolveResult;
1679      if (nrr != null) {
1680        return ResolveMemberAccessOnNamespace(nrr, identifier, typeArguments, parameterizeResultType);
1681      }
1682     
1683      if (target.Type.Kind == TypeKind.Dynamic)
1684        return new DynamicMemberResolveResult(target, identifier);
1685     
1686      MemberLookup lookup = CreateMemberLookup(lookupMode);
1687      ResolveResult result;
1688      switch (lookupMode) {
1689        case NameLookupMode.Expression:
1690          result = lookup.Lookup(target, identifier, typeArguments, isInvocation: false);
1691          break;
1692        case NameLookupMode.InvocationTarget:
1693          result = lookup.Lookup(target, identifier, typeArguments, isInvocation: true);
1694          break;
1695        case NameLookupMode.Type:
1696        case NameLookupMode.TypeInUsingDeclaration:
1697        case NameLookupMode.BaseTypeReference:
1698          // Don't do the UnknownMemberResolveResult/MethodGroupResolveResult processing,
1699          // it's only relevant for expressions.
1700          return lookup.LookupType(target.Type, identifier, typeArguments, parameterizeResultType);
1701        default:
1702          throw new NotSupportedException("Invalid value for NameLookupMode");
1703      }
1704      if (result is UnknownMemberResolveResult) {
1705        // We intentionally use all extension methods here, not just the eligible ones.
1706        // Proper eligibility checking is only possible for the full invocation
1707        // (after we know the remaining arguments).
1708        // The eligibility check in GetExtensionMethods is only intended for code completion.
1709        var extensionMethods = GetExtensionMethods(identifier, typeArguments);
1710        if (extensionMethods.Count > 0) {
1711          return new MethodGroupResolveResult(target, identifier, EmptyList<MethodListWithDeclaringType>.Instance, typeArguments) {
1712            extensionMethods = extensionMethods
1713          };
1714        }
1715      } else {
1716        MethodGroupResolveResult mgrr = result as MethodGroupResolveResult;
1717        if (mgrr != null) {
1718          Debug.Assert(mgrr.extensionMethods == null);
1719          // set the values that are necessary to make MethodGroupResolveResult.GetExtensionMethods() work
1720          mgrr.resolver = this;
1721        }
1722      }
1723      return result;
1724    }
1725   
1726    [Obsolete("Use ResolveMemberAccess() with NameLookupMode.Type instead")]
1727    public ResolveResult ResolveMemberType(ResolveResult target, string identifier, IList<IType> typeArguments)
1728    {
1729      return ResolveMemberAccess(target, identifier, typeArguments, NameLookupMode.Type);
1730    }
1731   
1732    ResolveResult ResolveMemberAccessOnNamespace(NamespaceResolveResult nrr, string identifier, IList<IType> typeArguments, bool parameterizeResultType)
1733    {
1734      if (typeArguments.Count == 0) {
1735        INamespace childNamespace = nrr.Namespace.GetChildNamespace(identifier);
1736        if (childNamespace != null)
1737          return new NamespaceResolveResult(childNamespace);
1738      }
1739      ITypeDefinition def = nrr.Namespace.GetTypeDefinition(identifier, typeArguments.Count);
1740      if (def != null) {
1741        if (parameterizeResultType && typeArguments.Count > 0)
1742          return new TypeResolveResult(new ParameterizedType(def, typeArguments));
1743        else
1744          return new TypeResolveResult(def);
1745      }
1746      return ErrorResult;
1747    }
1748   
1749    /// <summary>
1750    /// Creates a MemberLookup instance using this resolver's settings.
1751    /// </summary>
1752    public MemberLookup CreateMemberLookup()
1753    {
1754      ITypeDefinition currentTypeDefinition = this.CurrentTypeDefinition;
1755      bool isInEnumMemberInitializer = this.CurrentMember != null && this.CurrentMember.SymbolKind == SymbolKind.Field
1756        && currentTypeDefinition != null && currentTypeDefinition.Kind == TypeKind.Enum;
1757      return new MemberLookup(currentTypeDefinition, this.Compilation.MainAssembly, isInEnumMemberInitializer);
1758    }
1759   
1760    /// <summary>
1761    /// Creates a MemberLookup instance using this resolver's settings.
1762    /// </summary>
1763    public MemberLookup CreateMemberLookup(NameLookupMode lookupMode)
1764    {
1765      if (lookupMode == NameLookupMode.BaseTypeReference && this.CurrentTypeDefinition != null) {
1766        // When looking up a base type reference, treat us as being outside the current type definition
1767        // for accessibility purposes.
1768        // This avoids a stack overflow when referencing a protected class nested inside the base class
1769        // of a parent class. (NameLookupTests.InnerClassInheritingFromProtectedBaseInnerClassShouldNotCauseStackOverflow)
1770        return new MemberLookup(this.CurrentTypeDefinition.DeclaringTypeDefinition, this.Compilation.MainAssembly, false);
1771      } else {
1772        return CreateMemberLookup();
1773      }
1774    }
1775    #endregion
1776   
1777    #region ResolveIdentifierInObjectInitializer
1778    public ResolveResult ResolveIdentifierInObjectInitializer(string identifier)
1779    {
1780      MemberLookup memberLookup = CreateMemberLookup();
1781      return memberLookup.Lookup(this.CurrentObjectInitializer, identifier, EmptyList<IType>.Instance, false);
1782    }
1783    #endregion
1784   
1785    #region GetExtensionMethods
1786    /// <summary>
1787    /// Gets all extension methods that are available in the current context.
1788    /// </summary>
1789    /// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
1790    /// <param name="typeArguments">Explicitly provided type arguments.
1791    /// An empty list will return all matching extension method definitions;
1792    /// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
1793    /// with the matching number of type parameters.</param>
1794    /// <remarks>
1795    /// The results are stored in nested lists because they are grouped by using scope.
1796    /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
1797    /// the return value will be
1798    /// new List {
1799    ///    new List { all extensions from MoreExtensions },
1800    ///    new List { all extensions from SomeExtensions }
1801    /// }
1802    /// </remarks>
1803    public List<List<IMethod>> GetExtensionMethods(string name = null, IList<IType> typeArguments = null)
1804    {
1805      return GetExtensionMethods(null, name, typeArguments);
1806    }
1807   
1808    /// <summary>
1809    /// Gets the extension methods that are called 'name'
1810    /// and are applicable with a first argument type of 'targetType'.
1811    /// </summary>
1812    /// <param name="targetType">Type of the 'this' argument</param>
1813    /// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
1814    /// <param name="typeArguments">Explicitly provided type arguments.
1815    /// An empty list will return all matching extension method definitions;
1816    /// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
1817    /// with the matching number of type parameters.</param>
1818    /// <param name="substituteInferredTypes">
1819    /// Specifies whether to produce a <see cref="SpecializedMethod"/>
1820    /// when type arguments could be inferred from <paramref name="targetType"/>. This parameter
1821    /// is only used for inferred types and has no effect if <paramref name="typeArguments"/> is non-empty.
1822    /// </param>
1823    /// <remarks>
1824    /// The results are stored in nested lists because they are grouped by using scope.
1825    /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
1826    /// the return value will be
1827    /// new List {
1828    ///    new List { all extensions from MoreExtensions },
1829    ///    new List { all extensions from SomeExtensions }
1830    /// }
1831    /// </remarks>
1832    public List<List<IMethod>> GetExtensionMethods(IType targetType, string name = null, IList<IType> typeArguments = null, bool substituteInferredTypes = false)
1833    {
1834      var lookup = CreateMemberLookup();
1835      List<List<IMethod>> extensionMethodGroups = new List<List<IMethod>>();
1836      foreach (var inputGroup in GetAllExtensionMethods(lookup)) {
1837        List<IMethod> outputGroup = new List<IMethod>();
1838        foreach (var method in inputGroup) {
1839          if (name != null && method.Name != name)
1840            continue;
1841          if (!lookup.IsAccessible(method, false))
1842            continue;
1843          IType[] inferredTypes;
1844          if (typeArguments != null && typeArguments.Count > 0) {
1845            if (method.TypeParameters.Count != typeArguments.Count)
1846              continue;
1847            var sm = method.Specialize(new TypeParameterSubstitution(null, typeArguments));
1848            if (IsEligibleExtensionMethod(compilation, conversions, targetType, sm, false, out inferredTypes))
1849              outputGroup.Add(sm);
1850          } else {
1851            if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) {
1852              if (substituteInferredTypes && inferredTypes != null) {
1853                outputGroup.Add(method.Specialize(new TypeParameterSubstitution(null, inferredTypes)));
1854              } else {
1855                outputGroup.Add(method);
1856              }
1857            }
1858          }
1859        }
1860        if (outputGroup.Count > 0)
1861          extensionMethodGroups.Add(outputGroup);
1862      }
1863      return extensionMethodGroups;
1864    }
1865   
1866    /// <summary>
1867    /// Checks whether the specified extension method is eligible on the target type.
1868    /// </summary>
1869    /// <param name="targetType">Target type that is passed as first argument to the extension method.</param>
1870    /// <param name="method">The extension method.</param>
1871    /// <param name="useTypeInference">Whether to perform type inference for the method.
1872    /// Use <c>false</c> if <paramref name="method"/> is already parameterized (e.g. when type arguments were given explicitly).
1873    /// Otherwise, use <c>true</c>.
1874    /// </param>
1875    /// <param name="outInferredTypes">If the method is generic and <paramref name="useTypeInference"/> is <c>true</c>,
1876    /// and at least some of the type arguments could be inferred, this parameter receives the inferred type arguments.
1877    /// Since only the type for the first parameter is considered, not all type arguments may be inferred.
1878    /// If an array is returned, any slot with an uninferred type argument will be set to the method's
1879    /// corresponding type parameter.
1880    /// </param>
1881    public static bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
1882    {
1883      if (targetType == null)
1884        throw new ArgumentNullException("targetType");
1885      if (method == null)
1886        throw new ArgumentNullException("method");
1887      var compilation = method.Compilation;
1888      return IsEligibleExtensionMethod(compilation, CSharpConversions.Get(compilation), targetType, method, useTypeInference, out outInferredTypes);
1889    }
1890   
1891    static bool IsEligibleExtensionMethod(ICompilation compilation, CSharpConversions conversions, IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
1892    {
1893      outInferredTypes = null;
1894      if (targetType == null)
1895        return true;
1896      if (method.Parameters.Count == 0)
1897        return false;
1898      IType thisParameterType = method.Parameters[0].Type;
1899      if (useTypeInference && method.TypeParameters.Count > 0) {
1900        // We need to infer type arguments from targetType:
1901        TypeInference ti = new TypeInference(compilation, conversions);
1902        ResolveResult[] arguments = { new ResolveResult(targetType) };
1903        IType[] parameterTypes = { method.Parameters[0].Type };
1904        bool success;
1905        var inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out success);
1906        var substitution = new TypeParameterSubstitution(null, inferredTypes);
1907        // Validate that the types that could be inferred (aren't unknown) satisfy the constraints:
1908        bool hasInferredTypes = false;
1909        for (int i = 0; i < inferredTypes.Length; i++) {
1910          if (inferredTypes[i].Kind != TypeKind.Unknown && inferredTypes[i].Kind != TypeKind.UnboundTypeArgument) {
1911            hasInferredTypes = true;
1912            if (!OverloadResolution.ValidateConstraints(method.TypeParameters[i], inferredTypes[i], substitution, conversions))
1913              return false;
1914          } else {
1915            inferredTypes[i] = method.TypeParameters[i]; // do not substitute types that could not be inferred
1916          }
1917        }
1918        if (hasInferredTypes)
1919          outInferredTypes = inferredTypes;
1920        thisParameterType = thisParameterType.AcceptVisitor(substitution);
1921      }
1922      Conversion c = conversions.ImplicitConversion(targetType, thisParameterType);
1923      return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion);
1924    }
1925   
1926    /// <summary>
1927    /// Gets all extension methods available in the current using scope.
1928    /// This list includes inaccessible methods.
1929    /// </summary>
1930    IList<List<IMethod>> GetAllExtensionMethods(MemberLookup lookup)
1931    {
1932      var currentUsingScope = context.CurrentUsingScope;
1933      if (currentUsingScope == null)
1934        return EmptyList<List<IMethod>>.Instance;
1935      List<List<IMethod>> extensionMethodGroups = LazyInit.VolatileRead(ref currentUsingScope.AllExtensionMethods);
1936      if (extensionMethodGroups != null) {
1937        return extensionMethodGroups;
1938      }
1939      extensionMethodGroups = new List<List<IMethod>>();
1940      List<IMethod> m;
1941      for (ResolvedUsingScope scope = currentUsingScope; scope != null; scope = scope.Parent) {
1942        INamespace ns = scope.Namespace;
1943        if (ns != null) {
1944          m = GetExtensionMethods(lookup, ns).ToList();
1945          if (m.Count > 0)
1946            extensionMethodGroups.Add(m);
1947        }
1948       
1949        m = scope.Usings
1950          .Distinct()
1951          .SelectMany(importedNamespace =>  GetExtensionMethods(lookup, importedNamespace))
1952          .ToList();
1953        if (m.Count > 0)
1954          extensionMethodGroups.Add(m);
1955      }
1956      return LazyInit.GetOrSet(ref currentUsingScope.AllExtensionMethods, extensionMethodGroups);
1957    }
1958   
1959    IEnumerable<IMethod> GetExtensionMethods(MemberLookup lookup, INamespace ns)
1960    {
1961      // TODO: maybe make this a property on INamespace?
1962      return
1963        from c in ns.Types
1964        where c.IsStatic && c.HasExtensionMethods && c.TypeParameters.Count == 0 && lookup.IsAccessible(c, false)
1965        from m in c.Methods
1966        where m.IsExtensionMethod
1967        select m;
1968    }
1969    #endregion
1970   
1971    #region ResolveInvocation
1972
1973    IList<ResolveResult> AddArgumentNamesIfNecessary(ResolveResult[] arguments, string[] argumentNames) {
1974      if (argumentNames == null) {
1975        return arguments;
1976      }
1977      else {
1978        var result = new ResolveResult[arguments.Length];
1979        for (int i = 0; i < arguments.Length; i++) {
1980          result[i] = (argumentNames[i] != null ? new NamedArgumentResolveResult(argumentNames[i], arguments[i]) : arguments[i]);
1981        }
1982        return result;
1983      }
1984    }
1985
1986    private ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames, bool allowOptionalParameters)
1987    {
1988      // C# 4.0 spec: §7.6.5
1989     
1990      if (target.Type.Kind == TypeKind.Dynamic) {
1991        return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames));
1992      }
1993     
1994      bool isDynamic = arguments.Any(a => a.Type.Kind == TypeKind.Dynamic);
1995      MethodGroupResolveResult mgrr = target as MethodGroupResolveResult;
1996      if (mgrr != null) {
1997        if (isDynamic) {
1998          // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method.
1999          var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray());
2000          var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList();
2001
2002          if (applicableMethods.Count > 1) {
2003            ResolveResult actualTarget;
2004            if (applicableMethods.All(x => x.Method.IsStatic) && !(mgrr.TargetResult is TypeResolveResult))
2005              actualTarget = new TypeResolveResult(mgrr.TargetType);
2006            else
2007              actualTarget = mgrr.TargetResult;
2008
2009            var l = new List<MethodListWithDeclaringType>();
2010            foreach (var m in applicableMethods) {
2011              if (l.Count == 0 || l[l.Count - 1].DeclaringType != m.DeclaringType)
2012                l.Add(new MethodListWithDeclaringType(m.DeclaringType));
2013              l[l.Count - 1].Add(m.Method);
2014            }
2015            return new DynamicInvocationResolveResult(new MethodGroupResolveResult(actualTarget, mgrr.MethodName, l, mgrr.TypeArguments), DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames));
2016          }
2017        }
2018
2019        OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions, allowOptionalParameters: allowOptionalParameters);
2020        if (or.BestCandidate != null) {
2021          if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult))
2022            return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType), returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
2023          else
2024            return or.CreateResolveResult(mgrr.TargetResult, returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
2025        } else {
2026          // No candidate found at all (not even an inapplicable one).
2027          // This can happen with empty method groups (as sometimes used with extension methods)
2028          return new UnknownMethodResolveResult(
2029            mgrr.TargetType, mgrr.MethodName, mgrr.TypeArguments, CreateParameters(arguments, argumentNames));
2030        }
2031      }
2032      UnknownMemberResolveResult umrr = target as UnknownMemberResolveResult;
2033      if (umrr != null) {
2034        return new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames));
2035      }
2036      UnknownIdentifierResolveResult uirr = target as UnknownIdentifierResolveResult;
2037      if (uirr != null && CurrentTypeDefinition != null) {
2038        return new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList<IType>.Instance, CreateParameters(arguments, argumentNames));
2039      }
2040      IMethod invokeMethod = target.Type.GetDelegateInvokeMethod();
2041      if (invokeMethod != null) {
2042        OverloadResolution or = CreateOverloadResolution(arguments, argumentNames);
2043        or.AddCandidate(invokeMethod);
2044        return new CSharpInvocationResolveResult(
2045          target, invokeMethod, //invokeMethod.ReturnType.Resolve(context),
2046          or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors,
2047          isExpandedForm: or.BestCandidateIsExpandedForm,
2048          isDelegateInvocation: true,
2049          argumentToParameterMap: or.GetArgumentToParameterMap(),
2050          returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
2051      }
2052      return ErrorResult;
2053    }
2054
2055    /// <summary>
2056    /// Resolves an invocation.
2057    /// </summary>
2058    /// <param name="target">The target of the invocation. Usually a MethodGroupResolveResult.</param>
2059    /// <param name="arguments">
2060    /// Arguments passed to the method.
2061    /// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2062    /// </param>
2063    /// <param name="argumentNames">
2064    /// The argument names. Pass the null string for positional arguments.
2065    /// </param>
2066    /// <returns>InvocationResolveResult or UnknownMethodResolveResult</returns>
2067    public ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
2068    {
2069      return ResolveInvocation(target, arguments, argumentNames, allowOptionalParameters: true);
2070    }
2071   
2072    List<IParameter> CreateParameters(ResolveResult[] arguments, string[] argumentNames)
2073    {
2074      List<IParameter> list = new List<IParameter>();
2075      if (argumentNames == null) {
2076        argumentNames = new string[arguments.Length];
2077      } else {
2078        if (argumentNames.Length != arguments.Length)
2079          throw new ArgumentException();
2080        argumentNames = (string[])argumentNames.Clone();
2081      }
2082      for (int i = 0; i < arguments.Length; i++) {
2083        // invent argument names where necessary:
2084        if (argumentNames[i] == null) {
2085          string newArgumentName = GuessParameterName(arguments[i]);
2086          if (argumentNames.Contains(newArgumentName)) {
2087            // disambiguate argument name (e.g. add a number)
2088            int num = 1;
2089            string newName;
2090            do {
2091              newName = newArgumentName + num.ToString();
2092              num++;
2093            } while(argumentNames.Contains(newName));
2094            newArgumentName = newName;
2095          }
2096          argumentNames[i] = newArgumentName;
2097        }
2098       
2099        // create the parameter:
2100        ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
2101        if (brrr != null) {
2102          list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], isRef: brrr.IsRef, isOut: brrr.IsOut));
2103        } else {
2104          // argument might be a lambda or delegate type, so we have to try to guess the delegate type
2105          IType type = arguments[i].Type;
2106          if (type.Kind == TypeKind.Null || type.Kind == TypeKind.Unknown) {
2107            list.Add(new DefaultParameter(compilation.FindType(KnownTypeCode.Object), argumentNames[i]));
2108          } else {
2109            list.Add(new DefaultParameter(type, argumentNames[i]));
2110          }
2111        }
2112      }
2113      return list;
2114    }
2115   
2116    static string GuessParameterName(ResolveResult rr)
2117    {
2118      MemberResolveResult mrr = rr as MemberResolveResult;
2119      if (mrr != null)
2120        return mrr.Member.Name;
2121     
2122      UnknownMemberResolveResult umrr = rr as UnknownMemberResolveResult;
2123      if (umrr != null)
2124        return umrr.MemberName;
2125     
2126      MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult;
2127      if (mgrr != null)
2128        return mgrr.MethodName;
2129     
2130      LocalResolveResult vrr = rr as LocalResolveResult;
2131      if (vrr != null)
2132        return MakeParameterName(vrr.Variable.Name);
2133     
2134      if (rr.Type.Kind != TypeKind.Unknown && !string.IsNullOrEmpty(rr.Type.Name)) {
2135        return MakeParameterName(rr.Type.Name);
2136      } else {
2137        return "parameter";
2138      }
2139    }
2140   
2141    static string MakeParameterName(string variableName)
2142    {
2143      if (string.IsNullOrEmpty(variableName))
2144        return "parameter";
2145      if (variableName.Length > 1 && variableName[0] == '_')
2146        variableName = variableName.Substring(1);
2147      return char.ToLower(variableName[0]) + variableName.Substring(1);
2148    }
2149   
2150    OverloadResolution CreateOverloadResolution(ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null)
2151    {
2152      var or = new OverloadResolution(compilation, arguments, argumentNames, typeArguments, conversions);
2153      or.CheckForOverflow = checkForOverflow;
2154      return or;
2155    }
2156    #endregion
2157   
2158    #region ResolveIndexer
2159    /// <summary>
2160    /// Resolves an indexer access.
2161    /// </summary>
2162    /// <param name="target">Target expression.</param>
2163    /// <param name="arguments">
2164    /// Arguments passed to the indexer.
2165    /// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2166    /// </param>
2167    /// <param name="argumentNames">
2168    /// The argument names. Pass the null string for positional arguments.
2169    /// </param>
2170    /// <returns>ArrayAccessResolveResult, InvocationResolveResult, or ErrorResolveResult</returns>
2171    public ResolveResult ResolveIndexer(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
2172    {
2173      switch (target.Type.Kind) {
2174        case TypeKind.Dynamic:
2175          return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames));
2176         
2177        case TypeKind.Array:
2178        case TypeKind.Pointer:
2179          // §7.6.6.1 Array access / §18.5.3 Pointer element access
2180          AdjustArrayAccessArguments(arguments);
2181          return new ArrayAccessResolveResult(((TypeWithElementType)target.Type).ElementType, target, arguments);
2182      }
2183     
2184      // §7.6.6.2 Indexer access
2185
2186      MemberLookup lookup = CreateMemberLookup();
2187      var indexers = lookup.LookupIndexers(target);
2188
2189      if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) {
2190        // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable indexer.
2191        var or2 = CreateOverloadResolution(arguments, argumentNames, null);
2192        var applicableIndexers = indexers.SelectMany(x => x).Where(m => OverloadResolution.IsApplicable(or2.AddCandidate(m))).ToList();
2193
2194        if (applicableIndexers.Count > 1) {
2195          return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames));
2196        }
2197      }
2198
2199      OverloadResolution or = CreateOverloadResolution(arguments, argumentNames);
2200      or.AddMethodLists(indexers);
2201      if (or.BestCandidate != null) {
2202        return or.CreateResolveResult(target);
2203      } else {
2204        return ErrorResult;
2205      }
2206    }
2207   
2208    /// <summary>
2209    /// Converts all arguments to int,uint,long or ulong.
2210    /// </summary>
2211    void AdjustArrayAccessArguments(ResolveResult[] arguments)
2212    {
2213      for (int i = 0; i < arguments.Length; i++) {
2214        if (!(TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int32)) ||
2215              TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt32)) ||
2216              TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int64)) ||
2217              TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt64))))
2218        {
2219          // conversion failed
2220          arguments[i] = Convert(arguments[i], compilation.FindType(KnownTypeCode.Int32), Conversion.None);
2221        }
2222      }
2223    }
2224    #endregion
2225   
2226    #region ResolveObjectCreation
2227    /// <summary>
2228    /// Resolves an object creation.
2229    /// </summary>
2230    /// <param name="type">Type of the object to create.</param>
2231    /// <param name="arguments">
2232    /// Arguments passed to the constructor.
2233    /// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2234    /// </param>
2235    /// <param name="argumentNames">
2236    /// The argument names. Pass the null string for positional arguments.
2237    /// </param>
2238    /// <param name="allowProtectedAccess">
2239    /// Whether to allow calling protected constructors.
2240    /// This should be false except when resolving constructor initializers.
2241    /// </param>
2242    /// <param name="initializerStatements">
2243    /// Statements for Objects/Collections initializer.
2244    /// <see cref="InvocationResolveResult.InitializerStatements"/>
2245    /// </param>
2246    /// <returns>InvocationResolveResult or ErrorResolveResult</returns>
2247    public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList<ResolveResult> initializerStatements = null)
2248    {
2249      if (type.Kind == TypeKind.Delegate && arguments.Length == 1) {
2250        ResolveResult input = arguments[0];
2251        IMethod invoke = input.Type.GetDelegateInvokeMethod();
2252        if (invoke != null) {
2253          input = new MethodGroupResolveResult(
2254            input, invoke.Name,
2255            methods: new[] { new MethodListWithDeclaringType(invoke.DeclaringType) { invoke } },
2256            typeArguments: EmptyList<IType>.Instance
2257          );
2258        }
2259        return Convert(input, type);
2260      }
2261      OverloadResolution or = CreateOverloadResolution(arguments, argumentNames);
2262      MemberLookup lookup = CreateMemberLookup();
2263      var allApplicable = (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic) ? new List<IMethod>() : null);
2264      foreach (IMethod ctor in type.GetConstructors()) {
2265        if (lookup.IsAccessible(ctor, allowProtectedAccess)) {
2266          var orErrors = or.AddCandidate(ctor);
2267          if (allApplicable != null && OverloadResolution.IsApplicable(orErrors))
2268            allApplicable.Add(ctor);
2269        }
2270        else
2271          or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible);
2272      }
2273
2274      if (allApplicable != null && allApplicable.Count > 1) {
2275        // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable constructor.
2276        return new DynamicInvocationResolveResult(new MethodGroupResolveResult(null, allApplicable[0].Name, new[] { new MethodListWithDeclaringType(type, allApplicable) }, null), DynamicInvocationType.ObjectCreation, AddArgumentNamesIfNecessary(arguments, argumentNames), initializerStatements);
2277      }
2278
2279      if (or.BestCandidate != null) {
2280        return or.CreateResolveResult(null, initializerStatements);
2281      } else {
2282        return new ErrorResolveResult(type);
2283      }
2284    }
2285    #endregion
2286   
2287    #region ResolveSizeOf
2288    /// <summary>
2289    /// Resolves 'sizeof(type)'.
2290    /// </summary>
2291    public ResolveResult ResolveSizeOf(IType type)
2292    {
2293      IType int32 = compilation.FindType(KnownTypeCode.Int32);
2294      int? size = null;
2295      var typeForConstant = (type.Kind == TypeKind.Enum) ? type.GetDefinition().EnumUnderlyingType : type;
2296
2297      switch (ReflectionHelper.GetTypeCode(typeForConstant)) {
2298        case TypeCode.Boolean:
2299        case TypeCode.SByte:
2300        case TypeCode.Byte:
2301          size = 1;
2302          break;
2303        case TypeCode.Char:
2304        case TypeCode.Int16:
2305        case TypeCode.UInt16:
2306          size = 2;
2307          break;
2308        case TypeCode.Int32:
2309        case TypeCode.UInt32:
2310        case TypeCode.Single:
2311          size = 4;
2312          break;
2313        case TypeCode.Int64:
2314        case TypeCode.UInt64:
2315        case TypeCode.Double:
2316          size = 8;
2317          break;
2318      }
2319      return new SizeOfResolveResult(int32, type, size);
2320    }
2321    #endregion
2322   
2323    #region Resolve This/Base Reference
2324    /// <summary>
2325    /// Resolves 'this'.
2326    /// </summary>
2327    public ResolveResult ResolveThisReference()
2328    {
2329      ITypeDefinition t = CurrentTypeDefinition;
2330      if (t != null) {
2331        if (t.TypeParameterCount != 0) {
2332          // Self-parameterize the type
2333          return new ThisResolveResult(new ParameterizedType(t, t.TypeParameters));
2334        } else {
2335          return new ThisResolveResult(t);
2336        }
2337      }
2338      return ErrorResult;
2339    }
2340   
2341    /// <summary>
2342    /// Resolves 'base'.
2343    /// </summary>
2344    public ResolveResult ResolveBaseReference()
2345    {
2346      ITypeDefinition t = CurrentTypeDefinition;
2347      if (t != null) {
2348        foreach (IType baseType in t.DirectBaseTypes) {
2349          if (baseType.Kind != TypeKind.Unknown && baseType.Kind != TypeKind.Interface) {
2350            return new ThisResolveResult(baseType, causesNonVirtualInvocation: true);
2351          }
2352        }
2353      }
2354      return ErrorResult;
2355    }
2356    #endregion
2357   
2358    #region ResolveConditional
2359    /// <summary>
2360    /// Converts the input to <c>bool</c> using the rules for boolean expressions.
2361    /// That is, <c>operator true</c> is used if a regular conversion to <c>bool</c> is not possible.
2362    /// </summary>
2363    public ResolveResult ResolveCondition(ResolveResult input)
2364    {
2365      if (input == null)
2366        throw new ArgumentNullException("input");
2367      IType boolean = compilation.FindType(KnownTypeCode.Boolean);
2368      Conversion c = conversions.ImplicitConversion(input, boolean);
2369      if (!c.IsValid) {
2370        var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault();
2371        if (opTrue != null) {
2372          c = Conversion.UserDefinedConversion(opTrue, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
2373        }
2374      }
2375      return Convert(input, boolean, c);
2376    }
2377   
2378    /// <summary>
2379    /// Converts the negated input to <c>bool</c> using the rules for boolean expressions.
2380    /// Computes <c>!(bool)input</c> if the implicit cast to bool is valid; otherwise
2381    /// computes <c>input.operator false()</c>.
2382    /// </summary>
2383    public ResolveResult ResolveConditionFalse(ResolveResult input)
2384    {
2385      if (input == null)
2386        throw new ArgumentNullException("input");
2387      IType boolean = compilation.FindType(KnownTypeCode.Boolean);
2388      Conversion c = conversions.ImplicitConversion(input, boolean);
2389      if (!c.IsValid) {
2390        var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault();
2391        if (opFalse != null) {
2392          c = Conversion.UserDefinedConversion(opFalse, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
2393          return Convert(input, boolean, c);
2394        }
2395      }
2396      return ResolveUnaryOperator(UnaryOperatorType.Not, Convert(input, boolean, c));
2397    }
2398   
2399    public ResolveResult ResolveConditional(ResolveResult condition, ResolveResult trueExpression, ResolveResult falseExpression)
2400    {
2401      // C# 4.0 spec §7.14: Conditional operator
2402     
2403      bool isValid;
2404      IType resultType;
2405      if (trueExpression.Type.Kind == TypeKind.Dynamic || falseExpression.Type.Kind == TypeKind.Dynamic) {
2406        resultType = SpecialType.Dynamic;
2407        isValid = TryConvert(ref trueExpression, resultType) & TryConvert(ref falseExpression, resultType);
2408      } else if (HasType(trueExpression) && HasType(falseExpression)) {
2409        Conversion t2f = conversions.ImplicitConversion(trueExpression, falseExpression.Type);
2410        Conversion f2t = conversions.ImplicitConversion(falseExpression, trueExpression.Type);
2411        // The operator is valid:
2412        // a) if there's a conversion in one direction but not the other
2413        // b) if there are conversions in both directions, and the types are equivalent
2414        if (IsBetterConditionalConversion(t2f, f2t)) {
2415          resultType = falseExpression.Type;
2416          isValid = true;
2417          trueExpression = Convert(trueExpression, resultType, t2f);
2418        } else if (IsBetterConditionalConversion(f2t, t2f)) {
2419          resultType = trueExpression.Type;
2420          isValid = true;
2421          falseExpression = Convert(falseExpression, resultType, f2t);
2422        } else {
2423          resultType = trueExpression.Type;
2424          isValid = trueExpression.Type.Equals(falseExpression.Type);
2425        }
2426      } else if (HasType(trueExpression)) {
2427        resultType = trueExpression.Type;
2428        isValid = TryConvert(ref falseExpression, resultType);
2429      } else if (HasType(falseExpression)) {
2430        resultType = falseExpression.Type;
2431        isValid = TryConvert(ref trueExpression, resultType);
2432      } else {
2433        return ErrorResult;
2434      }
2435      condition = ResolveCondition(condition);
2436      if (isValid) {
2437        if (condition.IsCompileTimeConstant && trueExpression.IsCompileTimeConstant && falseExpression.IsCompileTimeConstant) {
2438          bool? val = condition.ConstantValue as bool?;
2439          if (val == true)
2440            return trueExpression;
2441          else if (val == false)
2442            return falseExpression;
2443        }
2444        return new OperatorResolveResult(resultType, System.Linq.Expressions.ExpressionType.Conditional,
2445                                         condition, trueExpression, falseExpression);
2446      } else {
2447        return new ErrorResolveResult(resultType);
2448      }
2449    }
2450   
2451    bool IsBetterConditionalConversion(Conversion c1, Conversion c2)
2452    {
2453      // Valid is better than ImplicitConstantExpressionConversion is better than invalid
2454      if (!c1.IsValid)
2455        return false;
2456      if (c1 != Conversion.ImplicitConstantExpressionConversion && c2 == Conversion.ImplicitConstantExpressionConversion)
2457        return true;
2458      return !c2.IsValid;
2459    }
2460   
2461    bool HasType(ResolveResult r)
2462    {
2463      return r.Type.Kind != TypeKind.Unknown && r.Type.Kind != TypeKind.Null;
2464    }
2465    #endregion
2466   
2467    #region ResolvePrimitive
2468    public ResolveResult ResolvePrimitive(object value)
2469    {
2470      if (value == null) {
2471        return new ResolveResult(SpecialType.NullType);
2472      } else {
2473        TypeCode typeCode = Type.GetTypeCode(value.GetType());
2474        IType type = compilation.FindType(typeCode);
2475        return new ConstantResolveResult(type, value);
2476      }
2477    }
2478    #endregion
2479   
2480    #region ResolveDefaultValue
2481    public ResolveResult ResolveDefaultValue(IType type)
2482    {
2483      return new ConstantResolveResult(type, GetDefaultValue(type));
2484    }
2485   
2486    public static object GetDefaultValue(IType type)
2487    {
2488      ITypeDefinition typeDef = type.GetDefinition();
2489      if (typeDef == null)
2490        return null;
2491      if (typeDef.Kind == TypeKind.Enum) {
2492        typeDef = typeDef.EnumUnderlyingType.GetDefinition();
2493        if (typeDef == null)
2494          return null;
2495      }
2496      switch (typeDef.KnownTypeCode) {
2497        case KnownTypeCode.Boolean:
2498          return false;
2499        case KnownTypeCode.Char:
2500          return '\0';
2501        case KnownTypeCode.SByte:
2502          return (sbyte)0;
2503        case KnownTypeCode.Byte:
2504          return (byte)0;
2505        case KnownTypeCode.Int16:
2506          return (short)0;
2507        case KnownTypeCode.UInt16:
2508          return (ushort)0;
2509        case KnownTypeCode.Int32:
2510          return 0;
2511        case KnownTypeCode.UInt32:
2512          return 0U;
2513        case KnownTypeCode.Int64:
2514          return 0L;
2515        case KnownTypeCode.UInt64:
2516          return 0UL;
2517        case KnownTypeCode.Single:
2518          return 0f;
2519        case KnownTypeCode.Double:
2520          return 0.0;
2521        case KnownTypeCode.Decimal:
2522          return 0m;
2523        default:
2524          return null;
2525      }
2526    }
2527    #endregion
2528   
2529    #region ResolveArrayCreation
2530    /// <summary>
2531    /// Resolves an array creation.
2532    /// </summary>
2533    /// <param name="elementType">
2534    /// The array element type.
2535    /// Pass null to resolve an implicitly-typed array creation.
2536    /// </param>
2537    /// <param name="sizeArguments">
2538    /// The size arguments.
2539    /// The length of this array will be used as the number of dimensions of the array type.
2540    /// Negative values will be treated as errors.
2541    /// </param>
2542    /// <param name="initializerElements">
2543    /// The initializer elements. May be null if no array initializer was specified.
2544    /// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2545    /// </param>
2546    public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, int[] sizeArguments, ResolveResult[] initializerElements = null)
2547    {
2548      ResolveResult[] sizeArgResults = new ResolveResult[sizeArguments.Length];
2549      for (int i = 0; i < sizeArguments.Length; i++) {
2550        if (sizeArguments[i] < 0)
2551          sizeArgResults[i] = ErrorResolveResult.UnknownError;
2552        else
2553          sizeArgResults[i] = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), sizeArguments[i]);
2554      }
2555      return ResolveArrayCreation(elementType, sizeArgResults, initializerElements);
2556    }
2557   
2558    /// <summary>
2559    /// Resolves an array creation.
2560    /// </summary>
2561    /// <param name="elementType">
2562    /// The array element type.
2563    /// Pass null to resolve an implicitly-typed array creation.
2564    /// </param>
2565    /// <param name="sizeArguments">
2566    /// The size arguments.
2567    /// The length of this array will be used as the number of dimensions of the array type.
2568    /// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2569    /// </param>
2570    /// <param name="initializerElements">
2571    /// The initializer elements. May be null if no array initializer was specified.
2572    /// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2573    /// </param>
2574    public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, ResolveResult[] sizeArguments, ResolveResult[] initializerElements = null)
2575    {
2576      int dimensions = sizeArguments.Length;
2577      if (dimensions == 0)
2578        throw new ArgumentException("sizeArguments.Length must not be 0");
2579      if (elementType == null) {
2580        TypeInference typeInference = new TypeInference(compilation, conversions);
2581        bool success;
2582        elementType = typeInference.GetBestCommonType(initializerElements, out success);
2583      }
2584      IType arrayType = new ArrayType(compilation, elementType, dimensions);
2585     
2586      AdjustArrayAccessArguments(sizeArguments);
2587     
2588      if (initializerElements != null) {
2589        for (int i = 0; i < initializerElements.Length; i++) {
2590          initializerElements[i] = Convert(initializerElements[i], elementType);
2591        }
2592      }
2593      return new ArrayCreateResolveResult(arrayType, sizeArguments, initializerElements);
2594    }
2595    #endregion
2596   
2597    public ResolveResult ResolveTypeOf(IType referencedType)
2598    {
2599      return new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), referencedType);
2600    }
2601   
2602    #region ResolveAssignment
2603    public ResolveResult ResolveAssignment(AssignmentOperatorType op, ResolveResult lhs, ResolveResult rhs)
2604    {
2605      var linqOp = AssignmentExpression.GetLinqNodeType(op, this.CheckForOverflow);
2606      var bop = AssignmentExpression.GetCorrespondingBinaryOperator(op);
2607      if (bop == null) {
2608        return new OperatorResolveResult(lhs.Type, linqOp, lhs, this.Convert(rhs, lhs.Type));
2609      }
2610      ResolveResult bopResult = ResolveBinaryOperator(bop.Value, lhs, rhs);
2611      OperatorResolveResult opResult = bopResult as OperatorResolveResult;
2612      if (opResult == null || opResult.Operands.Count != 2)
2613        return bopResult;
2614      return new OperatorResolveResult(lhs.Type, linqOp, opResult.UserDefinedOperatorMethod, opResult.IsLiftedOperator,
2615                                       new [] { lhs, opResult.Operands[1] });
2616    }
2617    #endregion
2618  }
2619}
Note: See TracBrowser for help on using the repository browser.