public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression)
{
if (expression.Type.Kind == TypeKind.Dynamic) {
if (op == UnaryOperatorType.Await) {
return new AwaitResolveResult(SpecialType.Dynamic, new DynamicInvocationResolveResult(new DynamicMemberResolveResult(expression, "GetAwaiter"), DynamicInvocationType.Invocation, EmptyList<ResolveResult>.Instance), SpecialType.Dynamic, null, null, null);
}
else {
return UnaryOperatorResolveResult(SpecialType.Dynamic, op, expression);
}
}
// C# 4.0 spec: §7.3.3 Unary operator overload resolution
string overloadableOperatorName = GetOverloadableOperatorName(op);
if (overloadableOperatorName == null) {
switch (op) {
case UnaryOperatorType.Dereference:
PointerType p = expression.Type as PointerType;
if (p != null)
return UnaryOperatorResolveResult(p.ElementType, op, expression);
else
return ErrorResult;
case UnaryOperatorType.AddressOf:
return UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression);
case UnaryOperatorType.Await: {
ResolveResult getAwaiterMethodGroup = ResolveMemberAccess(expression, "GetAwaiter", EmptyList<IType>.Instance, NameLookupMode.InvocationTarget);
ResolveResult getAwaiterInvocation = ResolveInvocation(getAwaiterMethodGroup, new ResolveResult[0], argumentNames: null, allowOptionalParameters: false);
var lookup = CreateMemberLookup();
IMethod getResultMethod;
IType awaitResultType;
var getResultMethodGroup = lookup.Lookup(getAwaiterInvocation, "GetResult", EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
if (getResultMethodGroup != null) {
var getResultOR = getResultMethodGroup.PerformOverloadResolution(compilation, new ResolveResult[0], allowExtensionMethods: false, conversions: conversions);
getResultMethod = getResultOR.FoundApplicableCandidate ? getResultOR.GetBestCandidateWithSubstitutedTypeArguments() as IMethod : null;
awaitResultType = getResultMethod != null ? getResultMethod.ReturnType : SpecialType.UnknownType;
}
else {
getResultMethod = null;
awaitResultType = SpecialType.UnknownType;
}
var isCompletedRR = lookup.Lookup(getAwaiterInvocation, "IsCompleted", EmptyList<IType>.Instance, false);
var isCompletedProperty = (isCompletedRR is MemberResolveResult ? ((MemberResolveResult)isCompletedRR).Member as IProperty : null);
if (isCompletedProperty != null && (!isCompletedProperty.ReturnType.IsKnownType(KnownTypeCode.Boolean) || !isCompletedProperty.CanGet))
isCompletedProperty = null;
var interfaceOnCompleted = compilation.FindType(KnownTypeCode.INotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "OnCompleted");
var interfaceUnsafeOnCompleted = compilation.FindType(KnownTypeCode.ICriticalNotifyCompletion).GetMethods().FirstOrDefault(x => x.Name == "UnsafeOnCompleted");
IMethod onCompletedMethod = null;
var candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceUnsafeOnCompleted)).ToList();
if (candidates.Count == 0) {
candidates = getAwaiterInvocation.Type.GetMethods().Where(x => x.ImplementedInterfaceMembers.Select(y => y.MemberDefinition).Contains(interfaceOnCompleted)).ToList();
if (candidates.Count == 1)
onCompletedMethod = candidates[0];
}
else if (candidates.Count == 1) {
onCompletedMethod = candidates[0];
}
return new AwaitResolveResult(awaitResultType, getAwaiterInvocation, getAwaiterInvocation.Type, isCompletedProperty, onCompletedMethod, getResultMethod);
}
default:
return ErrorResolveResult.UnknownError;
}
}
// If the type is nullable, get the underlying type:
IType type = NullableType.GetUnderlyingType(expression.Type);
bool isNullable = NullableType.IsNullable(expression.Type);
// the operator is overloadable:
OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { expression });
foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) {
userDefinedOperatorOR.AddCandidate(candidate);
}
if (userDefinedOperatorOR.FoundApplicableCandidate) {
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
}
expression = UnaryNumericPromotion(op, ref type, isNullable, expression);
CSharpOperators.OperatorMethod[] methodGroup;
CSharpOperators operators = CSharpOperators.Get(compilation);
switch (op) {
case UnaryOperatorType.Increment:
case UnaryOperatorType.Decrement:
case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.PostDecrement:
// C# 4.0 spec: §7.6.9 Postfix increment and decrement operators
// C# 4.0 spec: §7.7.5 Prefix increment and decrement operators
TypeCode code = ReflectionHelper.GetTypeCode(type);
if ((code >= TypeCode.Char && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer)
return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
else
return new ErrorResolveResult(expression.Type);
case UnaryOperatorType.Plus:
methodGroup = operators.UnaryPlusOperators;
break;
case UnaryOperatorType.Minus:
methodGroup = CheckForOverflow ? operators.CheckedUnaryMinusOperators : operators.UncheckedUnaryMinusOperators;
break;
case UnaryOperatorType.Not:
methodGroup = operators.LogicalNegationOperators;
break;
case UnaryOperatorType.BitNot:
if (type.Kind == TypeKind.Enum) {
if (expression.IsCompileTimeConstant && !isNullable && expression.ConstantValue != null) {
// evaluate as (E)(~(U)x);
var U = compilation.FindType(expression.ConstantValue.GetType());
var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue);
var rr = ResolveUnaryOperator(op, unpackedEnum);
rr = WithCheckForOverflow(false).ResolveCast(type, rr);
if (rr.IsCompileTimeConstant)
return rr;
}
return UnaryOperatorResolveResult(expression.Type, op, expression, isNullable);
} else {
methodGroup = operators.BitwiseComplementOperators;
break;
}
default:
throw new InvalidOperationException();
}
OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { expression });
foreach (var candidate in methodGroup) {
builtinOperatorOR.AddCandidate(candidate);
}
CSharpOperators.UnaryOperatorMethod m = (CSharpOperators.UnaryOperatorMethod)builtinOperatorOR.BestCandidate;
IType resultType = m.ReturnType;
if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
if (userDefinedOperatorOR.BestCandidate != null) {
// If there are any user-defined operators, prefer those over the built-in operators.
// It'll be a more informative error.
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
} else if (builtinOperatorOR.BestCandidateAmbiguousWith != null) {
// If the best candidate is ambiguous, just use the input type instead
// of picking one of the ambiguous overloads.
return new ErrorResolveResult(expression.Type);
} else {
return new ErrorResolveResult(resultType);
}
} else if (expression.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
object val;
try {
val = m.Invoke(this, expression.ConstantValue);
} catch (ArithmeticException) {
return new ErrorResolveResult(resultType);
}
return new ConstantResolveResult(resultType, val);
} else {
expression = Convert(expression, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
return UnaryOperatorResolveResult(resultType, op, expression,
builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
}
}