1
ļ»æ// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
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:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
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.
20
using System.Collections.Concurrent;
21
using System.Collections.Generic;
22
using System.Diagnostics;
23
using System.Globalization;
26
using System.Threading;
27
using ICSharpCode.NRefactory.CSharp.TypeSystem;
28
using ICSharpCode.NRefactory.Semantics;
29
using ICSharpCode.NRefactory.TypeSystem;
30
using ICSharpCode.NRefactory.TypeSystem.Implementation;
31
using ICSharpCode.NRefactory.Utils;
33
namespace ICSharpCode.NRefactory.CSharp.Resolver
36
/// Contains the main resolver logic.
39
/// This class is thread-safe.
41
public class CSharpResolver
43
static readonly ResolveResult ErrorResult = ErrorResolveResult.UnknownError;
45
readonly ICompilation compilation;
46
internal readonly CSharpConversions conversions;
47
readonly CSharpTypeResolveContext context;
48
readonly bool checkForOverflow;
49
readonly bool isWithinLambdaExpression;
52
public CSharpResolver(ICompilation compilation)
54
if (compilation == null)
55
throw new ArgumentNullException("compilation");
56
this.compilation = compilation;
57
this.conversions = CSharpConversions.Get(compilation);
58
this.context = new CSharpTypeResolveContext(compilation.MainAssembly);
60
var pc = compilation.MainAssembly.UnresolvedAssembly as CSharpProjectContent;
62
this.checkForOverflow = pc.CompilerSettings.CheckForOverflow;
66
public CSharpResolver(CSharpTypeResolveContext context)
69
throw new ArgumentNullException("context");
70
this.compilation = context.Compilation;
71
this.conversions = CSharpConversions.Get(compilation);
72
this.context = context;
73
if (context.CurrentTypeDefinition != null)
74
currentTypeDefinitionCache = new TypeDefinitionCache(context.CurrentTypeDefinition);
77
private CSharpResolver(ICompilation compilation, CSharpConversions conversions, CSharpTypeResolveContext context, bool checkForOverflow, bool isWithinLambdaExpression, TypeDefinitionCache currentTypeDefinitionCache, ImmutableStack<IVariable> localVariableStack, ObjectInitializerContext objectInitializerStack)
79
this.compilation = compilation;
80
this.conversions = conversions;
81
this.context = context;
82
this.checkForOverflow = checkForOverflow;
83
this.isWithinLambdaExpression = isWithinLambdaExpression;
84
this.currentTypeDefinitionCache = currentTypeDefinitionCache;
85
this.localVariableStack = localVariableStack;
86
this.objectInitializerStack = objectInitializerStack;
92
/// Gets the compilation used by the resolver.
94
public ICompilation Compilation {
95
get { return compilation; }
99
/// Gets the current type resolve context.
101
public CSharpTypeResolveContext CurrentTypeResolveContext {
102
get { return context; }
105
CSharpResolver WithContext(CSharpTypeResolveContext newContext)
107
return new CSharpResolver(compilation, conversions, newContext, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
111
/// Gets whether the current context is <c>checked</c>.
113
public bool CheckForOverflow {
114
get { return checkForOverflow; }
118
/// Sets whether the current context is <c>checked</c>.
120
public CSharpResolver WithCheckForOverflow(bool checkForOverflow)
122
if (checkForOverflow == this.checkForOverflow)
124
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
128
/// Gets whether the resolver is currently within a lambda expression.
130
public bool IsWithinLambdaExpression {
131
get { return isWithinLambdaExpression; }
135
/// Sets whether the resolver is currently within a lambda expression.
137
public CSharpResolver WithIsWithinLambdaExpression(bool isWithinLambdaExpression)
139
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
143
/// Gets the current member definition that is used to look up identifiers as parameters
144
/// or type parameters.
146
public IMember CurrentMember {
147
get { return context.CurrentMember; }
151
/// Sets the current member definition.
153
/// <remarks>Don't forget to also set CurrentTypeDefinition when setting CurrentMember;
154
/// setting one of the properties does not automatically set the other.</remarks>
155
public CSharpResolver WithCurrentMember(IMember member)
157
return WithContext(context.WithCurrentMember(member));
161
/// Gets the current using scope that is used to look up identifiers as class names.
163
public ResolvedUsingScope CurrentUsingScope {
164
get { return context.CurrentUsingScope; }
168
/// Sets the current using scope that is used to look up identifiers as class names.
170
public CSharpResolver WithCurrentUsingScope(ResolvedUsingScope usingScope)
172
return WithContext(context.WithUsingScope(usingScope));
176
#region Per-CurrentTypeDefinition Cache
177
readonly TypeDefinitionCache currentTypeDefinitionCache;
180
/// Gets the current type definition.
182
public ITypeDefinition CurrentTypeDefinition {
183
get { return context.CurrentTypeDefinition; }
187
/// Sets the current type definition.
189
public CSharpResolver WithCurrentTypeDefinition(ITypeDefinition typeDefinition)
191
if (this.CurrentTypeDefinition == typeDefinition)
194
TypeDefinitionCache newTypeDefinitionCache;
195
if (typeDefinition != null)
196
newTypeDefinitionCache = new TypeDefinitionCache(typeDefinition);
198
newTypeDefinitionCache = null;
200
return new CSharpResolver(compilation, conversions, context.WithCurrentTypeDefinition(typeDefinition),
201
checkForOverflow, isWithinLambdaExpression, newTypeDefinitionCache, localVariableStack, objectInitializerStack);
204
sealed class TypeDefinitionCache
206
public readonly ITypeDefinition TypeDefinition;
207
public readonly Dictionary<string, ResolveResult> SimpleNameLookupCacheExpression = new Dictionary<string, ResolveResult>();
208
public readonly Dictionary<string, ResolveResult> SimpleNameLookupCacheInvocationTarget = new Dictionary<string, ResolveResult>();
209
public readonly Dictionary<string, ResolveResult> SimpleTypeLookupCache = new Dictionary<string, ResolveResult>();
211
public TypeDefinitionCache(ITypeDefinition typeDefinition)
213
this.TypeDefinition = typeDefinition;
218
#region Local Variable Management
220
// We store the local variables in an immutable stack.
221
// The beginning of a block is marked by a null entry.
223
// This data structure is used to allow efficient cloning of the resolver with its local variable context.
224
readonly ImmutableStack<IVariable> localVariableStack = ImmutableStack<IVariable>.Empty;
226
CSharpResolver WithLocalVariableStack(ImmutableStack<IVariable> stack)
228
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, stack, objectInitializerStack);
232
/// Opens a new scope for local variables.
234
public CSharpResolver PushBlock()
236
return WithLocalVariableStack(localVariableStack.Push(null));
240
/// Closes the current scope for local variables; removing all variables in that scope.
242
public CSharpResolver PopBlock()
244
var stack = localVariableStack;
245
IVariable removedVar;
247
removedVar = stack.Peek();
249
} while (removedVar != null);
250
return WithLocalVariableStack(stack);
254
/// Adds a new variable or lambda parameter to the current block.
256
public CSharpResolver AddVariable(IVariable variable)
258
if (variable == null)
259
throw new ArgumentNullException("variable");
260
return WithLocalVariableStack(localVariableStack.Push(variable));
264
/// Removes the variable that was just added.
266
public CSharpResolver PopLastVariable()
268
if (localVariableStack.Peek() == null)
269
throw new InvalidOperationException("There is no variable within the current block.");
270
return WithLocalVariableStack(localVariableStack.Pop());
274
/// Gets all currently visible local variables and lambda parameters.
276
public IEnumerable<IVariable> LocalVariables {
278
return localVariableStack.Where(v => v != null);
283
#region Object Initializer Context
284
sealed class ObjectInitializerContext
286
internal readonly ResolveResult initializedObject;
287
internal readonly ObjectInitializerContext prev;
289
public ObjectInitializerContext(ResolveResult initializedObject, CSharpResolver.ObjectInitializerContext prev)
291
this.initializedObject = initializedObject;
296
readonly ObjectInitializerContext objectInitializerStack;
298
CSharpResolver WithObjectInitializerStack(ObjectInitializerContext stack)
300
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, stack);
304
/// Pushes the type of the object that is currently being initialized.
306
public CSharpResolver PushObjectInitializer(ResolveResult initializedObject)
308
if (initializedObject == null)
309
throw new ArgumentNullException("initializedObject");
310
return WithObjectInitializerStack(new ObjectInitializerContext(initializedObject, objectInitializerStack));
313
public CSharpResolver PopObjectInitializer()
315
if (objectInitializerStack == null)
316
throw new InvalidOperationException();
317
return WithObjectInitializerStack(objectInitializerStack.prev);
321
/// Gets whether this context is within an object initializer.
323
public bool IsInObjectInitializer {
324
get { return objectInitializerStack != null; }
328
/// Gets the current object initializer. This usually is an <see cref="InitializedObjectResolveResult"/>
329
/// or (for nested initializers) a semantic tree based on an <see cref="InitializedObjectResolveResult"/>.
330
/// Returns ErrorResolveResult if there is no object initializer.
332
public ResolveResult CurrentObjectInitializer {
334
return objectInitializerStack != null ? objectInitializerStack.initializedObject : ErrorResult;
339
/// Gets the type of the object currently being initialized.
340
/// Returns SharedTypes.Unknown if no object initializer is currently open (or if the object initializer
341
/// has unknown type).
343
public IType CurrentObjectInitializerType {
344
get { return CurrentObjectInitializer.Type; }
350
/// Creates a copy of this CSharp resolver.
352
[Obsolete("CSharpResolver is immutable, cloning is no longer necessary")]
353
public CSharpResolver Clone()
359
#region ResolveUnaryOperator
360
#region ResolveUnaryOperator method
361
public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression)
363
if (expression.Type.Kind == TypeKind.Dynamic) {
364
if (op == UnaryOperatorType.Await) {
365
return new AwaitResolveResult(SpecialType.Dynamic, new DynamicInvocationResolveResult(new DynamicMemberResolveResult(expression, "GetAwaiter"), DynamicInvocationType.Invocation, EmptyList<ResolveResult>.Instance), SpecialType.Dynamic, null, null, null);
368
return UnaryOperatorResolveResult(SpecialType.Dynamic, op, expression);
372
// C# 4.0 spec: Ā§7.3.3 Unary operator overload resolution
373
string overloadableOperatorName = GetOverloadableOperatorName(op);
374
if (overloadableOperatorName == null) {
376
case UnaryOperatorType.Dereference:
377
PointerType p = expression.Type as PointerType;
379
return UnaryOperatorResolveResult(p.ElementType, op, expression);
382
case UnaryOperatorType.AddressOf:
383
return UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression);
384
case UnaryOperatorType.Await: {
385
ResolveResult getAwaiterMethodGroup = ResolveMemberAccess(expression, "GetAwaiter", EmptyList<IType>.Instance, NameLookupMode.InvocationTarget);
386
ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, new ResolveResult[0], argumentNames: null, allowOptionalParameters: false);
388
var lookup = CreateMemberLookup();
389
IMethod getResultMethod;
390
IType awaitResultType;
391
var getResultMethodGroup = lookup.Lookup(getAwaiterInvocation, "GetResult", EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
392
if (getResultMethodGroup != null) {
393
var getResultOR = getResultMethodGroup.PerformOverloadResolution(compilation, new ResolveResult[0], allowExtensionMethods: false, conversions: conversions);
394
getResultMethod = getResultOR.FoundApplicableCandidate ? getResultOR.GetBestCandidateWithSubstitutedTypeArguments() as IMethod : null;
395
awaitResultType = getResultMethod != null ? getResultMethod.ReturnType : SpecialType.UnknownType;
398
getResultMethod = null;
399
awaitResultType = SpecialType.UnknownType;
402
var isCompletedRR = lookup.Lookup(getAwaiterInvocation, "IsCompleted", EmptyList<IType>.Instance, false);
403
var isCompletedProperty = (isCompletedRR is MemberResolveResult ? ((MemberResolveResult)isCompletedRR).Member as IProperty : null);
404
if (isCompletedProperty != null && (!isCompletedProperty.ReturnType.IsKnownType(KnownTypeCode.Boolean) || !isCompletedProperty.CanGet))
405
isCompletedProperty = null;
407
var interfaceOnCompleted = compilation.FindType(KnownTypeCode.INotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "OnCompleted");
408
var interfaceUnsafeOnCompleted = compilation.FindType(KnownTypeCode.ICriticalNotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "UnsafeOnCompleted");
410
IMethod onCompletedMethod = null;
411
var candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceUnsafeOnCompleted)).ToList();
412
if (candidates.Count == 0) {
413
candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceOnCompleted)).ToList();
414
if (candidates.Count == 1)
415
onCompletedMethod = candidates[0];
417
else if (candidates.Count == 1) {
418
onCompletedMethod = candidates[0];
421
return new AwaitResolveResult(awaitResultType, getAwaiterInvocation, getAwaiterInvocation.Type, isCompletedProperty, onCompletedMethod, getResultMethod);
425
throw new ArgumentException("Invalid value for UnaryOperatorType", "op");
428
// If the type is nullable, get the underlying type:
429
IType type = NullableType.GetUnderlyingType(expression.Type);
430
bool isNullable = NullableType.IsNullable(expression.Type);
432
// the operator is overloadable:
433
OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { expression });
434
foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) {
435
userDefinedOperatorOR.AddCandidate(candidate);
437
if (userDefinedOperatorOR.FoundApplicableCandidate) {
438
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
441
expression = UnaryNumericPromotion(op, ref type, isNullable, expression);
442
CSharpOperators.OperatorMethod[] methodGroup;
443
CSharpOperators operators = CSharpOperators.Get(compilation);
445
case UnaryOperatorType.Increment:
446
case UnaryOperatorType.Decrement:
447
case UnaryOperatorType.PostIncrement:
448
case UnaryOperatorType.PostDecrement:
449
// C# 4.0 spec: Ā§7.6.9 Postfix increment and decrement operators
450
// C# 4.0 spec: Ā§7.7.5 Prefix increment and decrement operators
451
TypeCode code = ReflectionHelper.GetTypeCode(type);
452
if ((code >= TypeCode.Char && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer)
453
return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
455
return new ErrorResolveResult(expression.Type);
456
case UnaryOperatorType.Plus:
457
methodGroup = operators.UnaryPlusOperators;
459
case UnaryOperatorType.Minus:
460
methodGroup = CheckForOverflow ? operators.CheckedUnaryMinusOperators : operators.UncheckedUnaryMinusOperators;
462
case UnaryOperatorType.Not:
463
methodGroup = operators.LogicalNegationOperators;
465
case UnaryOperatorType.BitNot:
466
if (type.Kind == TypeKind.Enum) {
467
if (expression.IsCompileTimeConstant && !isNullable && expression.ConstantValue != null) {
468
// evaluate as (E)(~(U)x);
469
var U = compilation.FindType(expression.ConstantValue.GetType());
470
var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue);
471
return CheckErrorAndResolveUncheckedCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum));
473
return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
476
methodGroup = operators.BitwiseComplementOperators;
480
throw new InvalidOperationException();
482
OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { expression });
483
foreach (var candidate in methodGroup) {
484
builtinOperatorOR.AddCandidate(candidate);
486
CSharpOperators.UnaryOperatorMethod m = (CSharpOperators.UnaryOperatorMethod)builtinOperatorOR.BestCandidate;
487
IType resultType = m.ReturnType;
488
if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
489
if (userDefinedOperatorOR.BestCandidate != null) {
490
// If there are any user-defined operators, prefer those over the built-in operators.
491
// It'll be a more informative error.
492
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
493
} else if (builtinOperatorOR.BestCandidateAmbiguousWith != null) {
494
// If the best candidate is ambiguous, just use the input type instead
495
// of picking one of the ambiguous overloads.
496
return new ErrorResolveResult(expression.Type);
498
return new ErrorResolveResult(resultType);
500
} else if (expression.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
503
val = m.Invoke(this, expression.ConstantValue);
504
} catch (ArithmeticException) {
505
return new ErrorResolveResult(resultType);
507
return new ConstantResolveResult(resultType, val);
509
expression = Convert(expression, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
510
return UnaryOperatorResolveResult(resultType, op, expression,
511
builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
515
OperatorResolveResult UnaryOperatorResolveResult(IType resultType, UnaryOperatorType op, ResolveResult expression, bool isLifted = false)
517
return new OperatorResolveResult(
518
resultType, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow),
519
null, isLifted, new[] { expression });
523
#region UnaryNumericPromotion
524
ResolveResult UnaryNumericPromotion(UnaryOperatorType op, ref IType type, bool isNullable, ResolveResult expression)
526
// C# 4.0 spec: Ā§7.3.6.1
527
TypeCode code = ReflectionHelper.GetTypeCode(type);
528
if (isNullable && type.Kind == TypeKind.Null)
529
code = TypeCode.SByte; // cause promotion of null to int32
531
case UnaryOperatorType.Minus:
532
if (code == TypeCode.UInt32) {
533
type = compilation.FindType(KnownTypeCode.Int64);
534
return Convert(expression, MakeNullable(type, isNullable),
535
isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
537
goto case UnaryOperatorType.Plus;
538
case UnaryOperatorType.Plus:
539
case UnaryOperatorType.BitNot:
540
if (code >= TypeCode.Char && code <= TypeCode.UInt16) {
541
type = compilation.FindType(KnownTypeCode.Int32);
542
return Convert(expression, MakeNullable(type, isNullable),
543
isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
551
#region GetOverloadableOperatorName
552
static string GetOverloadableOperatorName(UnaryOperatorType op)
555
case UnaryOperatorType.Not:
556
return "op_LogicalNot";
557
case UnaryOperatorType.BitNot:
558
return "op_OnesComplement";
559
case UnaryOperatorType.Minus:
560
return "op_UnaryNegation";
561
case UnaryOperatorType.Plus:
562
return "op_UnaryPlus";
563
case UnaryOperatorType.Increment:
564
case UnaryOperatorType.PostIncrement:
565
return "op_Increment";
566
case UnaryOperatorType.Decrement:
567
case UnaryOperatorType.PostDecrement:
568
return "op_Decrement";
576
#region ResolveBinaryOperator
577
#region ResolveBinaryOperator method
578
public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
580
if (lhs.Type.Kind == TypeKind.Dynamic || rhs.Type.Kind == TypeKind.Dynamic) {
581
lhs = Convert(lhs, SpecialType.Dynamic);
582
rhs = Convert(rhs, SpecialType.Dynamic);
583
return BinaryOperatorResolveResult(SpecialType.Dynamic, lhs, op, rhs);
586
// C# 4.0 spec: Ā§7.3.4 Binary operator overload resolution
587
string overloadableOperatorName = GetOverloadableOperatorName(op);
588
if (overloadableOperatorName == null) {
590
// Handle logical and/or exactly as bitwise and/or:
591
// - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
592
// - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
593
// - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
594
if (op == BinaryOperatorType.ConditionalAnd) {
595
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd);
596
} else if (op == BinaryOperatorType.ConditionalOr) {
597
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr);
598
} else if (op == BinaryOperatorType.NullCoalescing) {
599
// null coalescing operator is not overloadable and needs to be handled separately
600
return ResolveNullCoalescingOperator(lhs, rhs);
602
throw new ArgumentException("Invalid value for BinaryOperatorType", "op");
606
// If the type is nullable, get the underlying type:
607
bool isNullable = NullableType.IsNullable(lhs.Type) || NullableType.IsNullable(rhs.Type);
608
IType lhsType = NullableType.GetUnderlyingType(lhs.Type);
609
IType rhsType = NullableType.GetUnderlyingType(rhs.Type);
611
// the operator is overloadable:
612
OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { lhs, rhs });
613
HashSet<IParameterizedMember> userOperatorCandidates = new HashSet<IParameterizedMember>();
614
userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName));
615
userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName));
616
foreach (var candidate in userOperatorCandidates) {
617
userDefinedOperatorOR.AddCandidate(candidate);
619
if (userDefinedOperatorOR.FoundApplicableCandidate) {
620
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
623
if (lhsType.Kind == TypeKind.Null && rhsType.IsReferenceType == false
624
|| lhsType.IsReferenceType == false && rhsType.Kind == TypeKind.Null)
628
if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) {
629
// special case: the shift operators allow "var x = null << null", producing int?.
630
if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
632
// for shift operators, do unary promotion independently on both arguments
633
lhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref lhsType, isNullable, lhs);
634
rhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref rhsType, isNullable, rhs);
636
bool allowNullableConstants = op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality;
637
if (!BinaryNumericPromotion(isNullable, ref lhs, ref rhs, allowNullableConstants))
638
return new ErrorResolveResult(lhs.Type);
640
// re-read underlying types after numeric promotion
641
lhsType = NullableType.GetUnderlyingType(lhs.Type);
642
rhsType = NullableType.GetUnderlyingType(rhs.Type);
644
IEnumerable<CSharpOperators.OperatorMethod> methodGroup;
645
CSharpOperators operators = CSharpOperators.Get(compilation);
647
case BinaryOperatorType.Multiply:
648
methodGroup = operators.MultiplicationOperators;
650
case BinaryOperatorType.Divide:
651
methodGroup = operators.DivisionOperators;
653
case BinaryOperatorType.Modulus:
654
methodGroup = operators.RemainderOperators;
656
case BinaryOperatorType.Add:
657
methodGroup = operators.AdditionOperators;
659
if (lhsType.Kind == TypeKind.Enum) {
660
// E operator +(E x, U y);
661
IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
662
if (TryConvert(ref rhs, underlyingType)) {
663
return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
666
if (rhsType.Kind == TypeKind.Enum) {
667
// E operator +(U x, E y);
668
IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable);
669
if (TryConvert(ref lhs, underlyingType)) {
670
return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
674
if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
675
return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
676
} else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
677
return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
680
if (lhsType is PointerType) {
681
methodGroup = new [] {
682
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
683
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
684
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
685
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
687
} else if (rhsType is PointerType) {
688
methodGroup = new [] {
689
PointerArithmeticOperator(rhsType, KnownTypeCode.Int32, rhsType),
690
PointerArithmeticOperator(rhsType, KnownTypeCode.UInt32, rhsType),
691
PointerArithmeticOperator(rhsType, KnownTypeCode.Int64, rhsType),
692
PointerArithmeticOperator(rhsType, KnownTypeCode.UInt64, rhsType)
695
if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
696
return new ErrorResolveResult(SpecialType.NullType);
699
case BinaryOperatorType.Subtract:
700
methodGroup = operators.SubtractionOperators;
702
if (lhsType.Kind == TypeKind.Enum) {
703
// E operator ā(E x, U y);
704
IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
705
if (TryConvert(ref rhs, underlyingType)) {
706
return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
708
// U operator ā(E x, E y);
709
if (TryConvert(ref rhs, lhs.Type)) {
710
return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs);
713
if (rhsType.Kind == TypeKind.Enum) {
714
// U operator ā(E x, E y);
715
if (TryConvert(ref lhs, rhs.Type)) {
716
return HandleEnumSubtraction(isNullable, rhsType, lhs, rhs);
720
if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
721
return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
722
} else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
723
return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
726
if (lhsType is PointerType) {
727
if (rhsType is PointerType) {
728
IType int64 = compilation.FindType(KnownTypeCode.Int64);
729
if (lhsType.Equals(rhsType)) {
730
return BinaryOperatorResolveResult(int64, lhs, op, rhs);
732
return new ErrorResolveResult(int64);
735
methodGroup = new [] {
736
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
737
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
738
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
739
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
743
if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
744
return new ErrorResolveResult(SpecialType.NullType);
747
case BinaryOperatorType.ShiftLeft:
748
methodGroup = operators.ShiftLeftOperators;
750
case BinaryOperatorType.ShiftRight:
751
methodGroup = operators.ShiftRightOperators;
753
case BinaryOperatorType.Equality:
754
case BinaryOperatorType.InEquality:
755
case BinaryOperatorType.LessThan:
756
case BinaryOperatorType.GreaterThan:
757
case BinaryOperatorType.LessThanOrEqual:
758
case BinaryOperatorType.GreaterThanOrEqual:
760
if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) {
761
// bool operator op(E x, E y);
762
return HandleEnumComparison(op, lhsType, isNullable, lhs, rhs);
763
} else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) {
764
// bool operator op(E x, E y);
765
return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs);
766
} else if (lhsType is PointerType && rhsType is PointerType) {
767
return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
769
if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality) {
770
if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true) {
771
// If it's a reference comparison
772
if (op == BinaryOperatorType.Equality)
773
methodGroup = operators.ReferenceEqualityOperators;
775
methodGroup = operators.ReferenceInequalityOperators;
777
} else if (lhsType.Kind == TypeKind.Null && IsNullableTypeOrNonValueType(rhs.Type)
778
|| IsNullableTypeOrNonValueType(lhs.Type) && rhsType.Kind == TypeKind.Null) {
779
// compare type parameter or nullable type with the null literal
780
return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
784
case BinaryOperatorType.Equality:
785
methodGroup = operators.ValueEqualityOperators;
787
case BinaryOperatorType.InEquality:
788
methodGroup = operators.ValueInequalityOperators;
790
case BinaryOperatorType.LessThan:
791
methodGroup = operators.LessThanOperators;
793
case BinaryOperatorType.GreaterThan:
794
methodGroup = operators.GreaterThanOperators;
796
case BinaryOperatorType.LessThanOrEqual:
797
methodGroup = operators.LessThanOrEqualOperators;
799
case BinaryOperatorType.GreaterThanOrEqual:
800
methodGroup = operators.GreaterThanOrEqualOperators;
803
throw new InvalidOperationException();
807
case BinaryOperatorType.BitwiseAnd:
808
case BinaryOperatorType.BitwiseOr:
809
case BinaryOperatorType.ExclusiveOr:
811
if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) {
812
// bool operator op(E x, E y);
813
return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
814
} else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) {
815
// bool operator op(E x, E y);
816
return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
820
case BinaryOperatorType.BitwiseAnd:
821
methodGroup = operators.BitwiseAndOperators;
823
case BinaryOperatorType.BitwiseOr:
824
methodGroup = operators.BitwiseOrOperators;
826
case BinaryOperatorType.ExclusiveOr:
827
methodGroup = operators.BitwiseXorOperators;
830
throw new InvalidOperationException();
834
case BinaryOperatorType.ConditionalAnd:
835
methodGroup = operators.LogicalAndOperators;
837
case BinaryOperatorType.ConditionalOr:
838
methodGroup = operators.LogicalOrOperators;
841
throw new InvalidOperationException();
843
OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { lhs, rhs });
844
foreach (var candidate in methodGroup) {
845
builtinOperatorOR.AddCandidate(candidate);
847
CSharpOperators.BinaryOperatorMethod m = (CSharpOperators.BinaryOperatorMethod)builtinOperatorOR.BestCandidate;
848
IType resultType = m.ReturnType;
849
if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
850
// If there are any user-defined operators, prefer those over the built-in operators.
851
// It'll be a more informative error.
852
if (userDefinedOperatorOR.BestCandidate != null)
853
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
855
return new ErrorResolveResult(resultType);
856
} else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
859
val = m.Invoke(this, lhs.ConstantValue, rhs.ConstantValue);
860
} catch (ArithmeticException) {
861
return new ErrorResolveResult(resultType);
863
return new ConstantResolveResult(resultType, val);
865
lhs = Convert(lhs, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
866
rhs = Convert(rhs, m.Parameters[1].Type, builtinOperatorOR.ArgumentConversions[1]);
867
return BinaryOperatorResolveResult(resultType, lhs, op, rhs,
868
builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
872
bool IsNullableTypeOrNonValueType(IType type)
874
return NullableType.IsNullable(type) || type.IsReferenceType != false;
877
ResolveResult BinaryOperatorResolveResult(IType resultType, ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs, bool isLifted = false)
879
return new OperatorResolveResult(
880
resultType, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow),
881
null, isLifted, new[] { lhs, rhs });
885
#region Pointer arithmetic
886
CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, KnownTypeCode inputType2)
888
return PointerArithmeticOperator(resultType, inputType1, compilation.FindType(inputType2));
891
CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, KnownTypeCode inputType1, IType inputType2)
893
return PointerArithmeticOperator(resultType, compilation.FindType(inputType1), inputType2);
896
CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, IType inputType2)
898
return new CSharpOperators.BinaryOperatorMethod(compilation) {
899
ReturnType = resultType,
901
new DefaultParameter(inputType1, string.Empty),
902
new DefaultParameter(inputType2, string.Empty)
908
#region Enum helper methods
909
IType GetEnumUnderlyingType(IType enumType)
911
ITypeDefinition def = enumType.GetDefinition();
912
return def != null ? def.EnumUnderlyingType : SpecialType.UnknownType;
916
/// Handle the case where an enum value is compared with another enum value
917
/// bool operator op(E x, E y);
919
ResolveResult HandleEnumComparison(BinaryOperatorType op, IType enumType, bool isNullable, ResolveResult lhs, ResolveResult rhs)
921
// evaluate as ((U)x op (U)y)
922
IType elementType = GetEnumUnderlyingType(enumType);
923
if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) {
924
lhs = ResolveCast(elementType, lhs);
927
rhs = ResolveCast(elementType, rhs);
930
return ResolveBinaryOperator(op, lhs, rhs);
932
IType resultType = compilation.FindType(KnownTypeCode.Boolean);
933
return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable);
937
/// Handle the case where an enum value is subtracted from another enum value
938
/// U operator ā(E x, E y);
940
ResolveResult HandleEnumSubtraction(bool isNullable, IType enumType, ResolveResult lhs, ResolveResult rhs)
942
// evaluate as (U)((U)x ā (U)y)
943
IType elementType = GetEnumUnderlyingType(enumType);
944
if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) {
945
lhs = ResolveCast(elementType, lhs);
948
rhs = ResolveCast(elementType, rhs);
951
return CheckErrorAndResolveUncheckedCast(elementType, ResolveBinaryOperator(BinaryOperatorType.Subtract, lhs, rhs));
953
IType resultType = MakeNullable(elementType, isNullable);
954
return BinaryOperatorResolveResult(resultType, lhs, BinaryOperatorType.Subtract, rhs, isNullable);
958
/// Handle the following enum operators:
959
/// E operator +(E x, U y);
960
/// E operator +(U x, E y);
961
/// E operator ā(E x, U y);
962
/// E operator &(E x, E y);
963
/// E operator |(E x, E y);
964
/// E operator ^(E x, E y);
966
ResolveResult HandleEnumOperator(bool isNullable, IType enumType, BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
968
// evaluate as (E)((U)x op (U)y)
969
if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) {
970
IType elementType = GetEnumUnderlyingType(enumType);
971
lhs = ResolveCast(elementType, lhs);
974
rhs = ResolveCast(elementType, rhs);
977
return CheckErrorAndResolveUncheckedCast(enumType, ResolveBinaryOperator(op, lhs, rhs));
979
IType resultType = MakeNullable(enumType, isNullable);
980
return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable);
983
IType MakeNullable(IType type, bool isNullable)
986
return NullableType.Create(compilation, type);
992
#region BinaryNumericPromotion
993
bool BinaryNumericPromotion(bool isNullable, ref ResolveResult lhs, ref ResolveResult rhs, bool allowNullableConstants)
995
// C# 4.0 spec: Ā§7.3.6.2
996
TypeCode lhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(lhs.Type));
997
TypeCode rhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rhs.Type));
998
// if one of the inputs is the null literal, promote that to the type of the other operand
999
if (isNullable && lhs.Type.Kind == TypeKind.Null && rhsCode >= TypeCode.Boolean && rhsCode <= TypeCode.Decimal) {
1000
lhs = CastTo(rhsCode, isNullable, lhs, allowNullableConstants);
1002
} else if (isNullable && rhs.Type.Kind == TypeKind.Null && lhsCode >= TypeCode.Boolean && lhsCode <= TypeCode.Decimal) {
1003
rhs = CastTo(lhsCode, isNullable, rhs, allowNullableConstants);
1006
bool bindingError = false;
1007
if (lhsCode >= TypeCode.Char && lhsCode <= TypeCode.Decimal
1008
&& rhsCode >= TypeCode.Char && rhsCode <= TypeCode.Decimal)
1010
TypeCode targetType;
1011
if (lhsCode == TypeCode.Decimal || rhsCode == TypeCode.Decimal) {
1012
targetType = TypeCode.Decimal;
1013
bindingError = (lhsCode == TypeCode.Single || lhsCode == TypeCode.Double
1014
|| rhsCode == TypeCode.Single || rhsCode == TypeCode.Double);
1015
} else if (lhsCode == TypeCode.Double || rhsCode == TypeCode.Double) {
1016
targetType = TypeCode.Double;
1017
} else if (lhsCode == TypeCode.Single || rhsCode == TypeCode.Single) {
1018
targetType = TypeCode.Single;
1019
} else if (lhsCode == TypeCode.UInt64 || rhsCode == TypeCode.UInt64) {
1020
targetType = TypeCode.UInt64;
1021
bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs);
1022
} else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) {
1023
targetType = TypeCode.Int64;
1024
} else if (lhsCode == TypeCode.UInt32 || rhsCode == TypeCode.UInt32) {
1025
targetType = (IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs)) ? TypeCode.Int64 : TypeCode.UInt32;
1027
targetType = TypeCode.Int32;
1029
lhs = CastTo(targetType, isNullable, lhs, allowNullableConstants);
1030
rhs = CastTo(targetType, isNullable, rhs, allowNullableConstants);
1032
return !bindingError;
1035
bool IsSigned(TypeCode code, ResolveResult rr)
1037
// Determine whether the rr with code==ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rr.Type))
1038
// is a signed primitive type.
1040
case TypeCode.SByte:
1041
case TypeCode.Int16:
1043
case TypeCode.Int32:
1044
// for int, consider implicit constant expression conversion
1045
if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (int)rr.ConstantValue >= 0)
1049
case TypeCode.Int64:
1050
// for long, consider implicit constant expression conversion
1051
if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (long)rr.ConstantValue >= 0)
1060
ResolveResult CastTo(TypeCode targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants)
1062
IType elementType = compilation.FindType(targetType);
1063
IType nullableType = MakeNullable(elementType, isNullable);
1064
if (nullableType.Equals(expression.Type))
1066
if (allowNullableConstants && expression.IsCompileTimeConstant) {
1067
if (expression.ConstantValue == null)
1068
return new ConstantResolveResult(nullableType, null);
1069
ResolveResult rr = ResolveCast(elementType, expression);
1072
Debug.Assert(rr.IsCompileTimeConstant);
1073
return new ConstantResolveResult(nullableType, rr.ConstantValue);
1075
return Convert(expression, nullableType,
1076
isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
1081
#region GetOverloadableOperatorName
1082
static string GetOverloadableOperatorName(BinaryOperatorType op)
1085
case BinaryOperatorType.Add:
1086
return "op_Addition";
1087
case BinaryOperatorType.Subtract:
1088
return "op_Subtraction";
1089
case BinaryOperatorType.Multiply:
1090
return "op_Multiply";
1091
case BinaryOperatorType.Divide:
1092
return "op_Division";
1093
case BinaryOperatorType.Modulus:
1094
return "op_Modulus";
1095
case BinaryOperatorType.BitwiseAnd:
1096
return "op_BitwiseAnd";
1097
case BinaryOperatorType.BitwiseOr:
1098
return "op_BitwiseOr";
1099
case BinaryOperatorType.ExclusiveOr:
1100
return "op_ExclusiveOr";
1101
case BinaryOperatorType.ShiftLeft:
1102
return "op_LeftShift";
1103
case BinaryOperatorType.ShiftRight:
1104
return "op_RightShift";
1105
case BinaryOperatorType.Equality:
1106
return "op_Equality";
1107
case BinaryOperatorType.InEquality:
1108
return "op_Inequality";
1109
case BinaryOperatorType.GreaterThan:
1110
return "op_GreaterThan";
1111
case BinaryOperatorType.LessThan:
1112
return "op_LessThan";
1113
case BinaryOperatorType.GreaterThanOrEqual:
1114
return "op_GreaterThanOrEqual";
1115
case BinaryOperatorType.LessThanOrEqual:
1116
return "op_LessThanOrEqual";
1123
#region Null coalescing operator
1124
ResolveResult ResolveNullCoalescingOperator(ResolveResult lhs, ResolveResult rhs)
1126
if (NullableType.IsNullable(lhs.Type)) {
1127
IType a0 = NullableType.GetUnderlyingType(lhs.Type);
1128
if (TryConvert(ref rhs, a0)) {
1129
return BinaryOperatorResolveResult(a0, lhs, BinaryOperatorType.NullCoalescing, rhs);
1132
if (TryConvert(ref rhs, lhs.Type)) {
1133
return BinaryOperatorResolveResult(lhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs);
1135
if (TryConvert(ref lhs, rhs.Type)) {
1136
return BinaryOperatorResolveResult(rhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs);
1138
return new ErrorResolveResult(lhs.Type);
1144
#region Get user-defined operator candidates
1145
IEnumerable<IParameterizedMember> GetUserDefinedOperatorCandidates(IType type, string operatorName)
1147
if (operatorName == null)
1148
return EmptyList<IMethod>.Instance;
1149
TypeCode c = ReflectionHelper.GetTypeCode(type);
1150
if (TypeCode.Boolean <= c && c <= TypeCode.Decimal || c == TypeCode.String) {
1151
// The .NET framework contains some of C#'s built-in operators as user-defined operators.
1152
// However, we must not use those as user-defined operators (we would skip numeric promotion).
1153
return EmptyList<IMethod>.Instance;
1155
// C# 4.0 spec: Ā§7.3.5 Candidate user-defined operators
1156
var operators = type.GetMethods(m => m.IsOperator && m.Name == operatorName).ToList();
1157
LiftUserDefinedOperators(operators);
1161
void LiftUserDefinedOperators(List<IMethod> operators)
1163
int nonLiftedMethodCount = operators.Count;
1164
// Construct lifted operators
1165
for (int i = 0; i < nonLiftedMethodCount; i++) {
1166
var liftedMethod = LiftUserDefinedOperator(operators[i]);
1167
if (liftedMethod != null)
1168
operators.Add(liftedMethod);
1172
LiftedUserDefinedOperator LiftUserDefinedOperator(IMethod m)
1174
if (IsComparisonOperator(m)) {
1175
if (!m.ReturnType.Equals(compilation.FindType(KnownTypeCode.Boolean)))
1176
return null; // cannot lift this operator
1178
if (!NullableType.IsNonNullableValueType(m.ReturnType))
1179
return null; // cannot lift this operator
1181
for (int i = 0; i < m.Parameters.Count; i++) {
1182
if (!NullableType.IsNonNullableValueType(m.Parameters[i].Type))
1183
return null; // cannot lift this operator
1185
return new LiftedUserDefinedOperator(m);
1188
static bool IsComparisonOperator(IMethod m)
1190
var type = OperatorDeclaration.GetOperatorType(m.Name);
1191
return type.HasValue && type.Value.IsComparisonOperator();
1194
sealed class LiftedUserDefinedOperator : SpecializedMethod, OverloadResolution.ILiftedOperator
1196
internal readonly IParameterizedMember nonLiftedOperator;
1198
public LiftedUserDefinedOperator(IMethod nonLiftedMethod)
1199
: base(nonLiftedMethod, TypeParameterSubstitution.Identity)
1201
this.nonLiftedOperator = nonLiftedMethod;
1202
var substitution = new MakeNullableVisitor(nonLiftedMethod.Compilation);
1203
this.Parameters = base.CreateParameters(substitution);
1204
// Comparison operators keep the 'bool' return type even when lifted.
1205
if (IsComparisonOperator(nonLiftedMethod))
1206
this.ReturnType = nonLiftedMethod.ReturnType;
1208
this.ReturnType = nonLiftedMethod.ReturnType.AcceptVisitor(substitution);
1211
public IList<IParameter> NonLiftedParameters {
1212
get { return nonLiftedOperator.Parameters; }
1215
public override bool Equals(object obj)
1217
LiftedUserDefinedOperator op = obj as LiftedUserDefinedOperator;
1218
return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator);
1221
public override int GetHashCode()
1223
return nonLiftedOperator.GetHashCode() ^ 0x7191254;
1227
sealed class MakeNullableVisitor : TypeVisitor
1229
readonly ICompilation compilation;
1231
public MakeNullableVisitor(ICompilation compilation)
1233
this.compilation = compilation;
1236
public override IType VisitTypeDefinition(ITypeDefinition type)
1238
return NullableType.Create(compilation, type);
1241
public override IType VisitTypeParameter(ITypeParameter type)
1243
return NullableType.Create(compilation, type);
1246
public override IType VisitParameterizedType(ParameterizedType type)
1248
return NullableType.Create(compilation, type);
1251
public override IType VisitOtherType(IType type)
1253
return NullableType.Create(compilation, type);
1257
ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r, System.Linq.Expressions.ExpressionType operatorType)
1259
if (r.BestCandidateErrors != OverloadResolutionErrors.None)
1260
return r.CreateResolveResult(null);
1261
IMethod method = (IMethod)r.BestCandidate;
1262
return new OperatorResolveResult(method.ReturnType, operatorType, method,
1263
isLiftedOperator: method is OverloadResolution.ILiftedOperator,
1264
operands: r.GetArgumentsWithConversions());
1269
bool TryConvert(ref ResolveResult rr, IType targetType)
1271
Conversion c = conversions.ImplicitConversion(rr, targetType);
1273
rr = Convert(rr, targetType, c);
1280
ResolveResult Convert(ResolveResult rr, IType targetType)
1282
return Convert(rr, targetType, conversions.ImplicitConversion(rr, targetType));
1285
ResolveResult Convert(ResolveResult rr, IType targetType, Conversion c)
1287
if (c == Conversion.IdentityConversion)
1289
else if (rr.IsCompileTimeConstant && c != Conversion.None && !c.IsUserDefined)
1290
return ResolveCast(targetType, rr);
1292
return new ConversionResolveResult(targetType, rr, c, checkForOverflow);
1295
public ResolveResult ResolveCast(IType targetType, ResolveResult expression)
1297
// C# 4.0 spec: Ā§7.7.6 Cast expressions
1298
Conversion c = conversions.ExplicitConversion(expression, targetType);
1299
if (expression.IsCompileTimeConstant && !c.IsUserDefined) {
1300
TypeCode code = ReflectionHelper.GetTypeCode(targetType);
1301
if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) {
1303
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
1304
} catch (OverflowException) {
1305
return new ErrorResolveResult(targetType);
1306
} catch (InvalidCastException) {
1307
return new ErrorResolveResult(targetType);
1309
} else if (code == TypeCode.String) {
1310
if (expression.ConstantValue == null || expression.ConstantValue is string)
1311
return new ConstantResolveResult(targetType, expression.ConstantValue);
1313
return new ErrorResolveResult(targetType);
1314
} else if (targetType.Kind == TypeKind.Enum) {
1315
code = ReflectionHelper.GetTypeCode(GetEnumUnderlyingType(targetType));
1316
if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expression.ConstantValue != null) {
1318
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
1319
} catch (OverflowException) {
1320
return new ErrorResolveResult(targetType);
1321
} catch (InvalidCastException) {
1322
return new ErrorResolveResult(targetType);
1327
return new ConversionResolveResult(targetType, expression, c, checkForOverflow);
1330
internal object CSharpPrimitiveCast(TypeCode targetType, object input)
1332
return Utils.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow);
1335
ResolveResult CheckErrorAndResolveUncheckedCast(IType targetType, ResolveResult expression)
1337
if (expression.IsError)
1340
return WithCheckForOverflow(false).ResolveCast(targetType, expression);
1344
#region ResolveSimpleName
1345
public ResolveResult ResolveSimpleName(string identifier, IList<IType> typeArguments, bool isInvocationTarget = false)
1347
// C# 4.0 spec: Ā§7.6.2 Simple Names
1349
return LookupSimpleNameOrTypeName(
1350
identifier, typeArguments,
1351
isInvocationTarget ? NameLookupMode.InvocationTarget : NameLookupMode.Expression);
1354
public ResolveResult LookupSimpleNameOrTypeName(string identifier, IList<IType> typeArguments, NameLookupMode lookupMode)
1356
// C# 4.0 spec: Ā§3.8 Namespace and type names; Ā§7.6.2 Simple Names
1358
if (identifier == null)
1359
throw new ArgumentNullException("identifier");
1360
if (typeArguments == null)
1361
throw new ArgumentNullException("typeArguments");
1363
int k = typeArguments.Count;
1366
if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) {
1367
// Look in local variables
1368
foreach (IVariable v in this.LocalVariables) {
1369
if (v.Name == identifier) {
1370
return new LocalResolveResult(v);
1373
// Look in parameters of current method
1374
IParameterizedMember parameterizedMember = this.CurrentMember as IParameterizedMember;
1375
if (parameterizedMember != null) {
1376
foreach (IParameter p in parameterizedMember.Parameters) {
1377
if (p.Name == identifier) {
1378
return new LocalResolveResult(p);
1384
// look in type parameters of current method
1385
IMethod m = this.CurrentMember as IMethod;
1387
foreach (ITypeParameter tp in m.TypeParameters) {
1388
if (tp.Name == identifier)
1389
return new TypeResolveResult(tp);
1394
bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument));
1396
ResolveResult r = null;
1397
if (currentTypeDefinitionCache != null) {
1398
Dictionary<string, ResolveResult> cache = null;
1399
bool foundInCache = false;
1401
switch (lookupMode) {
1402
case NameLookupMode.Expression:
1403
cache = currentTypeDefinitionCache.SimpleNameLookupCacheExpression;
1405
case NameLookupMode.InvocationTarget:
1406
cache = currentTypeDefinitionCache.SimpleNameLookupCacheInvocationTarget;
1408
case NameLookupMode.Type:
1409
cache = currentTypeDefinitionCache.SimpleTypeLookupCache;
1412
if (cache != null) {
1414
foundInCache = cache.TryGetValue(identifier, out r);
1418
r = (r != null ? r.ShallowClone() : null);
1420
r = LookInCurrentType(identifier, typeArguments, lookupMode, parameterizeResultType);
1421
if (cache != null) {
1422
// also cache missing members (r==null)
1424
cache[identifier] = r;
1431
if (context.CurrentUsingScope == null) {
1432
// If no using scope was specified, we still need to look in the global namespace:
1433
r = LookInUsingScopeNamespace(null, compilation.RootNamespace, identifier, typeArguments, parameterizeResultType);
1435
if (k == 0 && lookupMode != NameLookupMode.TypeInUsingDeclaration) {
1436
if (context.CurrentUsingScope.ResolveCache.TryGetValue(identifier, out r)) {
1437
r = (r != null ? r.ShallowClone() : null);
1439
r = LookInCurrentUsingScope(identifier, typeArguments, false, false);
1440
context.CurrentUsingScope.ResolveCache.TryAdd(identifier, r);
1443
r = LookInCurrentUsingScope(identifier, typeArguments, lookupMode == NameLookupMode.TypeInUsingDeclaration, parameterizeResultType);
1449
if (typeArguments.Count == 0 && identifier == "dynamic") {
1450
return new TypeResolveResult(SpecialType.Dynamic);
1452
return new UnknownIdentifierResolveResult(identifier, typeArguments.Count);
1456
public bool IsVariableReferenceWithSameType (ResolveResult rr, string identifier, out TypeResolveResult trr)
1458
if (!(rr is MemberResolveResult || rr is LocalResolveResult)) {
1462
trr = LookupSimpleNameOrTypeName (identifier, EmptyList<IType>.Instance, NameLookupMode.Type) as TypeResolveResult;
1463
return trr != null && trr.Type.Equals (rr.Type);
1466
ResolveResult LookInCurrentType(string identifier, IList<IType> typeArguments, NameLookupMode lookupMode, bool parameterizeResultType)
1468
int k = typeArguments.Count;
1469
MemberLookup lookup = CreateMemberLookup(lookupMode);
1470
// look in current type definitions
1471
for (ITypeDefinition t = this.CurrentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) {
1473
// Look for type parameter with that name
1474
var typeParameters = t.TypeParameters;
1475
// Look at all type parameters, including those copied from outer classes,
1476
// so that we can fetch the version with the correct owner.
1477
for (int i = 0; i < typeParameters.Count; i++) {
1478
if (typeParameters[i].Name == identifier)
1479
return new TypeResolveResult(typeParameters[i]);
1483
if (lookupMode == NameLookupMode.BaseTypeReference && t == this.CurrentTypeDefinition) {
1484
// don't look in current type when resolving a base type reference
1489
if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) {
1490
var targetResolveResult = (t == this.CurrentTypeDefinition ? ResolveThisReference() : new TypeResolveResult(t));
1491
r = lookup.Lookup(targetResolveResult, identifier, typeArguments, lookupMode == NameLookupMode.InvocationTarget);
1493
r = lookup.LookupType(t, identifier, typeArguments, parameterizeResultType);
1495
if (!(r is UnknownMemberResolveResult)) // but do return AmbiguousMemberResolveResult
1501
ResolveResult LookInCurrentUsingScope(string identifier, IList<IType> typeArguments, bool isInUsingDeclaration, bool parameterizeResultType)
1503
// look in current namespace definitions
1504
ResolvedUsingScope currentUsingScope = this.CurrentUsingScope;
1505
for (ResolvedUsingScope u = currentUsingScope; u != null; u = u.Parent) {
1506
var resultInNamespace = LookInUsingScopeNamespace(u, u.Namespace, identifier, typeArguments, parameterizeResultType);
1507
if (resultInNamespace != null)
1508
return resultInNamespace;
1509
// then look for aliases:
1510
if (typeArguments.Count == 0) {
1511
if (u.ExternAliases.Contains(identifier)) {
1512
return ResolveExternAlias(identifier);
1514
if (!(isInUsingDeclaration && u == currentUsingScope)) {
1515
foreach (var pair in u.UsingAliases) {
1516
if (pair.Key == identifier) {
1517
return pair.Value.ShallowClone();
1522
// finally, look in the imported namespaces:
1523
if (!(isInUsingDeclaration && u == currentUsingScope)) {
1524
IType firstResult = null;
1525
foreach (var importedNamespace in u.Usings) {
1526
ITypeDefinition def = importedNamespace.GetTypeDefinition(identifier, typeArguments.Count);
1529
if (parameterizeResultType && typeArguments.Count > 0)
1530
resultType = new ParameterizedType(def, typeArguments);
1534
if (firstResult == null || !TopLevelTypeDefinitionIsAccessible(firstResult.GetDefinition())) {
1535
firstResult = resultType;
1536
} else if (TopLevelTypeDefinitionIsAccessible(def)) {
1537
return new AmbiguousTypeResolveResult(firstResult);
1541
if (firstResult != null)
1542
return new TypeResolveResult(firstResult);
1544
// if we didn't find anything: repeat lookup with parent namespace
1549
ResolveResult LookInUsingScopeNamespace(ResolvedUsingScope usingScope, INamespace n, string identifier, IList<IType> typeArguments, bool parameterizeResultType)
1553
// first look for a namespace
1554
int k = typeArguments.Count;
1556
INamespace childNamespace = n.GetChildNamespace(identifier);
1557
if (childNamespace != null) {
1558
if (usingScope != null && usingScope.HasAlias(identifier))
1559
return new AmbiguousTypeResolveResult(new UnknownType(null, identifier));
1560
return new NamespaceResolveResult(childNamespace);
1563
// then look for a type
1564
ITypeDefinition def = n.GetTypeDefinition(identifier, k);
1567
if (parameterizeResultType && k > 0) {
1568
result = new ParameterizedType(def, typeArguments);
1570
if (usingScope != null && usingScope.HasAlias(identifier))
1571
return new AmbiguousTypeResolveResult(result);
1573
return new TypeResolveResult(result);
1578
bool TopLevelTypeDefinitionIsAccessible(ITypeDefinition typeDef)
1580
if (typeDef.IsInternal) {
1581
return typeDef.ParentAssembly.InternalsVisibleTo(compilation.MainAssembly);
1587
/// Looks up an alias (identifier in front of :: operator)
1589
public ResolveResult ResolveAlias(string identifier)
1591
if (identifier == "global")
1592
return new NamespaceResolveResult(compilation.RootNamespace);
1594
for (ResolvedUsingScope n = this.CurrentUsingScope; n != null; n = n.Parent) {
1595
if (n.ExternAliases.Contains(identifier)) {
1596
return ResolveExternAlias(identifier);
1598
foreach (var pair in n.UsingAliases) {
1599
if (pair.Key == identifier) {
1600
return (pair.Value as NamespaceResolveResult) ?? ErrorResult;
1607
ResolveResult ResolveExternAlias(string alias)
1609
INamespace ns = compilation.GetNamespaceForExternAlias(alias);
1611
return new NamespaceResolveResult(ns);
1617
#region ResolveMemberAccess
1618
public ResolveResult ResolveMemberAccess(ResolveResult target, string identifier, IList<IType> typeArguments, NameLookupMode lookupMode = NameLookupMode.Expression)
1620
// C# 4.0 spec: Ā§7.6.4
1622
bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument));
1623
NamespaceResolveResult nrr = target as NamespaceResolveResult;
1625
return ResolveMemberAccessOnNamespace(nrr, identifier, typeArguments, parameterizeResultType);
1628
if (target.Type.Kind == TypeKind.Dynamic)
1629
return new DynamicMemberResolveResult(target, identifier);
1631
MemberLookup lookup = CreateMemberLookup(lookupMode);
1632
ResolveResult result;
1633
switch (lookupMode) {
1634
case NameLookupMode.Expression:
1635
result = lookup.Lookup(target, identifier, typeArguments, isInvocation: false);
1637
case NameLookupMode.InvocationTarget:
1638
result = lookup.Lookup(target, identifier, typeArguments, isInvocation: true);
1640
case NameLookupMode.Type:
1641
case NameLookupMode.TypeInUsingDeclaration:
1642
case NameLookupMode.BaseTypeReference:
1643
result = lookup.LookupType(target.Type, identifier, typeArguments, parameterizeResultType);
1646
throw new NotSupportedException("Invalid value for NameLookupMode");
1648
if (result is UnknownMemberResolveResult) {
1649
// We intentionally use all extension methods here, not just the eligible ones.
1650
// Proper eligibility checking is only possible for the full invocation
1651
// (after we know the remaining arguments).
1652
// The eligibility check in GetExtensionMethods is only intended for code completion.
1653
var extensionMethods = GetExtensionMethods(identifier, typeArguments);
1654
if (extensionMethods.Count > 0) {
1655
return new MethodGroupResolveResult(target, identifier, EmptyList<MethodListWithDeclaringType>.Instance, typeArguments) {
1656
extensionMethods = extensionMethods
1660
MethodGroupResolveResult mgrr = result as MethodGroupResolveResult;
1662
Debug.Assert(mgrr.extensionMethods == null);
1663
// set the values that are necessary to make MethodGroupResolveResult.GetExtensionMethods() work
1664
mgrr.resolver = this;
1670
[Obsolete("Use ResolveMemberAccess() with NameLookupMode.Type instead")]
1671
public ResolveResult ResolveMemberType(ResolveResult target, string identifier, IList<IType> typeArguments)
1673
return ResolveMemberAccess(target, identifier, typeArguments, NameLookupMode.Type);
1676
ResolveResult ResolveMemberAccessOnNamespace(NamespaceResolveResult nrr, string identifier, IList<IType> typeArguments, bool parameterizeResultType)
1678
if (typeArguments.Count == 0) {
1679
INamespace childNamespace = nrr.Namespace.GetChildNamespace(identifier);
1680
if (childNamespace != null)
1681
return new NamespaceResolveResult(childNamespace);
1683
ITypeDefinition def = nrr.Namespace.GetTypeDefinition(identifier, typeArguments.Count);
1685
if (parameterizeResultType && typeArguments.Count > 0)
1686
return new TypeResolveResult(new ParameterizedType(def, typeArguments));
1688
return new TypeResolveResult(def);
1694
/// Creates a MemberLookup instance using this resolver's settings.
1696
public MemberLookup CreateMemberLookup()
1698
ITypeDefinition currentTypeDefinition = this.CurrentTypeDefinition;
1699
bool isInEnumMemberInitializer = this.CurrentMember != null && this.CurrentMember.EntityType == EntityType.Field
1700
&& currentTypeDefinition != null && currentTypeDefinition.Kind == TypeKind.Enum;
1701
return new MemberLookup(currentTypeDefinition, this.Compilation.MainAssembly, isInEnumMemberInitializer);
1705
/// Creates a MemberLookup instance using this resolver's settings.
1707
public MemberLookup CreateMemberLookup(NameLookupMode lookupMode)
1709
if (lookupMode == NameLookupMode.BaseTypeReference && this.CurrentTypeDefinition != null) {
1710
// When looking up a base type reference, treat us as being outside the current type definition
1711
// for accessibility purposes.
1712
// This avoids a stack overflow when referencing a protected class nested inside the base class
1713
// of a parent class. (NameLookupTests.InnerClassInheritingFromProtectedBaseInnerClassShouldNotCauseStackOverflow)
1714
return new MemberLookup(this.CurrentTypeDefinition.DeclaringTypeDefinition, this.Compilation.MainAssembly, false);
1716
return CreateMemberLookup();
1721
#region ResolveIdentifierInObjectInitializer
1722
public ResolveResult ResolveIdentifierInObjectInitializer(string identifier)
1724
MemberLookup memberLookup = CreateMemberLookup();
1725
return memberLookup.Lookup(this.CurrentObjectInitializer, identifier, EmptyList<IType>.Instance, false);
1729
#region GetExtensionMethods
1731
/// Gets all extension methods that are available in the current context.
1733
/// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
1734
/// <param name="typeArguments">Explicitly provided type arguments.
1735
/// An empty list will return all matching extension method definitions;
1736
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
1737
/// with the matching number of type parameters.</param>
1739
/// The results are stored in nested lists because they are grouped by using scope.
1740
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
1741
/// the return value will be
1743
/// new List { all extensions from MoreExtensions },
1744
/// new List { all extensions from SomeExtensions }
1747
public List<List<IMethod>> GetExtensionMethods(string name = null, IList<IType> typeArguments = null)
1749
return GetExtensionMethods(null, name, typeArguments);
1753
/// Gets the extension methods that are called 'name'
1754
/// and are applicable with a first argument type of 'targetType'.
1756
/// <param name="targetType">Type of the 'this' argument</param>
1757
/// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
1758
/// <param name="typeArguments">Explicitly provided type arguments.
1759
/// An empty list will return all matching extension method definitions;
1760
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
1761
/// with the matching number of type parameters.</param>
1762
/// <param name="substituteInferredTypes">
1763
/// Specifies whether to produce a <see cref="SpecializedMethod"/>
1764
/// when type arguments could be inferred from <paramref name="targetType"/>. This parameter
1765
/// is only used for inferred types and has no effect if <paramref name="typeArguments"/> is non-empty.
1768
/// The results are stored in nested lists because they are grouped by using scope.
1769
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
1770
/// the return value will be
1772
/// new List { all extensions from MoreExtensions },
1773
/// new List { all extensions from SomeExtensions }
1776
public List<List<IMethod>> GetExtensionMethods(IType targetType, string name = null, IList<IType> typeArguments = null, bool substituteInferredTypes = false)
1778
List<List<IMethod>> extensionMethodGroups = new List<List<IMethod>>();
1779
foreach (var inputGroup in GetAllExtensionMethods()) {
1780
List<IMethod> outputGroup = new List<IMethod>();
1781
foreach (var method in inputGroup) {
1782
if (name != null && method.Name != name)
1785
IType[] inferredTypes;
1786
if (typeArguments != null && typeArguments.Count > 0) {
1787
if (method.TypeParameters.Count != typeArguments.Count)
1789
var sm = method.Specialize(new TypeParameterSubstitution(null, typeArguments));
1790
if (IsEligibleExtensionMethod(compilation, conversions, targetType, sm, false, out inferredTypes))
1791
outputGroup.Add(sm);
1793
if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) {
1794
if (substituteInferredTypes && inferredTypes != null) {
1795
outputGroup.Add(method.Specialize(new TypeParameterSubstitution(null, inferredTypes)));
1797
outputGroup.Add(method);
1802
if (outputGroup.Count > 0)
1803
extensionMethodGroups.Add(outputGroup);
1805
return extensionMethodGroups;
1809
/// Checks whether the specified extension method is eligible on the target type.
1811
/// <param name="targetType">Target type that is passed as first argument to the extension method.</param>
1812
/// <param name="method">The extension method.</param>
1813
/// <param name="useTypeInference">Whether to perform type inference for the method.
1814
/// Use <c>false</c> if <paramref name="method"/> is already parameterized (e.g. when type arguments were given explicitly).
1815
/// Otherwise, use <c>true</c>.
1817
/// <param name="outInferredTypes">If the method is generic and <paramref name="useTypeInference"/> is <c>true</c>,
1818
/// and at least some of the type arguments could be inferred, this parameter receives the inferred type arguments.
1819
/// Since only the type for the first parameter is considered, not all type arguments may be inferred.
1820
/// If an array is returned, any slot with an uninferred type argument will be set to the method's
1821
/// corresponding type parameter.
1823
public static bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
1825
if (targetType == null)
1826
throw new ArgumentNullException("targetType");
1828
throw new ArgumentNullException("method");
1829
var compilation = method.Compilation;
1830
return IsEligibleExtensionMethod(compilation, CSharpConversions.Get(compilation), targetType, method, useTypeInference, out outInferredTypes);
1833
static bool IsEligibleExtensionMethod(ICompilation compilation, CSharpConversions conversions, IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
1835
outInferredTypes = null;
1836
if (targetType == null)
1838
if (method.Parameters.Count == 0)
1840
IType thisParameterType = method.Parameters[0].Type;
1841
if (useTypeInference && method.TypeParameters.Count > 0) {
1842
// We need to infer type arguments from targetType:
1843
TypeInference ti = new TypeInference(compilation, conversions);
1844
ResolveResult[] arguments = { new ResolveResult(targetType) };
1845
IType[] parameterTypes = { method.Parameters[0].Type };
1847
var inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out success);
1848
var substitution = new TypeParameterSubstitution(null, inferredTypes);
1849
// Validate that the types that could be inferred (aren't unknown) satisfy the constraints:
1850
bool hasInferredTypes = false;
1851
for (int i = 0; i < inferredTypes.Length; i++) {
1852
if (inferredTypes[i].Kind != TypeKind.Unknown && inferredTypes[i].Kind != TypeKind.UnboundTypeArgument) {
1853
hasInferredTypes = true;
1854
if (!OverloadResolution.ValidateConstraints(method.TypeParameters[i], inferredTypes[i], substitution, conversions))
1857
inferredTypes[i] = method.TypeParameters[i]; // do not substitute types that could not be inferred
1860
if (hasInferredTypes)
1861
outInferredTypes = inferredTypes;
1862
thisParameterType = thisParameterType.AcceptVisitor(substitution);
1864
Conversion c = conversions.ImplicitConversion(targetType, thisParameterType);
1865
return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion);
1869
/// Gets all extension methods available in the current using scope.
1870
/// This list includes unaccessible
1872
IList<List<IMethod>> GetAllExtensionMethods()
1874
var currentUsingScope = context.CurrentUsingScope;
1875
if (currentUsingScope == null)
1876
return EmptyList<List<IMethod>>.Instance;
1877
List<List<IMethod>> extensionMethodGroups = LazyInit.VolatileRead(ref currentUsingScope.AllExtensionMethods);
1878
if (extensionMethodGroups != null) {
1879
return extensionMethodGroups;
1881
extensionMethodGroups = new List<List<IMethod>>();
1883
for (ResolvedUsingScope scope = currentUsingScope; scope != null; scope = scope.Parent) {
1884
INamespace ns = scope.Namespace;
1886
m = GetExtensionMethods(ns).ToList();
1888
extensionMethodGroups.Add(m);
1893
.SelectMany(importedNamespace => GetExtensionMethods(importedNamespace))
1896
extensionMethodGroups.Add(m);
1898
return LazyInit.GetOrSet(ref currentUsingScope.AllExtensionMethods, extensionMethodGroups);
1901
IEnumerable<IMethod> GetExtensionMethods(INamespace ns)
1903
// TODO: maybe make this a property on INamespace?
1906
where c.IsStatic && c.HasExtensionMethods && c.TypeParameters.Count == 0
1908
where m.IsExtensionMethod
1913
#region ResolveInvocation
1915
IList<ResolveResult> AddArgumentNamesIfNecessary(ResolveResult[] arguments, string[] argumentNames) {
1916
if (argumentNames == null) {
1920
var result = new ResolveResult[arguments.Length];
1921
for (int i = 0; i < arguments.Length; i++) {
1922
result[i] = (argumentNames[i] != null ? new NamedArgumentResolveResult(argumentNames[i], arguments[i]) : arguments[i]);
1928
private ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames, bool allowOptionalParameters)
1930
// C# 4.0 spec: Ā§7.6.5
1932
if (target.Type.Kind == TypeKind.Dynamic) {
1933
return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames));
1936
bool isDynamic = arguments.Any(a => a.Type.Kind == TypeKind.Dynamic);
1937
MethodGroupResolveResult mgrr = target as MethodGroupResolveResult;
1940
// If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method.
1941
var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray());
1942
var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList();
1944
if (applicableMethods.Count > 1) {
1945
ResolveResult actualTarget;
1946
if (applicableMethods.All(x => x.Method.IsStatic) && !(mgrr.TargetResult is TypeResolveResult))
1947
actualTarget = new TypeResolveResult(mgrr.TargetType);
1949
actualTarget = mgrr.TargetResult;
1951
var l = new List<MethodListWithDeclaringType>();
1952
foreach (var m in applicableMethods) {
1953
if (l.Count == 0 || l[l.Count - 1].DeclaringType != m.DeclaringType)
1954
l.Add(new MethodListWithDeclaringType(m.DeclaringType));
1955
l[l.Count - 1].Add(m.Method);
1957
return new DynamicInvocationResolveResult(new MethodGroupResolveResult(actualTarget, mgrr.MethodName, l, mgrr.TypeArguments), DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames));
1961
OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions, allowOptionalParameters: allowOptionalParameters);
1962
if (or.BestCandidate != null) {
1963
if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult))
1964
return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType), returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
1966
return or.CreateResolveResult(mgrr.TargetResult, returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
1968
// No candidate found at all (not even an inapplicable one).
1969
// This can happen with empty method groups (as sometimes used with extension methods)
1970
return new UnknownMethodResolveResult(
1971
mgrr.TargetType, mgrr.MethodName, mgrr.TypeArguments, CreateParameters(arguments, argumentNames));
1974
UnknownMemberResolveResult umrr = target as UnknownMemberResolveResult;
1976
return new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames));
1978
UnknownIdentifierResolveResult uirr = target as UnknownIdentifierResolveResult;
1979
if (uirr != null && CurrentTypeDefinition != null) {
1980
return new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList<IType>.Instance, CreateParameters(arguments, argumentNames));
1982
IMethod invokeMethod = target.Type.GetDelegateInvokeMethod();
1983
if (invokeMethod != null) {
1984
OverloadResolution or = CreateOverloadResolution(arguments, argumentNames);
1985
or.AddCandidate(invokeMethod);
1986
return new CSharpInvocationResolveResult(
1987
target, invokeMethod, //invokeMethod.ReturnType.Resolve(context),
1988
or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors,
1989
isExpandedForm: or.BestCandidateIsExpandedForm,
1990
isDelegateInvocation: true,
1991
argumentToParameterMap: or.GetArgumentToParameterMap(),
1992
returnTypeOverride: isDynamic ? SpecialType.Dynamic : null);
1998
/// Resolves an invocation.
2000
/// <param name="target">The target of the invocation. Usually a MethodGroupResolveResult.</param>
2001
/// <param name="arguments">
2002
/// Arguments passed to the method.
2003
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2005
/// <param name="argumentNames">
2006
/// The argument names. Pass the null string for positional arguments.
2008
/// <returns>InvocationResolveResult or UnknownMethodResolveResult</returns>
2009
public ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
2011
return ResolveInvocation(target, arguments, argumentNames, allowOptionalParameters: true);
2014
List<IParameter> CreateParameters(ResolveResult[] arguments, string[] argumentNames)
2016
List<IParameter> list = new List<IParameter>();
2017
if (argumentNames == null) {
2018
argumentNames = new string[arguments.Length];
2020
if (argumentNames.Length != arguments.Length)
2021
throw new ArgumentException();
2022
argumentNames = (string[])argumentNames.Clone();
2024
for (int i = 0; i < arguments.Length; i++) {
2025
// invent argument names where necessary:
2026
if (argumentNames[i] == null) {
2027
string newArgumentName = GuessParameterName(arguments[i]);
2028
if (argumentNames.Contains(newArgumentName)) {
2029
// disambiguate argument name (e.g. add a number)
2033
newName = newArgumentName + num.ToString();
2035
} while(argumentNames.Contains(newName));
2036
newArgumentName = newName;
2038
argumentNames[i] = newArgumentName;
2041
// create the parameter:
2042
ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
2044
list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], isRef: brrr.IsRef, isOut: brrr.IsOut));
2046
// argument might be a lambda or delegate type, so we have to try to guess the delegate type
2047
IType type = arguments[i].Type;
2048
if (type.Kind == TypeKind.Null || type.Kind == TypeKind.Unknown) {
2049
list.Add(new DefaultParameter(compilation.FindType(KnownTypeCode.Object), argumentNames[i]));
2051
list.Add(new DefaultParameter(type, argumentNames[i]));
2058
static string GuessParameterName(ResolveResult rr)
2060
MemberResolveResult mrr = rr as MemberResolveResult;
2062
return mrr.Member.Name;
2064
UnknownMemberResolveResult umrr = rr as UnknownMemberResolveResult;
2066
return umrr.MemberName;
2068
MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult;
2070
return mgrr.MethodName;
2072
LocalResolveResult vrr = rr as LocalResolveResult;
2074
return MakeParameterName(vrr.Variable.Name);
2076
if (rr.Type.Kind != TypeKind.Unknown && !string.IsNullOrEmpty(rr.Type.Name)) {
2077
return MakeParameterName(rr.Type.Name);
2083
static string MakeParameterName(string variableName)
2085
if (string.IsNullOrEmpty(variableName))
2087
if (variableName.Length > 1 && variableName[0] == '_')
2088
variableName = variableName.Substring(1);
2089
return char.ToLower(variableName[0]) + variableName.Substring(1);
2092
OverloadResolution CreateOverloadResolution(ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null)
2094
var or = new OverloadResolution(compilation, arguments, argumentNames, typeArguments, conversions);
2095
or.CheckForOverflow = checkForOverflow;
2100
#region ResolveIndexer
2102
/// Resolves an indexer access.
2104
/// <param name="target">Target expression.</param>
2105
/// <param name="arguments">
2106
/// Arguments passed to the indexer.
2107
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2109
/// <param name="argumentNames">
2110
/// The argument names. Pass the null string for positional arguments.
2112
/// <returns>ArrayAccessResolveResult, InvocationResolveResult, or ErrorResolveResult</returns>
2113
public ResolveResult ResolveIndexer(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
2115
switch (target.Type.Kind) {
2116
case TypeKind.Dynamic:
2117
return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames));
2119
case TypeKind.Array:
2120
case TypeKind.Pointer:
2121
// Ā§7.6.6.1 Array access / Ā§18.5.3 Pointer element access
2122
AdjustArrayAccessArguments(arguments);
2123
return new ArrayAccessResolveResult(((TypeWithElementType)target.Type).ElementType, target, arguments);
2126
// Ā§7.6.6.2 Indexer access
2128
MemberLookup lookup = CreateMemberLookup();
2129
var indexers = lookup.LookupIndexers(target.Type);
2131
if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) {
2132
// If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable indexer.
2133
var or2 = CreateOverloadResolution(arguments, argumentNames, null);
2134
var applicableIndexers = indexers.SelectMany(x => x).Where(m => OverloadResolution.IsApplicable(or2.AddCandidate(m))).ToList();
2136
if (applicableIndexers.Count > 1) {
2137
return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, AddArgumentNamesIfNecessary(arguments, argumentNames));
2141
OverloadResolution or = CreateOverloadResolution(arguments, argumentNames);
2142
or.AddMethodLists(indexers);
2143
if (or.BestCandidate != null) {
2144
return or.CreateResolveResult(target);
2151
/// Converts all arguments to int,uint,long or ulong.
2153
void AdjustArrayAccessArguments(ResolveResult[] arguments)
2155
for (int i = 0; i < arguments.Length; i++) {
2156
if (!(TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int32)) ||
2157
TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt32)) ||
2158
TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int64)) ||
2159
TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt64))))
2161
// conversion failed
2162
arguments[i] = Convert(arguments[i], compilation.FindType(KnownTypeCode.Int32), Conversion.None);
2168
#region ResolveObjectCreation
2170
/// Resolves an object creation.
2172
/// <param name="type">Type of the object to create.</param>
2173
/// <param name="arguments">
2174
/// Arguments passed to the constructor.
2175
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2177
/// <param name="argumentNames">
2178
/// The argument names. Pass the null string for positional arguments.
2180
/// <param name="allowProtectedAccess">
2181
/// Whether to allow calling protected constructors.
2182
/// This should be false except when resolving constructor initializers.
2184
/// <param name="initializerStatements">
2185
/// Statements for Objects/Collections initializer.
2186
/// <see cref="InvocationResolveResult.InitializerStatements"/>
2188
/// <returns>InvocationResolveResult or ErrorResolveResult</returns>
2189
public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList<ResolveResult> initializerStatements = null)
2191
if (type.Kind == TypeKind.Delegate && arguments.Length == 1) {
2192
ResolveResult input = arguments[0];
2193
IMethod invoke = input.Type.GetDelegateInvokeMethod();
2194
if (invoke != null) {
2195
input = new MethodGroupResolveResult(
2197
methods: new[] { new MethodListWithDeclaringType(invoke.DeclaringType) { invoke } },
2198
typeArguments: EmptyList<IType>.Instance
2201
return Convert(input, type);
2203
OverloadResolution or = CreateOverloadResolution(arguments, argumentNames);
2204
MemberLookup lookup = CreateMemberLookup();
2205
var allApplicable = (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic) ? new List<IMethod>() : null);
2206
foreach (IMethod ctor in type.GetConstructors()) {
2207
if (lookup.IsAccessible(ctor, allowProtectedAccess)) {
2208
var orErrors = or.AddCandidate(ctor);
2209
if (allApplicable != null && OverloadResolution.IsApplicable(orErrors))
2210
allApplicable.Add(ctor);
2213
or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible);
2216
if (allApplicable != null && allApplicable.Count > 1) {
2217
// If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable constructor.
2218
return new DynamicInvocationResolveResult(new MethodGroupResolveResult(null, allApplicable[0].Name, new[] { new MethodListWithDeclaringType(type, allApplicable) }, null), DynamicInvocationType.ObjectCreation, AddArgumentNamesIfNecessary(arguments, argumentNames), initializerStatements);
2221
if (or.BestCandidate != null) {
2222
return or.CreateResolveResult(null, initializerStatements);
2224
return new ErrorResolveResult(type);
2229
#region ResolveSizeOf
2231
/// Resolves 'sizeof(type)'.
2233
public ResolveResult ResolveSizeOf(IType type)
2235
IType int32 = compilation.FindType(KnownTypeCode.Int32);
2237
var typeForConstant = (type.Kind == TypeKind.Enum) ? type.GetDefinition().EnumUnderlyingType : type;
2239
switch (ReflectionHelper.GetTypeCode(typeForConstant)) {
2240
case TypeCode.Boolean:
2241
case TypeCode.SByte:
2246
case TypeCode.Int16:
2247
case TypeCode.UInt16:
2250
case TypeCode.Int32:
2251
case TypeCode.UInt32:
2252
case TypeCode.Single:
2255
case TypeCode.Int64:
2256
case TypeCode.UInt64:
2257
case TypeCode.Double:
2261
return new SizeOfResolveResult(int32, type, size);
2265
#region Resolve This/Base Reference
2267
/// Resolves 'this'.
2269
public ResolveResult ResolveThisReference()
2271
ITypeDefinition t = CurrentTypeDefinition;
2273
if (t.TypeParameterCount != 0) {
2274
// Self-parameterize the type
2275
return new ThisResolveResult(new ParameterizedType(t, t.TypeParameters));
2277
return new ThisResolveResult(t);
2284
/// Resolves 'base'.
2286
public ResolveResult ResolveBaseReference()
2288
ITypeDefinition t = CurrentTypeDefinition;
2290
foreach (IType baseType in t.DirectBaseTypes) {
2291
if (baseType.Kind != TypeKind.Unknown && baseType.Kind != TypeKind.Interface) {
2292
return new ThisResolveResult(baseType, causesNonVirtualInvocation: true);
2300
#region ResolveConditional
2302
/// Converts the input to <c>bool</c> using the rules for boolean expressions.
2303
/// That is, <c>operator true</c> is used if a regular conversion to <c>bool</c> is not possible.
2305
public ResolveResult ResolveCondition(ResolveResult input)
2308
throw new ArgumentNullException("input");
2309
IType boolean = compilation.FindType(KnownTypeCode.Boolean);
2310
Conversion c = conversions.ImplicitConversion(input, boolean);
2312
var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault();
2313
if (opTrue != null) {
2314
c = Conversion.UserDefinedConversion(opTrue, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
2317
return Convert(input, boolean, c);
2321
/// Converts the negated input to <c>bool</c> using the rules for boolean expressions.
2322
/// Computes <c>!(bool)input</c> if the implicit cast to bool is valid; otherwise
2323
/// computes <c>input.operator false()</c>.
2325
public ResolveResult ResolveConditionFalse(ResolveResult input)
2328
throw new ArgumentNullException("input");
2329
IType boolean = compilation.FindType(KnownTypeCode.Boolean);
2330
Conversion c = conversions.ImplicitConversion(input, boolean);
2332
var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault();
2333
if (opFalse != null) {
2334
c = Conversion.UserDefinedConversion(opFalse, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
2335
return Convert(input, boolean, c);
2338
return ResolveUnaryOperator(UnaryOperatorType.Not, Convert(input, boolean, c));
2341
public ResolveResult ResolveConditional(ResolveResult condition, ResolveResult trueExpression, ResolveResult falseExpression)
2343
// C# 4.0 spec Ā§7.14: Conditional operator
2347
if (trueExpression.Type.Kind == TypeKind.Dynamic || falseExpression.Type.Kind == TypeKind.Dynamic) {
2348
resultType = SpecialType.Dynamic;
2349
isValid = TryConvert(ref trueExpression, resultType) & TryConvert(ref falseExpression, resultType);
2350
} else if (HasType(trueExpression) && HasType(falseExpression)) {
2351
Conversion t2f = conversions.ImplicitConversion(trueExpression, falseExpression.Type);
2352
Conversion f2t = conversions.ImplicitConversion(falseExpression, trueExpression.Type);
2353
// The operator is valid:
2354
// a) if there's a conversion in one direction but not the other
2355
// b) if there are conversions in both directions, and the types are equivalent
2356
if (IsBetterConditionalConversion(t2f, f2t)) {
2357
resultType = falseExpression.Type;
2359
trueExpression = Convert(trueExpression, resultType, t2f);
2360
} else if (IsBetterConditionalConversion(f2t, t2f)) {
2361
resultType = trueExpression.Type;
2363
falseExpression = Convert(falseExpression, resultType, f2t);
2365
resultType = trueExpression.Type;
2366
isValid = trueExpression.Type.Equals(falseExpression.Type);
2368
} else if (HasType(trueExpression)) {
2369
resultType = trueExpression.Type;
2370
isValid = TryConvert(ref falseExpression, resultType);
2371
} else if (HasType(falseExpression)) {
2372
resultType = falseExpression.Type;
2373
isValid = TryConvert(ref trueExpression, resultType);
2377
condition = ResolveCondition(condition);
2379
if (condition.IsCompileTimeConstant && trueExpression.IsCompileTimeConstant && falseExpression.IsCompileTimeConstant) {
2380
bool? val = condition.ConstantValue as bool?;
2382
return trueExpression;
2383
else if (val == false)
2384
return falseExpression;
2386
return new OperatorResolveResult(resultType, System.Linq.Expressions.ExpressionType.Conditional,
2387
condition, trueExpression, falseExpression);
2389
return new ErrorResolveResult(resultType);
2393
bool IsBetterConditionalConversion(Conversion c1, Conversion c2)
2395
// Valid is better than ImplicitConstantExpressionConversion is better than invalid
2398
if (c1 != Conversion.ImplicitConstantExpressionConversion && c2 == Conversion.ImplicitConstantExpressionConversion)
2403
bool HasType(ResolveResult r)
2405
return r.Type.Kind != TypeKind.Unknown && r.Type.Kind != TypeKind.Null;
2409
#region ResolvePrimitive
2410
public ResolveResult ResolvePrimitive(object value)
2412
if (value == null) {
2413
return new ResolveResult(SpecialType.NullType);
2415
TypeCode typeCode = Type.GetTypeCode(value.GetType());
2416
IType type = compilation.FindType(typeCode);
2417
return new ConstantResolveResult(type, value);
2422
#region ResolveDefaultValue
2423
public ResolveResult ResolveDefaultValue(IType type)
2425
return new ConstantResolveResult(type, GetDefaultValue(type));
2428
public static object GetDefaultValue(IType type)
2430
switch (ReflectionHelper.GetTypeCode(type)) {
2431
case TypeCode.Boolean:
2435
case TypeCode.SByte:
2439
case TypeCode.Int16:
2441
case TypeCode.UInt16:
2443
case TypeCode.Int32:
2445
case TypeCode.UInt32:
2447
case TypeCode.Int64:
2449
case TypeCode.UInt64:
2451
case TypeCode.Single:
2453
case TypeCode.Double:
2455
case TypeCode.Decimal:
2463
#region ResolveArrayCreation
2465
/// Resolves an array creation.
2467
/// <param name="elementType">
2468
/// The array element type.
2469
/// Pass null to resolve an implicitly-typed array creation.
2471
/// <param name="sizeArguments">
2472
/// The size arguments.
2473
/// The length of this array will be used as the number of dimensions of the array type.
2474
/// Negative values will be treated as errors.
2476
/// <param name="initializerElements">
2477
/// The initializer elements. May be null if no array initializer was specified.
2478
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2480
public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, int[] sizeArguments, ResolveResult[] initializerElements = null)
2482
ResolveResult[] sizeArgResults = new ResolveResult[sizeArguments.Length];
2483
for (int i = 0; i < sizeArguments.Length; i++) {
2484
if (sizeArguments[i] < 0)
2485
sizeArgResults[i] = ErrorResolveResult.UnknownError;
2487
sizeArgResults[i] = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), sizeArguments[i]);
2489
return ResolveArrayCreation(elementType, sizeArgResults, initializerElements);
2493
/// Resolves an array creation.
2495
/// <param name="elementType">
2496
/// The array element type.
2497
/// Pass null to resolve an implicitly-typed array creation.
2499
/// <param name="sizeArguments">
2500
/// The size arguments.
2501
/// The length of this array will be used as the number of dimensions of the array type.
2502
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2504
/// <param name="initializerElements">
2505
/// The initializer elements. May be null if no array initializer was specified.
2506
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2508
public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, ResolveResult[] sizeArguments, ResolveResult[] initializerElements = null)
2510
int dimensions = sizeArguments.Length;
2511
if (dimensions == 0)
2512
throw new ArgumentException("sizeArguments.Length must not be 0");
2513
if (elementType == null) {
2514
TypeInference typeInference = new TypeInference(compilation, conversions);
2516
elementType = typeInference.GetBestCommonType(initializerElements, out success);
2518
IType arrayType = new ArrayType(compilation, elementType, dimensions);
2520
AdjustArrayAccessArguments(sizeArguments);
2522
if (initializerElements != null) {
2523
for (int i = 0; i < initializerElements.Length; i++) {
2524
initializerElements[i] = Convert(initializerElements[i], elementType);
2527
return new ArrayCreateResolveResult(arrayType, sizeArguments, initializerElements);
2531
public ResolveResult ResolveTypeOf(IType referencedType)
2533
return new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), referencedType);
2536
#region ResolveAssignment
2537
public ResolveResult ResolveAssignment(AssignmentOperatorType op, ResolveResult lhs, ResolveResult rhs)
2539
var linqOp = AssignmentExpression.GetLinqNodeType(op, this.CheckForOverflow);
2540
var bop = AssignmentExpression.GetCorrespondingBinaryOperator(op);
2542
return new OperatorResolveResult(lhs.Type, linqOp, lhs, this.Convert(rhs, lhs.Type));
2544
ResolveResult bopResult = ResolveBinaryOperator(bop.Value, lhs, rhs);
2545
OperatorResolveResult opResult = bopResult as OperatorResolveResult;
2546
if (opResult == null || opResult.Operands.Count != 2)
2548
return new OperatorResolveResult(lhs.Type, linqOp, opResult.UserDefinedOperatorMethod, opResult.IsLiftedOperator,
2549
new [] { lhs, opResult.Operands[1] });