1
ļ»æ// Copyright (c) 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;
44
static readonly ResolveResult DynamicResult = new ResolveResult(SpecialType.Dynamic);
45
static readonly ResolveResult NullResult = new ResolveResult(SpecialType.NullType);
47
readonly ICompilation compilation;
48
internal readonly CSharpConversions conversions;
49
readonly CSharpTypeResolveContext context;
50
readonly bool checkForOverflow;
51
readonly bool isWithinLambdaExpression;
54
public CSharpResolver(ICompilation compilation)
56
if (compilation == null)
57
throw new ArgumentNullException("compilation");
58
this.compilation = compilation;
59
this.conversions = CSharpConversions.Get(compilation);
60
this.context = new CSharpTypeResolveContext(compilation.MainAssembly);
63
public CSharpResolver(CSharpTypeResolveContext context)
66
throw new ArgumentNullException("context");
67
this.compilation = context.Compilation;
68
this.conversions = CSharpConversions.Get(compilation);
69
this.context = context;
70
if (context.CurrentTypeDefinition != null)
71
currentTypeDefinitionCache = new TypeDefinitionCache(context.CurrentTypeDefinition);
74
private CSharpResolver(ICompilation compilation, CSharpConversions conversions, CSharpTypeResolveContext context, bool checkForOverflow, bool isWithinLambdaExpression, TypeDefinitionCache currentTypeDefinitionCache, ImmutableStack<IVariable> localVariableStack, ObjectInitializerContext objectInitializerStack)
76
this.compilation = compilation;
77
this.conversions = conversions;
78
this.context = context;
79
this.checkForOverflow = checkForOverflow;
80
this.isWithinLambdaExpression = isWithinLambdaExpression;
81
this.currentTypeDefinitionCache = currentTypeDefinitionCache;
82
this.localVariableStack = localVariableStack;
83
this.objectInitializerStack = objectInitializerStack;
89
/// Gets the compilation used by the resolver.
91
public ICompilation Compilation {
92
get { return compilation; }
96
/// Gets the current type resolve context.
98
public CSharpTypeResolveContext CurrentTypeResolveContext {
99
get { return context; }
102
CSharpResolver WithContext(CSharpTypeResolveContext newContext)
104
return new CSharpResolver(compilation, conversions, newContext, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
108
/// Gets whether the current context is <c>checked</c>.
110
public bool CheckForOverflow {
111
get { return checkForOverflow; }
115
/// Sets whether the current context is <c>checked</c>.
117
public CSharpResolver WithCheckForOverflow(bool checkForOverflow)
119
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
123
/// Gets whether the resolver is currently within a lambda expression.
125
public bool IsWithinLambdaExpression {
126
get { return isWithinLambdaExpression; }
130
/// Sets whether the resolver is currently within a lambda expression.
132
public CSharpResolver WithIsWithinLambdaExpression(bool isWithinLambdaExpression)
134
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, objectInitializerStack);
138
/// Gets the current member definition that is used to look up identifiers as parameters
139
/// or type parameters.
141
public IMember CurrentMember {
142
get { return context.CurrentMember; }
146
/// Sets the current member definition.
148
/// <remarks>Don't forget to also set CurrentTypeDefinition when setting CurrentMember;
149
/// setting one of the properties does not automatically set the other.</remarks>
150
public CSharpResolver WithCurrentMember(IMember member)
152
return WithContext(context.WithCurrentMember(member));
156
/// Gets the current using scope that is used to look up identifiers as class names.
158
public ResolvedUsingScope CurrentUsingScope {
159
get { return context.CurrentUsingScope; }
163
/// Sets the current using scope that is used to look up identifiers as class names.
165
public CSharpResolver WithCurrentUsingScope(ResolvedUsingScope usingScope)
167
return WithContext(context.WithUsingScope(usingScope));
171
#region Per-CurrentTypeDefinition Cache
172
readonly TypeDefinitionCache currentTypeDefinitionCache;
175
/// Gets the current type definition.
177
public ITypeDefinition CurrentTypeDefinition {
178
get { return context.CurrentTypeDefinition; }
182
/// Sets the current type definition.
184
public CSharpResolver WithCurrentTypeDefinition(ITypeDefinition typeDefinition)
186
if (this.CurrentTypeDefinition == typeDefinition)
189
TypeDefinitionCache newTypeDefinitionCache;
190
if (typeDefinition != null)
191
newTypeDefinitionCache = new TypeDefinitionCache(typeDefinition);
193
newTypeDefinitionCache = null;
195
return new CSharpResolver(compilation, conversions, context.WithCurrentTypeDefinition(typeDefinition),
196
checkForOverflow, isWithinLambdaExpression, newTypeDefinitionCache, localVariableStack, objectInitializerStack);
199
sealed class TypeDefinitionCache
201
public readonly ITypeDefinition TypeDefinition;
202
public readonly Dictionary<string, ResolveResult> SimpleNameLookupCacheExpression = new Dictionary<string, ResolveResult>();
203
public readonly Dictionary<string, ResolveResult> SimpleNameLookupCacheInvocationTarget = new Dictionary<string, ResolveResult>();
204
public readonly Dictionary<string, ResolveResult> SimpleTypeLookupCache = new Dictionary<string, ResolveResult>();
206
public TypeDefinitionCache(ITypeDefinition typeDefinition)
208
this.TypeDefinition = typeDefinition;
213
#region Local Variable Management
215
// We store the local variables in an immutable stack.
216
// The beginning of a block is marked by a null entry.
218
// This data structure is used to allow efficient cloning of the resolver with its local variable context.
219
readonly ImmutableStack<IVariable> localVariableStack = ImmutableStack<IVariable>.Empty;
221
CSharpResolver WithLocalVariableStack(ImmutableStack<IVariable> stack)
223
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, stack, objectInitializerStack);
227
/// Opens a new scope for local variables.
229
public CSharpResolver PushBlock()
231
return WithLocalVariableStack(localVariableStack.Push(null));
235
/// Closes the current scope for local variables; removing all variables in that scope.
237
public CSharpResolver PopBlock()
239
var stack = localVariableStack;
240
IVariable removedVar;
242
removedVar = stack.Peek();
244
} while (removedVar != null);
245
return WithLocalVariableStack(stack);
249
/// Adds a new variable or lambda parameter to the current block.
251
public CSharpResolver AddVariable(IVariable variable)
253
if (variable == null)
254
throw new ArgumentNullException("variable");
255
return WithLocalVariableStack(localVariableStack.Push(variable));
259
/// Removes the variable that was just added.
261
public CSharpResolver PopLastVariable()
263
if (localVariableStack.Peek() == null)
264
throw new InvalidOperationException("There is no variable within the current block.");
265
return WithLocalVariableStack(localVariableStack.Pop());
269
/// Gets all currently visible local variables and lambda parameters.
271
public IEnumerable<IVariable> LocalVariables {
273
return localVariableStack.Where(v => v != null);
278
#region Object Initializer Context
279
sealed class ObjectInitializerContext
281
internal readonly ResolveResult initializedObject;
282
internal readonly ObjectInitializerContext prev;
284
public ObjectInitializerContext(ResolveResult initializedObject, CSharpResolver.ObjectInitializerContext prev)
286
this.initializedObject = initializedObject;
291
readonly ObjectInitializerContext objectInitializerStack;
293
CSharpResolver WithObjectInitializerStack(ObjectInitializerContext stack)
295
return new CSharpResolver(compilation, conversions, context, checkForOverflow, isWithinLambdaExpression, currentTypeDefinitionCache, localVariableStack, stack);
299
/// Pushes the type of the object that is currently being initialized.
301
public CSharpResolver PushObjectInitializer(ResolveResult initializedObject)
303
if (initializedObject == null)
304
throw new ArgumentNullException("initializedObject");
305
return WithObjectInitializerStack(new ObjectInitializerContext(initializedObject, objectInitializerStack));
308
public CSharpResolver PopObjectInitializer()
310
if (objectInitializerStack == null)
311
throw new InvalidOperationException();
312
return WithObjectInitializerStack(objectInitializerStack.prev);
316
/// Gets whether this context is within an object initializer.
318
public bool IsInObjectInitializer {
319
get { return objectInitializerStack != null; }
323
/// Gets the current object initializer. This usually is an <see cref="InitializedObjectResolveResult"/>
324
/// or (for nested initializers) a semantic tree based on an <see cref="InitializedObjectResolveResult"/>.
325
/// Returns ErrorResolveResult if there is no object initializer.
327
public ResolveResult CurrentObjectInitializer {
329
return objectInitializerStack != null ? objectInitializerStack.initializedObject : ErrorResult;
334
/// Gets the type of the object currently being initialized.
335
/// Returns SharedTypes.Unknown if no object initializer is currently open (or if the object initializer
336
/// has unknown type).
338
public IType CurrentObjectInitializerType {
339
get { return CurrentObjectInitializer.Type; }
345
/// Creates a copy of this CSharp resolver.
347
[Obsolete("CSharpResolver is immutable, cloning is no longer necessary")]
348
public CSharpResolver Clone()
354
#region ResolveUnaryOperator
355
#region ResolveUnaryOperator method
356
public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression)
358
if (expression.Type.Kind == TypeKind.Dynamic)
359
return UnaryOperatorResolveResult(SpecialType.Dynamic, op, expression);
361
// C# 4.0 spec: Ā§7.3.3 Unary operator overload resolution
362
string overloadableOperatorName = GetOverloadableOperatorName(op);
363
if (overloadableOperatorName == null) {
365
case UnaryOperatorType.Dereference:
366
PointerType p = expression.Type as PointerType;
368
return UnaryOperatorResolveResult(p.ElementType, op, expression);
371
case UnaryOperatorType.AddressOf:
372
return UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression);
373
case UnaryOperatorType.Await:
374
ResolveResult getAwaiterMethodGroup = ResolveMemberAccess(expression, "GetAwaiter", EmptyList<IType>.Instance, NameLookupMode.InvocationTarget);
375
ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, new ResolveResult[0]);
376
var getResultMethodGroup = CreateMemberLookup().Lookup(getAwaiterInvocation, "GetResult", EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
377
if (getResultMethodGroup != null) {
378
var or = getResultMethodGroup.PerformOverloadResolution(compilation, new ResolveResult[0], allowExtensionMethods: false, conversions: conversions);
379
IType awaitResultType = or.GetBestCandidateWithSubstitutedTypeArguments().ReturnType;
380
return UnaryOperatorResolveResult(awaitResultType, UnaryOperatorType.Await, expression);
382
return UnaryOperatorResolveResult(SpecialType.UnknownType, UnaryOperatorType.Await, expression);
385
throw new ArgumentException("Invalid value for UnaryOperatorType", "op");
388
// If the type is nullable, get the underlying type:
389
IType type = NullableType.GetUnderlyingType(expression.Type);
390
bool isNullable = NullableType.IsNullable(expression.Type);
392
// the operator is overloadable:
393
OverloadResolution userDefinedOperatorOR = new OverloadResolution(compilation, new[] { expression }, conversions: conversions);
394
foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) {
395
userDefinedOperatorOR.AddCandidate(candidate);
397
if (userDefinedOperatorOR.FoundApplicableCandidate) {
398
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
401
expression = UnaryNumericPromotion(op, ref type, isNullable, expression);
402
CSharpOperators.OperatorMethod[] methodGroup;
403
CSharpOperators operators = CSharpOperators.Get(compilation);
405
case UnaryOperatorType.Increment:
406
case UnaryOperatorType.Decrement:
407
case UnaryOperatorType.PostIncrement:
408
case UnaryOperatorType.PostDecrement:
409
// C# 4.0 spec: Ā§7.6.9 Postfix increment and decrement operators
410
// C# 4.0 spec: Ā§7.7.5 Prefix increment and decrement operators
411
TypeCode code = ReflectionHelper.GetTypeCode(type);
412
if ((code >= TypeCode.Char && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer)
413
return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
415
return new ErrorResolveResult(expression.Type);
416
case UnaryOperatorType.Plus:
417
methodGroup = operators.UnaryPlusOperators;
419
case UnaryOperatorType.Minus:
420
methodGroup = CheckForOverflow ? operators.CheckedUnaryMinusOperators : operators.UncheckedUnaryMinusOperators;
422
case UnaryOperatorType.Not:
423
methodGroup = operators.LogicalNegationOperators;
425
case UnaryOperatorType.BitNot:
426
if (type.Kind == TypeKind.Enum) {
427
if (expression.IsCompileTimeConstant && !isNullable && expression.ConstantValue != null) {
428
// evaluate as (E)(~(U)x);
429
var U = compilation.FindType(expression.ConstantValue.GetType());
430
var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue);
431
return CheckErrorAndResolveCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum));
433
return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
436
methodGroup = operators.BitwiseComplementOperators;
440
throw new InvalidOperationException();
442
OverloadResolution builtinOperatorOR = new OverloadResolution(compilation, new[] { expression }, conversions: conversions);
443
foreach (var candidate in methodGroup) {
444
builtinOperatorOR.AddCandidate(candidate);
446
CSharpOperators.UnaryOperatorMethod m = (CSharpOperators.UnaryOperatorMethod)builtinOperatorOR.BestCandidate;
447
IType resultType = m.ReturnType;
448
if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
449
if (userDefinedOperatorOR.BestCandidate != null) {
450
// If there are any user-defined operators, prefer those over the built-in operators.
451
// It'll be a more informative error.
452
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
453
} else if (builtinOperatorOR.BestCandidateAmbiguousWith != null) {
454
// If the best candidate is ambiguous, just use the input type instead
455
// of picking one of the ambiguous overloads.
456
return new ErrorResolveResult(expression.Type);
458
return new ErrorResolveResult(resultType);
460
} else if (expression.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
463
val = m.Invoke(this, expression.ConstantValue);
464
} catch (ArithmeticException) {
465
return new ErrorResolveResult(resultType);
467
return new ConstantResolveResult(resultType, val);
469
expression = Convert(expression, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
470
return UnaryOperatorResolveResult(resultType, op, expression,
471
builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
475
OperatorResolveResult UnaryOperatorResolveResult(IType resultType, UnaryOperatorType op, ResolveResult expression, bool isLifted = false)
477
return new OperatorResolveResult(
478
resultType, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow),
479
null, isLifted, new[] { expression });
483
#region UnaryNumericPromotion
484
ResolveResult UnaryNumericPromotion(UnaryOperatorType op, ref IType type, bool isNullable, ResolveResult expression)
486
// C# 4.0 spec: Ā§7.3.6.1
487
TypeCode code = ReflectionHelper.GetTypeCode(type);
488
if (isNullable && type.Kind == TypeKind.Null)
489
code = TypeCode.SByte; // cause promotion of null to int32
491
case UnaryOperatorType.Minus:
492
if (code == TypeCode.UInt32) {
493
type = compilation.FindType(KnownTypeCode.Int64);
494
return Convert(expression, MakeNullable(type, isNullable),
495
isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
497
goto case UnaryOperatorType.Plus;
498
case UnaryOperatorType.Plus:
499
case UnaryOperatorType.BitNot:
500
if (code >= TypeCode.Char && code <= TypeCode.UInt16) {
501
type = compilation.FindType(KnownTypeCode.Int32);
502
return Convert(expression, MakeNullable(type, isNullable),
503
isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
511
#region GetOverloadableOperatorName
512
static string GetOverloadableOperatorName(UnaryOperatorType op)
515
case UnaryOperatorType.Not:
516
return "op_LogicalNot";
517
case UnaryOperatorType.BitNot:
518
return "op_OnesComplement";
519
case UnaryOperatorType.Minus:
520
return "op_UnaryNegation";
521
case UnaryOperatorType.Plus:
522
return "op_UnaryPlus";
523
case UnaryOperatorType.Increment:
524
case UnaryOperatorType.PostIncrement:
525
return "op_Increment";
526
case UnaryOperatorType.Decrement:
527
case UnaryOperatorType.PostDecrement:
528
return "op_Decrement";
536
#region ResolveBinaryOperator
537
#region ResolveBinaryOperator method
538
public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
540
if (lhs.Type.Kind == TypeKind.Dynamic || rhs.Type.Kind == TypeKind.Dynamic) {
541
lhs = Convert(lhs, SpecialType.Dynamic);
542
rhs = Convert(rhs, SpecialType.Dynamic);
543
return BinaryOperatorResolveResult(SpecialType.Dynamic, lhs, op, rhs);
546
// C# 4.0 spec: Ā§7.3.4 Binary operator overload resolution
547
string overloadableOperatorName = GetOverloadableOperatorName(op);
548
if (overloadableOperatorName == null) {
550
// Handle logical and/or exactly as bitwise and/or:
551
// - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
552
// - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
553
// - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
554
if (op == BinaryOperatorType.ConditionalAnd) {
555
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd);
556
} else if (op == BinaryOperatorType.ConditionalOr) {
557
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr);
558
} else if (op == BinaryOperatorType.NullCoalescing) {
559
// null coalescing operator is not overloadable and needs to be handled separately
560
return ResolveNullCoalescingOperator(lhs, rhs);
562
throw new ArgumentException("Invalid value for BinaryOperatorType", "op");
566
// If the type is nullable, get the underlying type:
567
bool isNullable = NullableType.IsNullable(lhs.Type) || NullableType.IsNullable(rhs.Type);
568
IType lhsType = NullableType.GetUnderlyingType(lhs.Type);
569
IType rhsType = NullableType.GetUnderlyingType(rhs.Type);
571
// the operator is overloadable:
572
OverloadResolution userDefinedOperatorOR = new OverloadResolution(compilation, new[] { lhs, rhs }, conversions: conversions);
573
HashSet<IParameterizedMember> userOperatorCandidates = new HashSet<IParameterizedMember>();
574
userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName));
575
userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName));
576
foreach (var candidate in userOperatorCandidates) {
577
userDefinedOperatorOR.AddCandidate(candidate);
579
if (userDefinedOperatorOR.FoundApplicableCandidate) {
580
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
583
if (lhsType.Kind == TypeKind.Null && rhsType.IsReferenceType == false
584
|| lhsType.IsReferenceType == false && rhsType.Kind == TypeKind.Null)
588
if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) {
589
// special case: the shift operators allow "var x = null << null", producing int?.
590
if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
592
// for shift operators, do unary promotion independently on both arguments
593
lhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref lhsType, isNullable, lhs);
594
rhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref rhsType, isNullable, rhs);
596
bool allowNullableConstants = op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality;
597
if (!BinaryNumericPromotion(isNullable, ref lhs, ref rhs, allowNullableConstants))
598
return new ErrorResolveResult(lhs.Type);
600
// re-read underlying types after numeric promotion
601
lhsType = NullableType.GetUnderlyingType(lhs.Type);
602
rhsType = NullableType.GetUnderlyingType(rhs.Type);
604
IEnumerable<CSharpOperators.OperatorMethod> methodGroup;
605
CSharpOperators operators = CSharpOperators.Get(compilation);
607
case BinaryOperatorType.Multiply:
608
methodGroup = operators.MultiplicationOperators;
610
case BinaryOperatorType.Divide:
611
methodGroup = operators.DivisionOperators;
613
case BinaryOperatorType.Modulus:
614
methodGroup = operators.RemainderOperators;
616
case BinaryOperatorType.Add:
617
methodGroup = operators.AdditionOperators;
619
if (lhsType.Kind == TypeKind.Enum) {
620
// E operator +(E x, U y);
621
IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
622
if (TryConvert(ref rhs, underlyingType)) {
623
return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
626
if (rhsType.Kind == TypeKind.Enum) {
627
// E operator +(U x, E y);
628
IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable);
629
if (TryConvert(ref lhs, underlyingType)) {
630
return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
634
if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
635
return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
636
} else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
637
return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
640
if (lhsType is PointerType) {
641
methodGroup = new [] {
642
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
643
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
644
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
645
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
647
} else if (rhsType is PointerType) {
648
methodGroup = new [] {
649
PointerArithmeticOperator(rhsType, KnownTypeCode.Int32, rhsType),
650
PointerArithmeticOperator(rhsType, KnownTypeCode.UInt32, rhsType),
651
PointerArithmeticOperator(rhsType, KnownTypeCode.Int64, rhsType),
652
PointerArithmeticOperator(rhsType, KnownTypeCode.UInt64, rhsType)
655
if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
656
return new ErrorResolveResult(SpecialType.NullType);
659
case BinaryOperatorType.Subtract:
660
methodGroup = operators.SubtractionOperators;
662
if (lhsType.Kind == TypeKind.Enum) {
663
// E operator ā(E x, U y);
664
IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
665
if (TryConvert(ref rhs, underlyingType)) {
666
return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
668
// U operator ā(E x, E y);
669
if (TryConvert(ref rhs, lhs.Type)) {
670
return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs);
673
if (rhsType.Kind == TypeKind.Enum) {
674
// U operator ā(E x, E y);
675
if (TryConvert(ref lhs, rhs.Type)) {
676
return HandleEnumSubtraction(isNullable, rhsType, lhs, rhs);
680
if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
681
return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
682
} else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
683
return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
686
if (lhsType is PointerType) {
687
if (rhsType is PointerType) {
688
IType int64 = compilation.FindType(KnownTypeCode.Int64);
689
if (lhsType.Equals(rhsType)) {
690
return BinaryOperatorResolveResult(int64, lhs, op, rhs);
692
return new ErrorResolveResult(int64);
695
methodGroup = new [] {
696
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
697
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
698
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
699
PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
703
if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
704
return new ErrorResolveResult(SpecialType.NullType);
707
case BinaryOperatorType.ShiftLeft:
708
methodGroup = operators.ShiftLeftOperators;
710
case BinaryOperatorType.ShiftRight:
711
methodGroup = operators.ShiftRightOperators;
713
case BinaryOperatorType.Equality:
714
case BinaryOperatorType.InEquality:
715
case BinaryOperatorType.LessThan:
716
case BinaryOperatorType.GreaterThan:
717
case BinaryOperatorType.LessThanOrEqual:
718
case BinaryOperatorType.GreaterThanOrEqual:
720
if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) {
721
// bool operator op(E x, E y);
722
return HandleEnumComparison(op, lhsType, isNullable, lhs, rhs);
723
} else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) {
724
// bool operator op(E x, E y);
725
return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs);
726
} else if (lhsType is PointerType && rhsType is PointerType) {
727
return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
729
if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality) {
730
if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true) {
731
// If it's a reference comparison
732
if (op == BinaryOperatorType.Equality)
733
methodGroup = operators.ReferenceEqualityOperators;
735
methodGroup = operators.ReferenceInequalityOperators;
737
} else if (lhsType.Kind == TypeKind.Null && IsNullableTypeOrNonValueType(rhs.Type)
738
|| IsNullableTypeOrNonValueType(lhs.Type) && rhsType.Kind == TypeKind.Null) {
739
// compare type parameter or nullable type with the null literal
740
return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
744
case BinaryOperatorType.Equality:
745
methodGroup = operators.ValueEqualityOperators;
747
case BinaryOperatorType.InEquality:
748
methodGroup = operators.ValueInequalityOperators;
750
case BinaryOperatorType.LessThan:
751
methodGroup = operators.LessThanOperators;
753
case BinaryOperatorType.GreaterThan:
754
methodGroup = operators.GreaterThanOperators;
756
case BinaryOperatorType.LessThanOrEqual:
757
methodGroup = operators.LessThanOrEqualOperators;
759
case BinaryOperatorType.GreaterThanOrEqual:
760
methodGroup = operators.GreaterThanOrEqualOperators;
763
throw new InvalidOperationException();
767
case BinaryOperatorType.BitwiseAnd:
768
case BinaryOperatorType.BitwiseOr:
769
case BinaryOperatorType.ExclusiveOr:
771
if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) {
772
// bool operator op(E x, E y);
773
return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
774
} else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) {
775
// bool operator op(E x, E y);
776
return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
780
case BinaryOperatorType.BitwiseAnd:
781
methodGroup = operators.BitwiseAndOperators;
783
case BinaryOperatorType.BitwiseOr:
784
methodGroup = operators.BitwiseOrOperators;
786
case BinaryOperatorType.ExclusiveOr:
787
methodGroup = operators.BitwiseXorOperators;
790
throw new InvalidOperationException();
794
case BinaryOperatorType.ConditionalAnd:
795
methodGroup = operators.LogicalAndOperators;
797
case BinaryOperatorType.ConditionalOr:
798
methodGroup = operators.LogicalOrOperators;
801
throw new InvalidOperationException();
803
OverloadResolution builtinOperatorOR = new OverloadResolution(compilation, new[] { lhs, rhs }, conversions: conversions);
804
foreach (var candidate in methodGroup) {
805
builtinOperatorOR.AddCandidate(candidate);
807
CSharpOperators.BinaryOperatorMethod m = (CSharpOperators.BinaryOperatorMethod)builtinOperatorOR.BestCandidate;
808
IType resultType = m.ReturnType;
809
if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
810
// If there are any user-defined operators, prefer those over the built-in operators.
811
// It'll be a more informative error.
812
if (userDefinedOperatorOR.BestCandidate != null)
813
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
815
return new ErrorResolveResult(resultType);
816
} else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
819
val = m.Invoke(this, lhs.ConstantValue, rhs.ConstantValue);
820
} catch (ArithmeticException) {
821
return new ErrorResolveResult(resultType);
823
return new ConstantResolveResult(resultType, val);
825
lhs = Convert(lhs, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
826
rhs = Convert(rhs, m.Parameters[1].Type, builtinOperatorOR.ArgumentConversions[1]);
827
return BinaryOperatorResolveResult(resultType, lhs, op, rhs,
828
builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
832
bool IsNullableTypeOrNonValueType(IType type)
834
return NullableType.IsNullable(type) || type.IsReferenceType != false;
837
ResolveResult BinaryOperatorResolveResult(IType resultType, ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs, bool isLifted = false)
839
return new OperatorResolveResult(
840
resultType, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow),
841
null, isLifted, new[] { lhs, rhs });
845
#region Pointer arithmetic
846
CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, KnownTypeCode inputType2)
848
return PointerArithmeticOperator(resultType, inputType1, compilation.FindType(inputType2));
851
CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, KnownTypeCode inputType1, IType inputType2)
853
return PointerArithmeticOperator(resultType, compilation.FindType(inputType1), inputType2);
856
CSharpOperators.BinaryOperatorMethod PointerArithmeticOperator(IType resultType, IType inputType1, IType inputType2)
858
return new CSharpOperators.BinaryOperatorMethod(compilation) {
859
ReturnType = resultType,
861
new DefaultParameter(inputType1, string.Empty),
862
new DefaultParameter(inputType2, string.Empty)
868
#region Enum helper methods
869
IType GetEnumUnderlyingType(IType enumType)
871
ITypeDefinition def = enumType.GetDefinition();
872
return def != null ? def.EnumUnderlyingType : SpecialType.UnknownType;
876
/// Handle the case where an enum value is compared with another enum value
877
/// bool operator op(E x, E y);
879
ResolveResult HandleEnumComparison(BinaryOperatorType op, IType enumType, bool isNullable, ResolveResult lhs, ResolveResult rhs)
881
// evaluate as ((U)x op (U)y)
882
IType elementType = GetEnumUnderlyingType(enumType);
883
if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) {
884
lhs = ResolveCast(elementType, lhs);
887
rhs = ResolveCast(elementType, rhs);
890
return ResolveBinaryOperator(op, lhs, rhs);
892
IType resultType = compilation.FindType(KnownTypeCode.Boolean);
893
return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable);
897
/// Handle the case where an enum value is subtracted from another enum value
898
/// U operator ā(E x, E y);
900
ResolveResult HandleEnumSubtraction(bool isNullable, IType enumType, ResolveResult lhs, ResolveResult rhs)
902
// evaluate as (U)((U)x ā (U)y)
903
IType elementType = GetEnumUnderlyingType(enumType);
904
if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) {
905
lhs = ResolveCast(elementType, lhs);
908
rhs = ResolveCast(elementType, rhs);
911
return CheckErrorAndResolveCast(elementType, ResolveBinaryOperator(BinaryOperatorType.Subtract, lhs, rhs));
913
IType resultType = MakeNullable(elementType, isNullable);
914
return BinaryOperatorResolveResult(resultType, lhs, BinaryOperatorType.Subtract, rhs, isNullable);
918
/// Handle the following enum operators:
919
/// E operator +(E x, U y);
920
/// E operator +(U x, E y);
921
/// E operator ā(E x, U y);
922
/// E operator &(E x, E y);
923
/// E operator |(E x, E y);
924
/// E operator ^(E x, E y);
926
ResolveResult HandleEnumOperator(bool isNullable, IType enumType, BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
928
// evaluate as (E)((U)x op (U)y)
929
if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) {
930
IType elementType = GetEnumUnderlyingType(enumType);
931
lhs = ResolveCast(elementType, lhs);
934
rhs = ResolveCast(elementType, rhs);
937
return CheckErrorAndResolveCast(enumType, ResolveBinaryOperator(op, lhs, rhs));
939
IType resultType = MakeNullable(enumType, isNullable);
940
return BinaryOperatorResolveResult(resultType, lhs, op, rhs, isNullable);
943
IType MakeNullable(IType type, bool isNullable)
946
return NullableType.Create(compilation, type);
952
#region BinaryNumericPromotion
953
bool BinaryNumericPromotion(bool isNullable, ref ResolveResult lhs, ref ResolveResult rhs, bool allowNullableConstants)
955
// C# 4.0 spec: Ā§7.3.6.2
956
TypeCode lhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(lhs.Type));
957
TypeCode rhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rhs.Type));
958
// if one of the inputs is the null literal, promote that to the type of the other operand
959
if (isNullable && lhs.Type.Kind == TypeKind.Null && rhsCode >= TypeCode.Boolean && rhsCode <= TypeCode.Decimal) {
960
lhs = CastTo(rhsCode, isNullable, lhs, allowNullableConstants);
962
} else if (isNullable && rhs.Type.Kind == TypeKind.Null && lhsCode >= TypeCode.Boolean && lhsCode <= TypeCode.Decimal) {
963
rhs = CastTo(lhsCode, isNullable, rhs, allowNullableConstants);
966
bool bindingError = false;
967
if (lhsCode >= TypeCode.Char && lhsCode <= TypeCode.Decimal
968
&& rhsCode >= TypeCode.Char && rhsCode <= TypeCode.Decimal)
971
if (lhsCode == TypeCode.Decimal || rhsCode == TypeCode.Decimal) {
972
targetType = TypeCode.Decimal;
973
bindingError = (lhsCode == TypeCode.Single || lhsCode == TypeCode.Double
974
|| rhsCode == TypeCode.Single || rhsCode == TypeCode.Double);
975
} else if (lhsCode == TypeCode.Double || rhsCode == TypeCode.Double) {
976
targetType = TypeCode.Double;
977
} else if (lhsCode == TypeCode.Single || rhsCode == TypeCode.Single) {
978
targetType = TypeCode.Single;
979
} else if (lhsCode == TypeCode.UInt64 || rhsCode == TypeCode.UInt64) {
980
targetType = TypeCode.UInt64;
981
bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs);
982
} else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) {
983
targetType = TypeCode.Int64;
984
} else if (lhsCode == TypeCode.UInt32 || rhsCode == TypeCode.UInt32) {
985
targetType = (IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs)) ? TypeCode.Int64 : TypeCode.UInt32;
987
targetType = TypeCode.Int32;
989
lhs = CastTo(targetType, isNullable, lhs, allowNullableConstants);
990
rhs = CastTo(targetType, isNullable, rhs, allowNullableConstants);
992
return !bindingError;
995
bool IsSigned(TypeCode code, ResolveResult rr)
997
// Determine whether the rr with code==ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rr.Type))
998
// is a signed primitive type.
1000
case TypeCode.SByte:
1001
case TypeCode.Int16:
1003
case TypeCode.Int32:
1004
// for int, consider implicit constant expression conversion
1005
if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (int)rr.ConstantValue >= 0)
1009
case TypeCode.Int64:
1010
// for long, consider implicit constant expression conversion
1011
if (rr.IsCompileTimeConstant && rr.ConstantValue != null && (long)rr.ConstantValue >= 0)
1020
ResolveResult CastTo(TypeCode targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants)
1022
IType elementType = compilation.FindType(targetType);
1023
IType nullableType = MakeNullable(elementType, isNullable);
1024
if (nullableType.Equals(expression.Type))
1026
if (allowNullableConstants && expression.IsCompileTimeConstant) {
1027
if (expression.ConstantValue == null)
1028
return new ConstantResolveResult(nullableType, null);
1029
ResolveResult rr = ResolveCast(elementType, expression);
1032
Debug.Assert(rr.IsCompileTimeConstant);
1033
return new ConstantResolveResult(nullableType, rr.ConstantValue);
1035
return Convert(expression, nullableType,
1036
isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion);
1041
#region GetOverloadableOperatorName
1042
static string GetOverloadableOperatorName(BinaryOperatorType op)
1045
case BinaryOperatorType.Add:
1046
return "op_Addition";
1047
case BinaryOperatorType.Subtract:
1048
return "op_Subtraction";
1049
case BinaryOperatorType.Multiply:
1050
return "op_Multiply";
1051
case BinaryOperatorType.Divide:
1052
return "op_Division";
1053
case BinaryOperatorType.Modulus:
1054
return "op_Modulus";
1055
case BinaryOperatorType.BitwiseAnd:
1056
return "op_BitwiseAnd";
1057
case BinaryOperatorType.BitwiseOr:
1058
return "op_BitwiseOr";
1059
case BinaryOperatorType.ExclusiveOr:
1060
return "op_ExclusiveOr";
1061
case BinaryOperatorType.ShiftLeft:
1062
return "op_LeftShift";
1063
case BinaryOperatorType.ShiftRight:
1064
return "op_RightShift";
1065
case BinaryOperatorType.Equality:
1066
return "op_Equality";
1067
case BinaryOperatorType.InEquality:
1068
return "op_Inequality";
1069
case BinaryOperatorType.GreaterThan:
1070
return "op_GreaterThan";
1071
case BinaryOperatorType.LessThan:
1072
return "op_LessThan";
1073
case BinaryOperatorType.GreaterThanOrEqual:
1074
return "op_GreaterThanOrEqual";
1075
case BinaryOperatorType.LessThanOrEqual:
1076
return "op_LessThanOrEqual";
1083
#region Null coalescing operator
1084
ResolveResult ResolveNullCoalescingOperator(ResolveResult lhs, ResolveResult rhs)
1086
if (NullableType.IsNullable(lhs.Type)) {
1087
IType a0 = NullableType.GetUnderlyingType(lhs.Type);
1088
if (TryConvert(ref rhs, a0)) {
1089
return BinaryOperatorResolveResult(a0, lhs, BinaryOperatorType.NullCoalescing, rhs);
1092
if (TryConvert(ref rhs, lhs.Type)) {
1093
return BinaryOperatorResolveResult(lhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs);
1095
if (TryConvert(ref lhs, rhs.Type)) {
1096
return BinaryOperatorResolveResult(rhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs);
1098
return new ErrorResolveResult(lhs.Type);
1104
#region Get user-defined operator candidates
1105
IEnumerable<IParameterizedMember> GetUserDefinedOperatorCandidates(IType type, string operatorName)
1107
if (operatorName == null)
1108
return EmptyList<IMethod>.Instance;
1109
TypeCode c = ReflectionHelper.GetTypeCode(type);
1110
if (TypeCode.Boolean <= c && c <= TypeCode.Decimal || c == TypeCode.String) {
1111
// The .NET framework contains some of C#'s built-in operators as user-defined operators.
1112
// However, we must not use those as user-defined operators (we would skip numeric promotion).
1113
return EmptyList<IMethod>.Instance;
1115
// C# 4.0 spec: Ā§7.3.5 Candidate user-defined operators
1116
var operators = type.GetMethods(m => m.IsOperator && m.Name == operatorName).ToList();
1117
LiftUserDefinedOperators(operators);
1121
void LiftUserDefinedOperators(List<IMethod> operators)
1123
int nonLiftedMethodCount = operators.Count;
1124
// Construct lifted operators
1125
for (int i = 0; i < nonLiftedMethodCount; i++) {
1126
var liftedMethod = LiftUserDefinedOperator(operators[i]);
1127
if (liftedMethod != null)
1128
operators.Add(liftedMethod);
1132
LiftedUserDefinedOperator LiftUserDefinedOperator(IMethod m)
1134
if (IsComparisonOperator(m)) {
1135
if (!m.ReturnType.Equals(compilation.FindType(KnownTypeCode.Boolean)))
1136
return null; // cannot lift this operator
1138
if (!NullableType.IsNonNullableValueType(m.ReturnType))
1139
return null; // cannot lift this operator
1141
for (int i = 0; i < m.Parameters.Count; i++) {
1142
if (!NullableType.IsNonNullableValueType(m.Parameters[i].Type))
1143
return null; // cannot lift this operator
1145
return new LiftedUserDefinedOperator(m);
1148
static bool IsComparisonOperator(IMethod m)
1150
var type = OperatorDeclaration.GetOperatorType(m.Name);
1151
if (type.HasValue) {
1152
switch (type.Value) {
1153
case OperatorType.Equality:
1154
case OperatorType.Inequality:
1155
case OperatorType.GreaterThan:
1156
case OperatorType.LessThan:
1157
case OperatorType.GreaterThanOrEqual:
1158
case OperatorType.LessThanOrEqual:
1165
sealed class LiftedUserDefinedOperator : SpecializedMethod, OverloadResolution.ILiftedOperator
1167
internal readonly IParameterizedMember nonLiftedOperator;
1169
public LiftedUserDefinedOperator(IMethod nonLiftedMethod)
1170
: base(nonLiftedMethod, TypeParameterSubstitution.Identity)
1172
this.nonLiftedOperator = nonLiftedMethod;
1173
var substitution = new MakeNullableVisitor(nonLiftedMethod.Compilation);
1174
this.Parameters = base.CreateParameters(substitution);
1175
// Comparison operators keep the 'bool' return type even when lifted.
1176
if (IsComparisonOperator(nonLiftedMethod))
1177
this.ReturnType = nonLiftedMethod.ReturnType;
1179
this.ReturnType = nonLiftedMethod.ReturnType.AcceptVisitor(substitution);
1182
public IList<IParameter> NonLiftedParameters {
1183
get { return nonLiftedOperator.Parameters; }
1186
public override bool Equals(object obj)
1188
LiftedUserDefinedOperator op = obj as LiftedUserDefinedOperator;
1189
return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator);
1192
public override int GetHashCode()
1194
return nonLiftedOperator.GetHashCode() ^ 0x7191254;
1198
sealed class MakeNullableVisitor : TypeVisitor
1200
readonly ICompilation compilation;
1202
public MakeNullableVisitor(ICompilation compilation)
1204
this.compilation = compilation;
1207
public override IType VisitTypeDefinition(ITypeDefinition type)
1209
return NullableType.Create(compilation, type);
1212
public override IType VisitTypeParameter(ITypeParameter type)
1214
return NullableType.Create(compilation, type);
1217
public override IType VisitParameterizedType(ParameterizedType type)
1219
return NullableType.Create(compilation, type);
1222
public override IType VisitOtherType(IType type)
1224
return NullableType.Create(compilation, type);
1228
ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r, System.Linq.Expressions.ExpressionType operatorType)
1230
if (r.BestCandidateErrors != OverloadResolutionErrors.None)
1231
return r.CreateResolveResult(null);
1232
IMethod method = (IMethod)r.BestCandidate;
1233
return new OperatorResolveResult(method.ReturnType, operatorType, method,
1234
isLiftedOperator: method is OverloadResolution.ILiftedOperator,
1235
operands: r.GetArgumentsWithConversions());
1240
bool TryConvert(ref ResolveResult rr, IType targetType)
1242
Conversion c = conversions.ImplicitConversion(rr, targetType);
1244
rr = Convert(rr, targetType, c);
1251
ResolveResult Convert(ResolveResult rr, IType targetType)
1253
return Convert(rr, targetType, conversions.ImplicitConversion(rr, targetType));
1256
ResolveResult Convert(ResolveResult rr, IType targetType, Conversion c)
1258
if (c == Conversion.IdentityConversion)
1260
else if (rr.IsCompileTimeConstant && c != Conversion.None)
1261
return ResolveCast(targetType, rr);
1263
return new ConversionResolveResult(targetType, rr, c);
1266
public ResolveResult ResolveCast(IType targetType, ResolveResult expression)
1268
// C# 4.0 spec: Ā§7.7.6 Cast expressions
1269
if (expression.IsCompileTimeConstant) {
1270
TypeCode code = ReflectionHelper.GetTypeCode(targetType);
1271
if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) {
1273
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
1274
} catch (OverflowException) {
1275
return new ErrorResolveResult(targetType);
1277
} else if (code == TypeCode.String) {
1278
if (expression.ConstantValue == null || expression.ConstantValue is string)
1279
return new ConstantResolveResult(targetType, expression.ConstantValue);
1281
return new ErrorResolveResult(targetType);
1282
} else if (targetType.Kind == TypeKind.Enum) {
1283
code = ReflectionHelper.GetTypeCode(GetEnumUnderlyingType(targetType));
1284
if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expression.ConstantValue != null) {
1286
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
1287
} catch (OverflowException) {
1288
return new ErrorResolveResult(targetType);
1293
Conversion c = conversions.ExplicitConversion(expression, targetType);
1294
return new ConversionResolveResult(targetType, expression, c);
1297
internal object CSharpPrimitiveCast(TypeCode targetType, object input)
1299
return Utils.CSharpPrimitiveCast.Cast(targetType, input, this.CheckForOverflow);
1302
ResolveResult CheckErrorAndResolveCast(IType targetType, ResolveResult expression)
1304
if (expression.IsError)
1307
return ResolveCast(targetType, expression);
1311
#region ResolveSimpleName
1312
public ResolveResult ResolveSimpleName(string identifier, IList<IType> typeArguments, bool isInvocationTarget = false)
1314
// C# 4.0 spec: Ā§7.6.2 Simple Names
1316
return LookupSimpleNameOrTypeName(
1317
identifier, typeArguments,
1318
isInvocationTarget ? NameLookupMode.InvocationTarget : NameLookupMode.Expression);
1321
public ResolveResult LookupSimpleNameOrTypeName(string identifier, IList<IType> typeArguments, NameLookupMode lookupMode)
1323
// C# 4.0 spec: Ā§3.8 Namespace and type names; Ā§7.6.2 Simple Names
1325
if (identifier == null)
1326
throw new ArgumentNullException("identifier");
1327
if (typeArguments == null)
1328
throw new ArgumentNullException("typeArguments");
1330
int k = typeArguments.Count;
1333
if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) {
1334
// Look in local variables
1335
foreach (IVariable v in this.LocalVariables) {
1336
if (v.Name == identifier) {
1337
return new LocalResolveResult(v);
1340
// Look in parameters of current method
1341
IParameterizedMember parameterizedMember = this.CurrentMember as IParameterizedMember;
1342
if (parameterizedMember != null) {
1343
foreach (IParameter p in parameterizedMember.Parameters) {
1344
if (p.Name == identifier) {
1345
return new LocalResolveResult(p);
1351
// look in type parameters of current method
1352
IMethod m = this.CurrentMember as IMethod;
1354
foreach (ITypeParameter tp in m.TypeParameters) {
1355
if (tp.Name == identifier)
1356
return new TypeResolveResult(tp);
1361
bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument));
1363
ResolveResult r = null;
1364
if (currentTypeDefinitionCache != null) {
1365
Dictionary<string, ResolveResult> cache = null;
1366
bool foundInCache = false;
1368
switch (lookupMode) {
1369
case NameLookupMode.Expression:
1370
cache = currentTypeDefinitionCache.SimpleNameLookupCacheExpression;
1372
case NameLookupMode.InvocationTarget:
1373
cache = currentTypeDefinitionCache.SimpleNameLookupCacheInvocationTarget;
1375
case NameLookupMode.Type:
1376
cache = currentTypeDefinitionCache.SimpleTypeLookupCache;
1379
if (cache != null) {
1381
foundInCache = cache.TryGetValue(identifier, out r);
1384
if (!foundInCache) {
1385
r = LookInCurrentType(identifier, typeArguments, lookupMode, parameterizeResultType);
1386
if (cache != null) {
1387
// also cache missing members (r==null)
1389
cache[identifier] = r;
1396
if (context.CurrentUsingScope == null) {
1397
// If no using scope was specified, we still need to look in the global namespace:
1398
r = LookInUsingScopeNamespace(null, compilation.RootNamespace, identifier, typeArguments, parameterizeResultType);
1400
if (k == 0 && lookupMode != NameLookupMode.TypeInUsingDeclaration) {
1401
if (!context.CurrentUsingScope.ResolveCache.TryGetValue(identifier, out r)) {
1402
r = LookInCurrentUsingScope(identifier, typeArguments, false, false);
1403
r = context.CurrentUsingScope.ResolveCache.GetOrAdd(identifier, r);
1406
r = LookInCurrentUsingScope(identifier, typeArguments, lookupMode == NameLookupMode.TypeInUsingDeclaration, parameterizeResultType);
1412
if (typeArguments.Count == 0 && identifier == "dynamic") {
1413
return new TypeResolveResult(SpecialType.Dynamic);
1415
return new UnknownIdentifierResolveResult(identifier, typeArguments.Count);
1419
public bool IsVariableReferenceWithSameType (ResolveResult rr, string identifier, out TypeResolveResult trr)
1421
if (!(rr is MemberResolveResult || rr is LocalResolveResult)) {
1425
trr = LookupSimpleNameOrTypeName (identifier, EmptyList<IType>.Instance, NameLookupMode.Type) as TypeResolveResult;
1426
return trr != null && trr.Type.Equals (rr.Type);
1429
ResolveResult LookInCurrentType(string identifier, IList<IType> typeArguments, NameLookupMode lookupMode, bool parameterizeResultType)
1431
int k = typeArguments.Count;
1432
MemberLookup lookup = CreateMemberLookup(lookupMode);
1433
// look in current type definitions
1434
for (ITypeDefinition t = this.CurrentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) {
1436
// Look for type parameter with that name
1437
var typeParameters = t.TypeParameters;
1438
// Look at all type parameters, including those copied from outer classes,
1439
// so that we can fetch the version with the correct owner.
1440
for (int i = 0; i < typeParameters.Count; i++) {
1441
if (typeParameters[i].Name == identifier)
1442
return new TypeResolveResult(typeParameters[i]);
1446
if (lookupMode == NameLookupMode.BaseTypeReference && t == this.CurrentTypeDefinition) {
1447
// don't look in current type when resolving a base type reference
1452
if (lookupMode == NameLookupMode.Expression || lookupMode == NameLookupMode.InvocationTarget) {
1453
var targetResolveResult = (t == this.CurrentTypeDefinition ? ResolveThisReference() : new TypeResolveResult(t));
1454
r = lookup.Lookup(targetResolveResult, identifier, typeArguments, lookupMode == NameLookupMode.InvocationTarget);
1456
r = lookup.LookupType(t, identifier, typeArguments, parameterizeResultType);
1458
if (!(r is UnknownMemberResolveResult)) // but do return AmbiguousMemberResolveResult
1464
ResolveResult LookInCurrentUsingScope(string identifier, IList<IType> typeArguments, bool isInUsingDeclaration, bool parameterizeResultType)
1466
// look in current namespace definitions
1467
ResolvedUsingScope currentUsingScope = this.CurrentUsingScope;
1468
for (ResolvedUsingScope u = currentUsingScope; u != null; u = u.Parent) {
1469
var resultInNamespace = LookInUsingScopeNamespace(u, u.Namespace, identifier, typeArguments, parameterizeResultType);
1470
if (resultInNamespace != null)
1471
return resultInNamespace;
1472
// then look for aliases:
1473
if (typeArguments.Count == 0) {
1474
if (u.ExternAliases.Contains(identifier)) {
1475
return ResolveExternAlias(identifier);
1477
if (!(isInUsingDeclaration && u == currentUsingScope)) {
1478
foreach (var pair in u.UsingAliases) {
1479
if (pair.Key == identifier) {
1485
// finally, look in the imported namespaces:
1486
if (!(isInUsingDeclaration && u == currentUsingScope)) {
1487
IType firstResult = null;
1488
foreach (var importedNamespace in u.Usings) {
1489
ITypeDefinition def = importedNamespace.GetTypeDefinition(identifier, typeArguments.Count);
1492
if (parameterizeResultType && typeArguments.Count > 0)
1493
resultType = new ParameterizedType(def, typeArguments);
1497
if (firstResult == null || !TopLevelTypeDefinitionIsAccessible(firstResult.GetDefinition())) {
1498
firstResult = resultType;
1499
} else if (TopLevelTypeDefinitionIsAccessible(def)) {
1500
return new AmbiguousTypeResolveResult(firstResult);
1504
if (firstResult != null)
1505
return new TypeResolveResult(firstResult);
1507
// if we didn't find anything: repeat lookup with parent namespace
1512
ResolveResult LookInUsingScopeNamespace(ResolvedUsingScope usingScope, INamespace n, string identifier, IList<IType> typeArguments, bool parameterizeResultType)
1516
// first look for a namespace
1517
int k = typeArguments.Count;
1519
INamespace childNamespace = n.GetChildNamespace(identifier);
1520
if (childNamespace != null) {
1521
if (usingScope != null && usingScope.HasAlias(identifier))
1522
return new AmbiguousTypeResolveResult(new UnknownType(null, identifier));
1523
return new NamespaceResolveResult(childNamespace);
1526
// then look for a type
1527
ITypeDefinition def = n.GetTypeDefinition(identifier, k);
1530
if (parameterizeResultType && k > 0) {
1531
result = new ParameterizedType(def, typeArguments);
1533
if (usingScope != null && usingScope.HasAlias(identifier))
1534
return new AmbiguousTypeResolveResult(result);
1536
return new TypeResolveResult(result);
1541
bool TopLevelTypeDefinitionIsAccessible(ITypeDefinition typeDef)
1543
if (typeDef.IsInternal) {
1544
return typeDef.ParentAssembly.InternalsVisibleTo(compilation.MainAssembly);
1550
/// Looks up an alias (identifier in front of :: operator)
1552
public ResolveResult ResolveAlias(string identifier)
1554
if (identifier == "global")
1555
return new NamespaceResolveResult(compilation.RootNamespace);
1557
for (ResolvedUsingScope n = this.CurrentUsingScope; n != null; n = n.Parent) {
1558
if (n.ExternAliases.Contains(identifier)) {
1559
return ResolveExternAlias(identifier);
1561
foreach (var pair in n.UsingAliases) {
1562
if (pair.Key == identifier) {
1563
return (pair.Value as NamespaceResolveResult) ?? ErrorResult;
1570
ResolveResult ResolveExternAlias(string alias)
1572
INamespace ns = compilation.GetNamespaceForExternAlias(alias);
1574
return new NamespaceResolveResult(ns);
1580
#region ResolveMemberAccess
1581
public ResolveResult ResolveMemberAccess(ResolveResult target, string identifier, IList<IType> typeArguments, NameLookupMode lookupMode = NameLookupMode.Expression)
1583
// C# 4.0 spec: Ā§7.6.4
1585
bool parameterizeResultType = !(typeArguments.Count != 0 && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument));
1586
NamespaceResolveResult nrr = target as NamespaceResolveResult;
1588
return ResolveMemberAccessOnNamespace(nrr, identifier, typeArguments, parameterizeResultType);
1591
if (target.Type.Kind == TypeKind.Dynamic)
1592
return DynamicResult;
1594
MemberLookup lookup = CreateMemberLookup(lookupMode);
1595
ResolveResult result;
1596
switch (lookupMode) {
1597
case NameLookupMode.Expression:
1598
result = lookup.Lookup(target, identifier, typeArguments, isInvocation: false);
1600
case NameLookupMode.InvocationTarget:
1601
result = lookup.Lookup(target, identifier, typeArguments, isInvocation: true);
1603
case NameLookupMode.Type:
1604
case NameLookupMode.TypeInUsingDeclaration:
1605
case NameLookupMode.BaseTypeReference:
1606
result = lookup.LookupType(target.Type, identifier, typeArguments, parameterizeResultType);
1609
throw new NotSupportedException("Invalid value for NameLookupMode");
1611
if (result is UnknownMemberResolveResult) {
1612
var extensionMethods = GetExtensionMethods(identifier, typeArguments);
1613
if (extensionMethods.Count > 0) {
1614
return new MethodGroupResolveResult(target, identifier, EmptyList<MethodListWithDeclaringType>.Instance, typeArguments) {
1615
extensionMethods = extensionMethods
1619
MethodGroupResolveResult mgrr = result as MethodGroupResolveResult;
1621
Debug.Assert(mgrr.extensionMethods == null);
1622
// set the values that are necessary to make MethodGroupResolveResult.GetExtensionMethods() work
1623
mgrr.resolver = this;
1629
[Obsolete("Use ResolveMemberAccess() with NameLookupMode.Type instead")]
1630
public ResolveResult ResolveMemberType(ResolveResult target, string identifier, IList<IType> typeArguments)
1632
return ResolveMemberAccess(target, identifier, typeArguments, NameLookupMode.Type);
1635
ResolveResult ResolveMemberAccessOnNamespace(NamespaceResolveResult nrr, string identifier, IList<IType> typeArguments, bool parameterizeResultType)
1637
if (typeArguments.Count == 0) {
1638
INamespace childNamespace = nrr.Namespace.GetChildNamespace(identifier);
1639
if (childNamespace != null)
1640
return new NamespaceResolveResult(childNamespace);
1642
ITypeDefinition def = nrr.Namespace.GetTypeDefinition(identifier, typeArguments.Count);
1644
if (parameterizeResultType && typeArguments.Count > 0)
1645
return new TypeResolveResult(new ParameterizedType(def, typeArguments));
1647
return new TypeResolveResult(def);
1653
/// Creates a MemberLookup instance using this resolver's settings.
1655
public MemberLookup CreateMemberLookup()
1657
ITypeDefinition currentTypeDefinition = this.CurrentTypeDefinition;
1658
bool isInEnumMemberInitializer = this.CurrentMember != null && this.CurrentMember.EntityType == EntityType.Field
1659
&& currentTypeDefinition != null && currentTypeDefinition.Kind == TypeKind.Enum;
1660
return new MemberLookup(currentTypeDefinition, this.Compilation.MainAssembly, isInEnumMemberInitializer);
1664
/// Creates a MemberLookup instance using this resolver's settings.
1666
public MemberLookup CreateMemberLookup(NameLookupMode lookupMode)
1668
if (lookupMode == NameLookupMode.BaseTypeReference && this.CurrentTypeDefinition != null) {
1669
// When looking up a base type reference, treat us as being outside the current type definition
1670
// for accessibility purposes.
1671
// This avoids a stack overflow when referencing a protected class nested inside the base class
1672
// of a parent class. (NameLookupTests.InnerClassInheritingFromProtectedBaseInnerClassShouldNotCauseStackOverflow)
1673
return new MemberLookup(this.CurrentTypeDefinition.DeclaringTypeDefinition, this.Compilation.MainAssembly, false);
1675
return CreateMemberLookup();
1680
#region ResolveIdentifierInObjectInitializer
1681
public ResolveResult ResolveIdentifierInObjectInitializer(string identifier)
1683
MemberLookup memberLookup = CreateMemberLookup();
1684
return memberLookup.Lookup(this.CurrentObjectInitializer, identifier, EmptyList<IType>.Instance, false);
1688
#region GetExtensionMethods
1690
/// Gets all extension methods that are available in the current context.
1692
/// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
1693
/// <param name="typeArguments">Explicitly provided type arguments.
1694
/// An empty list will return all matching extension method definitions;
1695
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
1696
/// with the matching number of type parameters.</param>
1698
/// The results are stored in nested lists because they are grouped by using scope.
1699
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
1700
/// the return value will be
1702
/// new List { all extensions from MoreExtensions },
1703
/// new List { all extensions from SomeExtensions }
1706
public List<List<IMethod>> GetExtensionMethods(string name = null, IList<IType> typeArguments = null)
1708
return GetExtensionMethods(null, name, typeArguments);
1712
/// Gets the extension methods that are called 'name'
1713
/// and are applicable with a first argument type of 'targetType'.
1715
/// <param name="targetType">Type of the 'this' argument</param>
1716
/// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
1717
/// <param name="typeArguments">Explicitly provided type arguments.
1718
/// An empty list will return all matching extension method definitions;
1719
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
1720
/// with the matching number of type parameters.</param>
1721
/// <param name="substituteInferredTypes">
1722
/// Specifies whether to produce a <see cref="SpecializedMethod"/>
1723
/// when type arguments could be inferred from <paramref name="targetType"/>. This parameter
1724
/// is only used for inferred types and has no effect if <paramref name="typeArguments"/> is non-empty.
1727
/// The results are stored in nested lists because they are grouped by using scope.
1728
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
1729
/// the return value will be
1731
/// new List { all extensions from MoreExtensions },
1732
/// new List { all extensions from SomeExtensions }
1735
public List<List<IMethod>> GetExtensionMethods(IType targetType, string name = null, IList<IType> typeArguments = null, bool substituteInferredTypes = false)
1737
List<List<IMethod>> extensionMethodGroups = new List<List<IMethod>>();
1738
foreach (var inputGroup in GetAllExtensionMethods()) {
1739
List<IMethod> outputGroup = new List<IMethod>();
1740
foreach (var method in inputGroup) {
1741
if (name != null && method.Name != name)
1744
IType[] inferredTypes;
1745
if (typeArguments != null && typeArguments.Count > 0) {
1746
if (method.TypeParameters.Count != typeArguments.Count)
1748
SpecializedMethod sm = new SpecializedMethod(method, new TypeParameterSubstitution(null, typeArguments));
1749
if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, false, out inferredTypes))
1750
outputGroup.Add(sm);
1752
if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) {
1753
if (substituteInferredTypes && inferredTypes != null) {
1754
outputGroup.Add(new SpecializedMethod(method, new TypeParameterSubstitution(null, inferredTypes)));
1756
outputGroup.Add(method);
1761
if (outputGroup.Count > 0)
1762
extensionMethodGroups.Add(outputGroup);
1764
return extensionMethodGroups;
1768
/// Checks whether the specified extension method is eligible on the target type.
1770
/// <param name="targetType">Target type that is passed as first argument to the extension method.</param>
1771
/// <param name="method">The extension method.</param>
1772
/// <param name="useTypeInference">Whether to perform type inference for the method.
1773
/// Use <c>false</c> if <paramref name="method"/> is already specialized (e.g. when type arguments were given explicitly).
1774
/// Otherwise, use <c>true</c>.
1776
/// <param name="outInferredTypes">If the method is generic and <paramref name="useTypeInference"/> is <c>true</c>,
1777
/// and at least some of the type arguments could be inferred, this parameter receives the inferred type arguments.
1778
/// Since only the type for the first parameter is considered, not all type arguments may be inferred.
1779
/// If an array is returned, any slot with an uninferred type argument will be set to the method's
1780
/// corresponding type parameter.
1782
public static bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
1784
if (targetType == null)
1785
throw new ArgumentNullException("targetType");
1787
throw new ArgumentNullException("method");
1788
var compilation = method.Compilation;
1789
return IsEligibleExtensionMethod(compilation, CSharpConversions.Get(compilation), targetType, method, useTypeInference, out outInferredTypes);
1792
static bool IsEligibleExtensionMethod(ICompilation compilation, CSharpConversions conversions, IType targetType, IMethod method, bool useTypeInference, out IType[] outInferredTypes)
1794
outInferredTypes = null;
1795
if (targetType == null)
1797
if (method.Parameters.Count == 0)
1799
IType thisParameterType = method.Parameters[0].Type;
1800
if (useTypeInference && method.TypeParameters.Count > 0) {
1801
// We need to infer type arguments from targetType:
1802
TypeInference ti = new TypeInference(compilation, conversions);
1803
ResolveResult[] arguments = { new ResolveResult(targetType) };
1804
IType[] parameterTypes = { method.Parameters[0].Type };
1806
var inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out success);
1807
var substitution = new TypeParameterSubstitution(null, inferredTypes);
1808
// Validate that the types that could be inferred (aren't unknown) satisfy the constraints:
1809
bool hasInferredTypes = false;
1810
for (int i = 0; i < inferredTypes.Length; i++) {
1811
if (inferredTypes[i].Kind != TypeKind.Unknown && inferredTypes[i].Kind != TypeKind.UnboundTypeArgument) {
1812
hasInferredTypes = true;
1813
if (!OverloadResolution.ValidateConstraints(method.TypeParameters[i], inferredTypes[i], substitution, conversions))
1816
inferredTypes[i] = method.TypeParameters[i]; // do not substitute types that could not be inferred
1819
if (hasInferredTypes)
1820
outInferredTypes = inferredTypes;
1821
thisParameterType = thisParameterType.AcceptVisitor(substitution);
1823
Conversion c = conversions.ImplicitConversion(targetType, thisParameterType);
1824
return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion);
1828
/// Gets all extension methods available in the current using scope.
1829
/// This list includes unaccessible
1831
IList<List<IMethod>> GetAllExtensionMethods()
1833
var currentUsingScope = context.CurrentUsingScope;
1834
if (currentUsingScope == null)
1835
return EmptyList<List<IMethod>>.Instance;
1836
List<List<IMethod>> extensionMethodGroups = LazyInit.VolatileRead(ref currentUsingScope.AllExtensionMethods);
1837
if (extensionMethodGroups != null) {
1838
return extensionMethodGroups;
1840
extensionMethodGroups = new List<List<IMethod>>();
1842
for (ResolvedUsingScope scope = currentUsingScope; scope != null; scope = scope.Parent) {
1843
INamespace ns = scope.Namespace;
1845
m = GetExtensionMethods(ns).ToList();
1847
extensionMethodGroups.Add(m);
1852
.SelectMany(importedNamespace => GetExtensionMethods(importedNamespace))
1855
extensionMethodGroups.Add(m);
1857
return LazyInit.GetOrSet(ref currentUsingScope.AllExtensionMethods, extensionMethodGroups);
1860
IEnumerable<IMethod> GetExtensionMethods(INamespace ns)
1862
// TODO: maybe make this a property on INamespace?
1865
where c.IsStatic && c.HasExtensionMethods && c.TypeParameters.Count == 0
1867
where m.IsExtensionMethod
1872
#region ResolveInvocation
1874
/// Resolves an invocation.
1876
/// <param name="target">The target of the invocation. Usually a MethodGroupResolveResult.</param>
1877
/// <param name="arguments">
1878
/// Arguments passed to the method.
1879
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
1881
/// <param name="argumentNames">
1882
/// The argument names. Pass the null string for positional arguments.
1884
/// <returns>InvocationResolveResult or UnknownMethodResolveResult</returns>
1885
public ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
1887
// C# 4.0 spec: Ā§7.6.5
1889
if (target.Type.Kind == TypeKind.Dynamic)
1890
return DynamicResult;
1892
MethodGroupResolveResult mgrr = target as MethodGroupResolveResult;
1894
OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, conversions: conversions);
1895
if (or.BestCandidate != null) {
1896
if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult))
1897
return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetResult.Type));
1899
return or.CreateResolveResult(mgrr.TargetResult);
1901
// No candidate found at all (not even an inapplicable one).
1902
// This can happen with empty method groups (as sometimes used with extension methods)
1903
return new UnknownMethodResolveResult(
1904
mgrr.TargetType, mgrr.MethodName, mgrr.TypeArguments, CreateParameters(arguments, argumentNames));
1907
UnknownMemberResolveResult umrr = target as UnknownMemberResolveResult;
1909
return new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames));
1911
UnknownIdentifierResolveResult uirr = target as UnknownIdentifierResolveResult;
1912
if (uirr != null && CurrentTypeDefinition != null) {
1913
return new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList<IType>.Instance, CreateParameters(arguments, argumentNames));
1915
IMethod invokeMethod = target.Type.GetDelegateInvokeMethod();
1916
if (invokeMethod != null) {
1917
OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, conversions: conversions);
1918
or.AddCandidate(invokeMethod);
1919
return new CSharpInvocationResolveResult(
1920
target, invokeMethod, //invokeMethod.ReturnType.Resolve(context),
1921
or.GetArgumentsWithConversions(), or.BestCandidateErrors,
1922
isExpandedForm: or.BestCandidateIsExpandedForm,
1923
isDelegateInvocation: true,
1924
argumentToParameterMap: or.GetArgumentToParameterMap());
1929
List<IParameter> CreateParameters(ResolveResult[] arguments, string[] argumentNames)
1931
List<IParameter> list = new List<IParameter>();
1932
if (argumentNames == null) {
1933
argumentNames = new string[arguments.Length];
1935
if (argumentNames.Length != arguments.Length)
1936
throw new ArgumentException();
1937
argumentNames = (string[])argumentNames.Clone();
1939
for (int i = 0; i < arguments.Length; i++) {
1940
// invent argument names where necessary:
1941
if (argumentNames[i] == null) {
1942
string newArgumentName = GuessParameterName(arguments[i]);
1943
if (argumentNames.Contains(newArgumentName)) {
1944
// disambiguate argument name (e.g. add a number)
1948
newName = newArgumentName + num.ToString();
1950
} while(argumentNames.Contains(newName));
1951
newArgumentName = newName;
1953
argumentNames[i] = newArgumentName;
1956
// create the parameter:
1957
ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
1959
list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], isRef: brrr.IsRef, isOut: brrr.IsOut));
1961
// argument might be a lambda or delegate type, so we have to try to guess the delegate type
1962
IType type = arguments[i].Type;
1963
if (type.Kind == TypeKind.Null || type.Kind == TypeKind.Unknown) {
1964
list.Add(new DefaultParameter(compilation.FindType(KnownTypeCode.Object), argumentNames[i]));
1966
list.Add(new DefaultParameter(type, argumentNames[i]));
1973
static string GuessParameterName(ResolveResult rr)
1975
MemberResolveResult mrr = rr as MemberResolveResult;
1977
return mrr.Member.Name;
1979
UnknownMemberResolveResult umrr = rr as UnknownMemberResolveResult;
1981
return umrr.MemberName;
1983
MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult;
1985
return mgrr.MethodName;
1987
LocalResolveResult vrr = rr as LocalResolveResult;
1989
return MakeParameterName(vrr.Variable.Name);
1991
if (rr.Type.Kind != TypeKind.Unknown && !string.IsNullOrEmpty(rr.Type.Name)) {
1992
return MakeParameterName(rr.Type.Name);
1998
static string MakeParameterName(string variableName)
2000
if (string.IsNullOrEmpty(variableName))
2002
if (variableName.Length > 1 && variableName[0] == '_')
2003
variableName = variableName.Substring(1);
2004
return char.ToLower(variableName[0]) + variableName.Substring(1);
2008
#region ResolveIndexer
2010
/// Resolves an indexer access.
2012
/// <param name="target">Target expression.</param>
2013
/// <param name="arguments">
2014
/// Arguments passed to the indexer.
2015
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2017
/// <param name="argumentNames">
2018
/// The argument names. Pass the null string for positional arguments.
2020
/// <returns>ArrayAccessResolveResult, InvocationResolveResult, or ErrorResolveResult</returns>
2021
public ResolveResult ResolveIndexer(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
2023
switch (target.Type.Kind) {
2024
case TypeKind.Dynamic:
2025
for (int i = 0; i < arguments.Length; i++) {
2026
arguments[i] = Convert(arguments[i], SpecialType.Dynamic);
2028
return new ArrayAccessResolveResult(SpecialType.Dynamic, target, arguments);
2030
case TypeKind.Array:
2031
case TypeKind.Pointer:
2032
// Ā§7.6.6.1 Array access / Ā§18.5.3 Pointer element access
2033
AdjustArrayAccessArguments(arguments);
2034
return new ArrayAccessResolveResult(((TypeWithElementType)target.Type).ElementType, target, arguments);
2037
// Ā§7.6.6.2 Indexer access
2038
OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, conversions: conversions);
2039
MemberLookup lookup = CreateMemberLookup();
2040
var indexers = lookup.LookupIndexers(target.Type);
2041
or.AddMethodLists(indexers);
2042
if (or.BestCandidate != null) {
2043
return or.CreateResolveResult(target);
2050
/// Converts all arguments to int,uint,long or ulong.
2052
void AdjustArrayAccessArguments(ResolveResult[] arguments)
2054
for (int i = 0; i < arguments.Length; i++) {
2055
if (!(TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int32)) ||
2056
TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt32)) ||
2057
TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.Int64)) ||
2058
TryConvert(ref arguments[i], compilation.FindType(KnownTypeCode.UInt64))))
2060
// conversion failed
2061
arguments[i] = Convert(arguments[i], compilation.FindType(KnownTypeCode.Int32), Conversion.None);
2067
#region ResolveObjectCreation
2069
/// Resolves an object creation.
2071
/// <param name="type">Type of the object to create.</param>
2072
/// <param name="arguments">
2073
/// Arguments passed to the constructor.
2074
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2076
/// <param name="argumentNames">
2077
/// The argument names. Pass the null string for positional arguments.
2079
/// <param name="allowProtectedAccess">
2080
/// Whether to allow calling protected constructors.
2081
/// This should be false except when resolving constructor initializers.
2083
/// <param name="initializerStatements">
2084
/// Statements for Objects/Collections initializer.
2085
/// <see cref="InvocationResolveResult.InitializerStatements"/>
2087
/// <returns>InvocationResolveResult or ErrorResolveResult</returns>
2088
public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList<ResolveResult> initializerStatements = null)
2090
if (type.Kind == TypeKind.Delegate && arguments.Length == 1) {
2091
return Convert(arguments[0], type);
2093
OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, conversions: conversions);
2094
MemberLookup lookup = CreateMemberLookup();
2095
foreach (IMethod ctor in type.GetConstructors()) {
2096
if (lookup.IsAccessible(ctor, allowProtectedAccess))
2097
or.AddCandidate(ctor);
2099
or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible);
2101
if (or.BestCandidate != null) {
2102
return or.CreateResolveResult(null, initializerStatements);
2104
return new ErrorResolveResult(type);
2109
#region ResolveSizeOf
2111
/// Resolves 'sizeof(type)'.
2113
public ResolveResult ResolveSizeOf(IType type)
2115
IType int32 = compilation.FindType(KnownTypeCode.Int32);
2117
switch (ReflectionHelper.GetTypeCode(type)) {
2118
case TypeCode.Boolean:
2119
case TypeCode.SByte:
2124
case TypeCode.Int16:
2125
case TypeCode.UInt16:
2128
case TypeCode.Int32:
2129
case TypeCode.UInt32:
2130
case TypeCode.Single:
2133
case TypeCode.Int64:
2134
case TypeCode.UInt64:
2135
case TypeCode.Double:
2139
return new ResolveResult(int32);
2141
return new ConstantResolveResult(int32, size);
2145
#region Resolve This/Base Reference
2147
/// Resolves 'this'.
2149
public ResolveResult ResolveThisReference()
2151
ITypeDefinition t = CurrentTypeDefinition;
2153
if (t.TypeParameterCount != 0) {
2154
// Self-parameterize the type
2155
return new ThisResolveResult(new ParameterizedType(t, t.TypeParameters));
2157
return new ThisResolveResult(t);
2164
/// Resolves 'base'.
2166
public ResolveResult ResolveBaseReference()
2168
ITypeDefinition t = CurrentTypeDefinition;
2170
foreach (IType baseType in t.DirectBaseTypes) {
2171
if (baseType.Kind != TypeKind.Unknown && baseType.Kind != TypeKind.Interface) {
2172
return new ThisResolveResult(baseType, causesNonVirtualInvocation: true);
2180
#region ResolveConditional
2182
/// Converts the input to <c>bool</c> using the rules for boolean expressions.
2183
/// That is, <c>operator true</c> is used if a regular conversion to <c>bool</c> is not possible.
2185
public ResolveResult ResolveCondition(ResolveResult input)
2188
throw new ArgumentNullException("input");
2189
IType boolean = compilation.FindType(KnownTypeCode.Boolean);
2190
Conversion c = conversions.ImplicitConversion(input, boolean);
2192
var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault();
2193
if (opTrue != null) {
2194
c = Conversion.UserDefinedImplicitConversion(opTrue, false);
2197
return Convert(input, boolean, c);
2200
public ResolveResult ResolveConditional(ResolveResult condition, ResolveResult trueExpression, ResolveResult falseExpression)
2202
// C# 4.0 spec Ā§7.14: Conditional operator
2206
if (trueExpression.Type.Kind == TypeKind.Dynamic || falseExpression.Type.Kind == TypeKind.Dynamic) {
2207
resultType = SpecialType.Dynamic;
2208
isValid = TryConvert(ref trueExpression, resultType) & TryConvert(ref falseExpression, resultType);
2209
} else if (HasType(trueExpression) && HasType(falseExpression)) {
2210
Conversion t2f = conversions.ImplicitConversion(trueExpression, falseExpression.Type);
2211
Conversion f2t = conversions.ImplicitConversion(falseExpression, trueExpression.Type);
2212
// The operator is valid:
2213
// a) if there's a conversion in one direction but not the other
2214
// b) if there are conversions in both directions, and the types are equivalent
2215
if (IsBetterConditionalConversion(t2f, f2t)) {
2216
resultType = falseExpression.Type;
2218
trueExpression = Convert(trueExpression, resultType, t2f);
2219
} else if (IsBetterConditionalConversion(f2t, t2f)) {
2220
resultType = trueExpression.Type;
2222
falseExpression = Convert(falseExpression, resultType, f2t);
2224
resultType = trueExpression.Type;
2225
isValid = trueExpression.Type.Equals(falseExpression.Type);
2227
} else if (HasType(trueExpression)) {
2228
resultType = trueExpression.Type;
2229
isValid = TryConvert(ref falseExpression, resultType);
2230
} else if (HasType(falseExpression)) {
2231
resultType = falseExpression.Type;
2232
isValid = TryConvert(ref trueExpression, resultType);
2236
condition = ResolveCondition(condition);
2238
if (condition.IsCompileTimeConstant && trueExpression.IsCompileTimeConstant && falseExpression.IsCompileTimeConstant) {
2239
bool? val = condition.ConstantValue as bool?;
2241
return trueExpression;
2242
else if (val == false)
2243
return falseExpression;
2245
return new OperatorResolveResult(resultType, System.Linq.Expressions.ExpressionType.Conditional,
2246
condition, trueExpression, falseExpression);
2248
return new ErrorResolveResult(resultType);
2252
bool IsBetterConditionalConversion(Conversion c1, Conversion c2)
2254
// Valid is better than ImplicitConstantExpressionConversion is better than invalid
2257
if (c1 != Conversion.ImplicitConstantExpressionConversion && c2 == Conversion.ImplicitConstantExpressionConversion)
2262
bool HasType(ResolveResult r)
2264
return r.Type.Kind != TypeKind.Unknown && r.Type.Kind != TypeKind.Null;
2268
#region ResolvePrimitive
2269
public ResolveResult ResolvePrimitive(object value)
2271
if (value == null) {
2274
TypeCode typeCode = Type.GetTypeCode(value.GetType());
2275
IType type = compilation.FindType(typeCode);
2276
return new ConstantResolveResult(type, value);
2281
#region ResolveDefaultValue
2282
public ResolveResult ResolveDefaultValue(IType type)
2284
return new ConstantResolveResult(type, GetDefaultValue(type));
2287
public static object GetDefaultValue(IType type)
2289
switch (ReflectionHelper.GetTypeCode(type)) {
2290
case TypeCode.Boolean:
2294
case TypeCode.SByte:
2298
case TypeCode.Int16:
2300
case TypeCode.UInt16:
2302
case TypeCode.Int32:
2304
case TypeCode.UInt32:
2306
case TypeCode.Int64:
2308
case TypeCode.UInt64:
2310
case TypeCode.Single:
2312
case TypeCode.Double:
2314
case TypeCode.Decimal:
2322
#region ResolveArrayCreation
2324
/// Resolves an array creation.
2326
/// <param name="elementType">
2327
/// The array element type.
2328
/// Pass null to resolve an implicitly-typed array creation.
2330
/// <param name="dimensions">
2331
/// The number of array dimensions.
2333
/// <param name="sizeArguments">
2334
/// The size arguments. May be null if no explicit size was given.
2335
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2337
/// <param name="initializerElements">
2338
/// The initializer elements. May be null if no array initializer was specified.
2339
/// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s!
2341
public ArrayCreateResolveResult ResolveArrayCreation(IType elementType, int dimensions = 1, ResolveResult[] sizeArguments = null, ResolveResult[] initializerElements = null)
2343
if (sizeArguments != null && dimensions != Math.Max(1, sizeArguments.Length))
2344
throw new ArgumentException("dimensions and sizeArguments.Length don't match");
2345
if (elementType == null) {
2346
TypeInference typeInference = new TypeInference(compilation, conversions);
2348
elementType = typeInference.GetBestCommonType(initializerElements, out success);
2350
IType arrayType = new ArrayType(compilation, elementType, dimensions);
2352
if (sizeArguments != null)
2353
AdjustArrayAccessArguments(sizeArguments);
2355
if (initializerElements != null) {
2356
for (int i = 0; i < initializerElements.Length; i++) {
2357
initializerElements[i] = Convert(initializerElements[i], elementType);
2360
return new ArrayCreateResolveResult(arrayType, sizeArguments, initializerElements);
2364
public ResolveResult ResolveTypeOf(IType referencedType)
2366
return new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), referencedType);
2369
#region ResolveAssignment
2370
public ResolveResult ResolveAssignment(AssignmentOperatorType op, ResolveResult lhs, ResolveResult rhs)
2372
var linqOp = AssignmentExpression.GetLinqNodeType(op, this.CheckForOverflow);
2373
var bop = AssignmentExpression.GetCorrespondingBinaryOperator(op);
2375
return new OperatorResolveResult(lhs.Type, linqOp, lhs, this.Convert(rhs, lhs.Type));
2377
ResolveResult bopResult = ResolveBinaryOperator(bop.Value, lhs, rhs);
2378
OperatorResolveResult opResult = bopResult as OperatorResolveResult;
2379
if (opResult == null || opResult.Operands.Count != 2)
2381
return new OperatorResolveResult(lhs.Type, linqOp, opResult.UserDefinedOperatorMethod, opResult.IsLiftedOperator,
2382
new [] { lhs, opResult.Operands[1] });