mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1177 lines
37 KiB
1177 lines
37 KiB
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
#nullable enable |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Text; |
|
|
|
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
|
using ICSharpCode.Decompiler.Util; |
|
|
|
namespace ICSharpCode.Decompiler.CSharp.Resolver |
|
{ |
|
sealed class CSharpOperators |
|
{ |
|
readonly ICompilation compilation; |
|
|
|
private CSharpOperators(ICompilation compilation) |
|
{ |
|
this.compilation = compilation; |
|
InitParameterArrays(); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the CSharpOperators instance for the specified <see cref="ICompilation"/>. |
|
/// This will make use of the context's cache manager (if available) to reuse the CSharpOperators instance. |
|
/// </summary> |
|
public static CSharpOperators Get(ICompilation compilation) |
|
{ |
|
CacheManager cache = compilation.CacheManager; |
|
CSharpOperators? operators = (CSharpOperators?)cache.GetShared(typeof(CSharpOperators)); |
|
if (operators == null) |
|
{ |
|
operators = (CSharpOperators)cache.GetOrAddShared(typeof(CSharpOperators), new CSharpOperators(compilation)); |
|
} |
|
return operators; |
|
} |
|
|
|
#region class OperatorMethod |
|
OperatorMethod[] Lift(params OperatorMethod[] methods) |
|
{ |
|
List<OperatorMethod> result = new List<OperatorMethod>(methods); |
|
foreach (OperatorMethod method in methods) |
|
{ |
|
OperatorMethod? lifted = method.Lift(this); |
|
if (lifted != null) |
|
result.Add(lifted); |
|
} |
|
return result.ToArray(); |
|
} |
|
|
|
IParameter[] normalParameters = new IParameter[(int)(TypeCode.String + 1 - TypeCode.Object)]; |
|
IParameter[] nullableParameters = new IParameter[(int)(TypeCode.Decimal + 1 - TypeCode.Boolean)]; |
|
|
|
void InitParameterArrays() |
|
{ |
|
for (TypeCode i = TypeCode.Object; i <= TypeCode.String; i++) |
|
{ |
|
normalParameters[i - TypeCode.Object] = new DefaultParameter(compilation.FindType(i), string.Empty); |
|
} |
|
for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) |
|
{ |
|
IType type = NullableType.Create(compilation, compilation.FindType(i)); |
|
nullableParameters[i - TypeCode.Boolean] = new DefaultParameter(type, string.Empty); |
|
} |
|
} |
|
|
|
IParameter MakeParameter(TypeCode code) |
|
{ |
|
return normalParameters[code - TypeCode.Object]; |
|
} |
|
|
|
IParameter MakeNullableParameter(IParameter normalParameter) |
|
{ |
|
for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) |
|
{ |
|
if (normalParameter == normalParameters[i - TypeCode.Object]) |
|
return nullableParameters[i - TypeCode.Boolean]; |
|
} |
|
throw new ArgumentException(); |
|
} |
|
|
|
internal class OperatorMethod : IParameterizedMember |
|
{ |
|
readonly ICompilation compilation; |
|
internal readonly List<IParameter> parameters = new List<IParameter>(); |
|
|
|
protected OperatorMethod(ICompilation compilation) |
|
{ |
|
this.compilation = compilation; |
|
} |
|
|
|
public IReadOnlyList<IParameter> Parameters { |
|
get { return parameters; } |
|
} |
|
|
|
public IType ReturnType { get; internal set; } = null!; // initialized by derived class ctor |
|
|
|
public ICompilation Compilation { |
|
get { return compilation; } |
|
} |
|
|
|
public virtual OperatorMethod? Lift(CSharpOperators operators) |
|
{ |
|
return null; |
|
} |
|
|
|
public System.Reflection.Metadata.EntityHandle MetadataToken => default; |
|
|
|
ITypeDefinition? IEntity.DeclaringTypeDefinition { |
|
get { return null; } |
|
} |
|
|
|
public IType DeclaringType => SpecialType.UnknownType; |
|
|
|
IMember IMember.MemberDefinition { |
|
get { return this; } |
|
} |
|
|
|
IEnumerable<IMember> IMember.ExplicitlyImplementedInterfaceMembers { |
|
get { return EmptyList<IMember>.Instance; } |
|
} |
|
|
|
bool IMember.IsVirtual { |
|
get { return false; } |
|
} |
|
|
|
bool IMember.IsOverride { |
|
get { return false; } |
|
} |
|
|
|
bool IMember.IsOverridable { |
|
get { return false; } |
|
} |
|
|
|
SymbolKind ISymbol.SymbolKind { |
|
get { return SymbolKind.Operator; } |
|
} |
|
|
|
IEnumerable<IAttribute> IEntity.GetAttributes() => EmptyList<IAttribute>.Instance; |
|
bool IEntity.HasAttribute(KnownAttribute attribute) => false; |
|
IAttribute? IEntity.GetAttribute(KnownAttribute attribute) => null; |
|
|
|
Accessibility IEntity.Accessibility { |
|
get { return Accessibility.Public; } |
|
} |
|
|
|
bool IEntity.IsStatic { |
|
get { return true; } |
|
} |
|
|
|
bool IEntity.IsAbstract { |
|
get { return false; } |
|
} |
|
|
|
bool IEntity.IsSealed { |
|
get { return false; } |
|
} |
|
|
|
bool IMember.IsExplicitInterfaceImplementation { |
|
get { return false; } |
|
} |
|
|
|
IModule IEntity.ParentModule { |
|
get { return compilation.MainModule; } |
|
} |
|
|
|
TypeParameterSubstitution IMember.Substitution { |
|
get { |
|
return TypeParameterSubstitution.Identity; |
|
} |
|
} |
|
|
|
IMember IMember.Specialize(TypeParameterSubstitution substitution) |
|
{ |
|
if (TypeParameterSubstitution.Identity.Equals(substitution)) |
|
return this; |
|
throw new NotSupportedException(); |
|
} |
|
|
|
string INamedElement.FullName { |
|
get { return "operator"; } |
|
} |
|
|
|
public string Name { |
|
get { return "operator"; } |
|
} |
|
|
|
string INamedElement.Namespace { |
|
get { return string.Empty; } |
|
} |
|
|
|
string INamedElement.ReflectionName { |
|
get { return "operator"; } |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
StringBuilder b = new StringBuilder(); |
|
b.Append(ReturnType + " operator("); |
|
for (int i = 0; i < parameters.Count; i++) |
|
{ |
|
if (i > 0) |
|
b.Append(", "); |
|
b.Append(parameters[i].Type); |
|
} |
|
b.Append(')'); |
|
return b.ToString(); |
|
} |
|
|
|
bool IMember.Equals(IMember? obj, TypeVisitor? typeNormalization) |
|
{ |
|
return this == obj; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Unary operator class definitions |
|
internal class UnaryOperatorMethod : OperatorMethod |
|
{ |
|
public virtual bool CanEvaluateAtCompileTime { get { return false; } } |
|
|
|
public virtual object? Invoke(CSharpResolver resolver, object? input) |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
|
|
public UnaryOperatorMethod(ICompilation compilation) : base(compilation) |
|
{ |
|
} |
|
} |
|
|
|
sealed class LambdaUnaryOperatorMethod<T> : UnaryOperatorMethod |
|
{ |
|
readonly Func<T, T> func; |
|
|
|
public LambdaUnaryOperatorMethod(CSharpOperators operators, Func<T, T> func) |
|
: base(operators.compilation) |
|
{ |
|
TypeCode typeCode = Type.GetTypeCode(typeof(T)); |
|
this.ReturnType = operators.compilation.FindType(typeCode); |
|
parameters.Add(operators.MakeParameter(typeCode)); |
|
this.func = func; |
|
} |
|
|
|
public override bool CanEvaluateAtCompileTime { |
|
get { return true; } |
|
} |
|
|
|
public override object? Invoke(CSharpResolver resolver, object? input) |
|
{ |
|
if (input == null) |
|
return null; |
|
return func((T)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T)), input)); |
|
} |
|
|
|
public override OperatorMethod Lift(CSharpOperators operators) |
|
{ |
|
return new LiftedUnaryOperatorMethod(operators, this); |
|
} |
|
} |
|
|
|
sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod, ILiftedOperator |
|
{ |
|
UnaryOperatorMethod baseMethod; |
|
|
|
public LiftedUnaryOperatorMethod(CSharpOperators operators, UnaryOperatorMethod baseMethod) : base(operators.compilation) |
|
{ |
|
this.baseMethod = baseMethod; |
|
this.ReturnType = NullableType.Create(baseMethod.Compilation, baseMethod.ReturnType); |
|
parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); |
|
} |
|
|
|
public IReadOnlyList<IParameter> NonLiftedParameters => baseMethod.Parameters; |
|
public IType NonLiftedReturnType => baseMethod.ReturnType; |
|
} |
|
#endregion |
|
|
|
#region Unary operator definitions |
|
// C# 4.0 spec: §7.7.1 Unary plus operator |
|
OperatorMethod[]? unaryPlusOperators; |
|
|
|
public OperatorMethod[] UnaryPlusOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref unaryPlusOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref unaryPlusOperators, Lift( |
|
new LambdaUnaryOperatorMethod<int>(this, i => +i), |
|
new LambdaUnaryOperatorMethod<uint>(this, i => +i), |
|
new LambdaUnaryOperatorMethod<long>(this, i => +i), |
|
new LambdaUnaryOperatorMethod<ulong>(this, i => +i), |
|
new LambdaUnaryOperatorMethod<float>(this, i => +i), |
|
new LambdaUnaryOperatorMethod<double>(this, i => +i), |
|
new LambdaUnaryOperatorMethod<decimal>(this, i => +i) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.7.2 Unary minus operator |
|
OperatorMethod[]? uncheckedUnaryMinusOperators; |
|
|
|
public OperatorMethod[] UncheckedUnaryMinusOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref uncheckedUnaryMinusOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref uncheckedUnaryMinusOperators, Lift( |
|
new LambdaUnaryOperatorMethod<int>(this, i => unchecked(-i)), |
|
new LambdaUnaryOperatorMethod<long>(this, i => unchecked(-i)), |
|
new LambdaUnaryOperatorMethod<float>(this, i => unchecked(-i)), |
|
new LambdaUnaryOperatorMethod<double>(this, i => unchecked(-i)), |
|
new LambdaUnaryOperatorMethod<decimal>(this, i => unchecked(-i)) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? checkedUnaryMinusOperators; |
|
|
|
public OperatorMethod[] CheckedUnaryMinusOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref checkedUnaryMinusOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref checkedUnaryMinusOperators, Lift( |
|
new LambdaUnaryOperatorMethod<int>(this, i => checked(-i)), |
|
new LambdaUnaryOperatorMethod<long>(this, i => checked(-i)), |
|
new LambdaUnaryOperatorMethod<float>(this, i => checked(-i)), |
|
new LambdaUnaryOperatorMethod<double>(this, i => checked(-i)), |
|
new LambdaUnaryOperatorMethod<decimal>(this, i => checked(-i)) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.7.3 Logical negation operator |
|
OperatorMethod[]? logicalNegationOperators; |
|
|
|
public OperatorMethod[] LogicalNegationOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref logicalNegationOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref logicalNegationOperators, Lift( |
|
new LambdaUnaryOperatorMethod<bool>(this, b => !b) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.7.4 Bitwise complement operator |
|
OperatorMethod[]? bitwiseComplementOperators; |
|
|
|
public OperatorMethod[] BitwiseComplementOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseComplementOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref bitwiseComplementOperators, Lift( |
|
new LambdaUnaryOperatorMethod<int>(this, i => ~i), |
|
new LambdaUnaryOperatorMethod<uint>(this, i => ~i), |
|
new LambdaUnaryOperatorMethod<long>(this, i => ~i), |
|
new LambdaUnaryOperatorMethod<ulong>(this, i => ~i) |
|
)); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region Binary operator class definitions |
|
internal class BinaryOperatorMethod : OperatorMethod |
|
{ |
|
public virtual bool CanEvaluateAtCompileTime { get { return false; } } |
|
public virtual object? Invoke(CSharpResolver resolver, object? lhs, object? rhs) |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
|
|
public BinaryOperatorMethod(ICompilation compilation) : base(compilation) { } |
|
} |
|
|
|
sealed class LambdaBinaryOperatorMethod<T1, T2> : BinaryOperatorMethod |
|
{ |
|
readonly Func<T1, T2, T1> checkedFunc; |
|
readonly Func<T1, T2, T1> uncheckedFunc; |
|
|
|
public LambdaBinaryOperatorMethod(CSharpOperators operators, Func<T1, T2, T1> func) |
|
: this(operators, func, func) |
|
{ |
|
} |
|
|
|
public LambdaBinaryOperatorMethod(CSharpOperators operators, Func<T1, T2, T1> checkedFunc, Func<T1, T2, T1> uncheckedFunc) |
|
: base(operators.compilation) |
|
{ |
|
TypeCode t1 = Type.GetTypeCode(typeof(T1)); |
|
this.ReturnType = operators.compilation.FindType(t1); |
|
parameters.Add(operators.MakeParameter(t1)); |
|
parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2)))); |
|
this.checkedFunc = checkedFunc; |
|
this.uncheckedFunc = uncheckedFunc; |
|
} |
|
|
|
public override bool CanEvaluateAtCompileTime { |
|
get { return true; } |
|
} |
|
|
|
public override object? Invoke(CSharpResolver resolver, object? lhs, object? rhs) |
|
{ |
|
if (lhs == null || rhs == null) |
|
return null; |
|
Func<T1, T2, T1> func = resolver.CheckForOverflow ? checkedFunc : uncheckedFunc; |
|
return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs), |
|
(T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs)); |
|
} |
|
|
|
public override OperatorMethod Lift(CSharpOperators operators) |
|
{ |
|
return new LiftedBinaryOperatorMethod(operators, this); |
|
} |
|
} |
|
|
|
sealed class LiftedBinaryOperatorMethod : BinaryOperatorMethod, ILiftedOperator |
|
{ |
|
readonly BinaryOperatorMethod baseMethod; |
|
|
|
public LiftedBinaryOperatorMethod(CSharpOperators operators, BinaryOperatorMethod baseMethod) |
|
: base(operators.compilation) |
|
{ |
|
this.baseMethod = baseMethod; |
|
this.ReturnType = NullableType.Create(operators.compilation, baseMethod.ReturnType); |
|
parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); |
|
parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[1])); |
|
} |
|
|
|
public IReadOnlyList<IParameter> NonLiftedParameters => baseMethod.Parameters; |
|
public IType NonLiftedReturnType => baseMethod.ReturnType; |
|
} |
|
#endregion |
|
|
|
#region Arithmetic operators |
|
// C# 4.0 spec: §7.8.1 Multiplication operator |
|
|
|
OperatorMethod[]? multiplicationOperators; |
|
|
|
public OperatorMethod[] MultiplicationOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref multiplicationOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref multiplicationOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), |
|
new LambdaBinaryOperatorMethod<float, float>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), |
|
new LambdaBinaryOperatorMethod<double, double>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), |
|
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.8.2 Division operator |
|
OperatorMethod[]? divisionOperators; |
|
|
|
public OperatorMethod[] DivisionOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref divisionOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref divisionOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), |
|
new LambdaBinaryOperatorMethod<float, float>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), |
|
new LambdaBinaryOperatorMethod<double, double>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), |
|
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.8.3 Remainder operator |
|
OperatorMethod[]? remainderOperators; |
|
|
|
public OperatorMethod[] RemainderOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref remainderOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref remainderOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), |
|
new LambdaBinaryOperatorMethod<float, float>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), |
|
new LambdaBinaryOperatorMethod<double, double>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), |
|
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.8.3 Addition operator |
|
OperatorMethod[]? additionOperators; |
|
|
|
public OperatorMethod[] AdditionOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref additionOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref additionOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), |
|
new LambdaBinaryOperatorMethod<float, float>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), |
|
new LambdaBinaryOperatorMethod<double, double>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), |
|
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), |
|
new StringConcatenation(this, TypeCode.String, TypeCode.String), |
|
new StringConcatenation(this, TypeCode.String, TypeCode.Object), |
|
new StringConcatenation(this, TypeCode.Object, TypeCode.String) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// not in this list, but handled manually: enum addition, delegate combination |
|
sealed class StringConcatenation : BinaryOperatorMethod |
|
{ |
|
bool canEvaluateAtCompileTime; |
|
|
|
public StringConcatenation(CSharpOperators operators, TypeCode p1, TypeCode p2) |
|
: base(operators.compilation) |
|
{ |
|
this.canEvaluateAtCompileTime = p1 == TypeCode.String && p2 == TypeCode.String; |
|
this.ReturnType = operators.compilation.FindType(KnownTypeCode.String); |
|
parameters.Add(operators.MakeParameter(p1)); |
|
parameters.Add(operators.MakeParameter(p2)); |
|
} |
|
|
|
public override bool CanEvaluateAtCompileTime { |
|
get { return canEvaluateAtCompileTime; } |
|
} |
|
|
|
public override object? Invoke(CSharpResolver? resolver, object? lhs, object? rhs) |
|
{ |
|
return string.Concat(lhs, rhs); |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.8.4 Subtraction operator |
|
OperatorMethod[]? subtractionOperators; |
|
|
|
public OperatorMethod[] SubtractionOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref subtractionOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref subtractionOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), |
|
new LambdaBinaryOperatorMethod<float, float>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), |
|
new LambdaBinaryOperatorMethod<double, double>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), |
|
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// C# 4.0 spec: §7.8.5 Shift operators |
|
OperatorMethod[]? shiftLeftOperators; |
|
|
|
public OperatorMethod[] ShiftLeftOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref shiftLeftOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref shiftLeftOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => a << b), |
|
new LambdaBinaryOperatorMethod<uint, int>(this, (a, b) => a << b), |
|
new LambdaBinaryOperatorMethod<long, int>(this, (a, b) => a << b), |
|
new LambdaBinaryOperatorMethod<ulong, int>(this, (a, b) => a << b) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? shiftRightOperators; |
|
|
|
public OperatorMethod[] ShiftRightOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref shiftRightOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref shiftRightOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => a >> b), |
|
new LambdaBinaryOperatorMethod<uint, int>(this, (a, b) => a >> b), |
|
new LambdaBinaryOperatorMethod<long, int>(this, (a, b) => a >> b), |
|
new LambdaBinaryOperatorMethod<ulong, int>(this, (a, b) => a >> b) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? unsignedShiftRightOperators; |
|
|
|
public OperatorMethod[] UnsignedShiftRightOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref unsignedShiftRightOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref unsignedShiftRightOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => (int)((uint)a >> b)), |
|
new LambdaBinaryOperatorMethod<uint, int>(this, (a, b) => a >> b), |
|
new LambdaBinaryOperatorMethod<long, int>(this, (a, b) => (long)((ulong)a >> b)), |
|
new LambdaBinaryOperatorMethod<ulong, int>(this, (a, b) => a >> b) |
|
)); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region Equality operators |
|
sealed class EqualityOperatorMethod : BinaryOperatorMethod |
|
{ |
|
public readonly TypeCode Type; |
|
public readonly bool Negate; |
|
|
|
public EqualityOperatorMethod(CSharpOperators operators, TypeCode type, bool negate) |
|
: base(operators.compilation) |
|
{ |
|
this.Negate = negate; |
|
this.Type = type; |
|
this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean); |
|
parameters.Add(operators.MakeParameter(type)); |
|
parameters.Add(operators.MakeParameter(type)); |
|
} |
|
|
|
public override bool CanEvaluateAtCompileTime { |
|
get { return Type != TypeCode.Object; } |
|
} |
|
|
|
public override object Invoke(CSharpResolver resolver, object? lhs, object? rhs) |
|
{ |
|
if (lhs == null && rhs == null) |
|
return !Negate; // ==: true; !=: false |
|
if (lhs == null || rhs == null) |
|
return Negate; // ==: false; !=: true |
|
lhs = resolver.CSharpPrimitiveCast(Type, lhs); |
|
rhs = resolver.CSharpPrimitiveCast(Type, rhs); |
|
bool equal; |
|
if (Type == TypeCode.Single) |
|
{ |
|
equal = (float)lhs == (float)rhs; |
|
} |
|
else if (Type == TypeCode.Double) |
|
{ |
|
equal = (double)lhs == (double)rhs; |
|
} |
|
else |
|
{ |
|
equal = object.Equals(lhs, rhs); |
|
} |
|
return equal ^ Negate; |
|
} |
|
|
|
public override OperatorMethod? Lift(CSharpOperators operators) |
|
{ |
|
if (Type == TypeCode.Object || Type == TypeCode.String) |
|
return null; |
|
else |
|
return new LiftedEqualityOperatorMethod(operators, this); |
|
} |
|
} |
|
|
|
sealed class LiftedEqualityOperatorMethod : BinaryOperatorMethod, ILiftedOperator |
|
{ |
|
readonly EqualityOperatorMethod baseMethod; |
|
|
|
public LiftedEqualityOperatorMethod(CSharpOperators operators, EqualityOperatorMethod baseMethod) |
|
: base(operators.compilation) |
|
{ |
|
this.baseMethod = baseMethod; |
|
this.ReturnType = baseMethod.ReturnType; |
|
IParameter p = operators.MakeNullableParameter(baseMethod.Parameters[0]); |
|
parameters.Add(p); |
|
parameters.Add(p); |
|
} |
|
|
|
public override bool CanEvaluateAtCompileTime { |
|
get { return baseMethod.CanEvaluateAtCompileTime; } |
|
} |
|
|
|
public override object Invoke(CSharpResolver resolver, object? lhs, object? rhs) |
|
{ |
|
return baseMethod.Invoke(resolver, lhs, rhs); |
|
} |
|
|
|
public IReadOnlyList<IParameter> NonLiftedParameters => baseMethod.Parameters; |
|
public IType NonLiftedReturnType => baseMethod.ReturnType; |
|
} |
|
|
|
// C# 4.0 spec: §7.10 Relational and type-testing operators |
|
static readonly TypeCode[] valueEqualityOperatorsFor = { |
|
TypeCode.Int32, TypeCode.UInt32, |
|
TypeCode.Int64, TypeCode.UInt64, |
|
TypeCode.Single, TypeCode.Double, |
|
TypeCode.Decimal, |
|
TypeCode.Boolean |
|
}; |
|
|
|
OperatorMethod[]? valueEqualityOperators; |
|
|
|
public OperatorMethod[] ValueEqualityOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref valueEqualityOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref valueEqualityOperators, Lift( |
|
valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, false)).ToArray() |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? valueInequalityOperators; |
|
|
|
public OperatorMethod[] ValueInequalityOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref valueInequalityOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref valueInequalityOperators, Lift( |
|
valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, true)).ToArray() |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? referenceEqualityOperators; |
|
|
|
public OperatorMethod[] ReferenceEqualityOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref referenceEqualityOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref referenceEqualityOperators, Lift( |
|
new EqualityOperatorMethod(this, TypeCode.Object, false), |
|
new EqualityOperatorMethod(this, TypeCode.String, false) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? referenceInequalityOperators; |
|
|
|
public OperatorMethod[] ReferenceInequalityOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref referenceInequalityOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref referenceInequalityOperators, Lift( |
|
new EqualityOperatorMethod(this, TypeCode.Object, true), |
|
new EqualityOperatorMethod(this, TypeCode.String, true) |
|
)); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region Relational Operators |
|
sealed class RelationalOperatorMethod<T1, T2> : BinaryOperatorMethod |
|
{ |
|
readonly Func<T1, T2, bool> func; |
|
|
|
public RelationalOperatorMethod(CSharpOperators operators, Func<T1, T2, bool> func) |
|
: base(operators.compilation) |
|
{ |
|
this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean); |
|
parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T1)))); |
|
parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2)))); |
|
this.func = func; |
|
} |
|
|
|
public override bool CanEvaluateAtCompileTime { |
|
get { return true; } |
|
} |
|
|
|
public override object? Invoke(CSharpResolver resolver, object? lhs, object? rhs) |
|
{ |
|
if (lhs == null || rhs == null) |
|
return null; |
|
return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs), |
|
(T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs)); |
|
} |
|
|
|
public override OperatorMethod Lift(CSharpOperators operators) |
|
{ |
|
var lifted = new LiftedBinaryOperatorMethod(operators, this); |
|
lifted.ReturnType = this.ReturnType; // don't lift the return type for relational operators |
|
return lifted; |
|
} |
|
} |
|
|
|
OperatorMethod[]? lessThanOperators; |
|
|
|
public OperatorMethod[] LessThanOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref lessThanOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref lessThanOperators, Lift( |
|
new RelationalOperatorMethod<int, int>(this, (a, b) => a < b), |
|
new RelationalOperatorMethod<uint, uint>(this, (a, b) => a < b), |
|
new RelationalOperatorMethod<long, long>(this, (a, b) => a < b), |
|
new RelationalOperatorMethod<ulong, ulong>(this, (a, b) => a < b), |
|
new RelationalOperatorMethod<float, float>(this, (a, b) => a < b), |
|
new RelationalOperatorMethod<double, double>(this, (a, b) => a < b), |
|
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a < b) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? lessThanOrEqualOperators; |
|
|
|
public OperatorMethod[] LessThanOrEqualOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref lessThanOrEqualOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref lessThanOrEqualOperators, Lift( |
|
new RelationalOperatorMethod<int, int>(this, (a, b) => a <= b), |
|
new RelationalOperatorMethod<uint, uint>(this, (a, b) => a <= b), |
|
new RelationalOperatorMethod<long, long>(this, (a, b) => a <= b), |
|
new RelationalOperatorMethod<ulong, ulong>(this, (a, b) => a <= b), |
|
new RelationalOperatorMethod<float, float>(this, (a, b) => a <= b), |
|
new RelationalOperatorMethod<double, double>(this, (a, b) => a <= b), |
|
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a <= b) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? greaterThanOperators; |
|
|
|
public OperatorMethod[] GreaterThanOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref greaterThanOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref greaterThanOperators, Lift( |
|
new RelationalOperatorMethod<int, int>(this, (a, b) => a > b), |
|
new RelationalOperatorMethod<uint, uint>(this, (a, b) => a > b), |
|
new RelationalOperatorMethod<long, long>(this, (a, b) => a > b), |
|
new RelationalOperatorMethod<ulong, ulong>(this, (a, b) => a > b), |
|
new RelationalOperatorMethod<float, float>(this, (a, b) => a > b), |
|
new RelationalOperatorMethod<double, double>(this, (a, b) => a > b), |
|
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a > b) |
|
)); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? greaterThanOrEqualOperators; |
|
|
|
public OperatorMethod[] GreaterThanOrEqualOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref greaterThanOrEqualOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref greaterThanOrEqualOperators, Lift( |
|
new RelationalOperatorMethod<int, int>(this, (a, b) => a >= b), |
|
new RelationalOperatorMethod<uint, uint>(this, (a, b) => a >= b), |
|
new RelationalOperatorMethod<long, long>(this, (a, b) => a >= b), |
|
new RelationalOperatorMethod<ulong, ulong>(this, (a, b) => a >= b), |
|
new RelationalOperatorMethod<float, float>(this, (a, b) => a >= b), |
|
new RelationalOperatorMethod<double, double>(this, (a, b) => a >= b), |
|
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a >= b) |
|
)); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region Bitwise operators |
|
OperatorMethod[]? logicalAndOperators; |
|
|
|
public OperatorMethod[] LogicalAndOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref logicalAndOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref logicalAndOperators, new OperatorMethod[] { |
|
new LambdaBinaryOperatorMethod<bool, bool>(this, (a, b) => a & b) |
|
}); |
|
} |
|
} |
|
} |
|
|
|
|
|
OperatorMethod[]? bitwiseAndOperators; |
|
|
|
public OperatorMethod[] BitwiseAndOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseAndOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref bitwiseAndOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => a & b), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => a & b), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => a & b), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => a & b), |
|
this.LogicalAndOperators[0] |
|
)); |
|
} |
|
} |
|
} |
|
|
|
|
|
OperatorMethod[]? logicalOrOperators; |
|
|
|
public OperatorMethod[] LogicalOrOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref logicalOrOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref logicalOrOperators, new OperatorMethod[] { |
|
new LambdaBinaryOperatorMethod<bool, bool>(this, (a, b) => a | b) |
|
}); |
|
} |
|
} |
|
} |
|
|
|
OperatorMethod[]? bitwiseOrOperators; |
|
|
|
public OperatorMethod[] BitwiseOrOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseOrOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref bitwiseOrOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => a | b), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => a | b), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => a | b), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => a | b), |
|
this.LogicalOrOperators[0] |
|
)); |
|
} |
|
} |
|
} |
|
|
|
// Note: the logic for the lifted bool? bitwise operators is wrong; |
|
// we produce "true | null" = "null" when it should be true. However, this is irrelevant |
|
// because bool? cannot be a compile-time type. |
|
|
|
OperatorMethod[]? bitwiseXorOperators; |
|
|
|
public OperatorMethod[] BitwiseXorOperators { |
|
get { |
|
OperatorMethod[]? ops = LazyInit.VolatileRead(ref bitwiseXorOperators); |
|
if (ops != null) |
|
{ |
|
return ops; |
|
} |
|
else |
|
{ |
|
return LazyInit.GetOrSet(ref bitwiseXorOperators, Lift( |
|
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => a ^ b), |
|
new LambdaBinaryOperatorMethod<uint, uint>(this, (a, b) => a ^ b), |
|
new LambdaBinaryOperatorMethod<long, long>(this, (a, b) => a ^ b), |
|
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => a ^ b), |
|
new LambdaBinaryOperatorMethod<bool, bool>(this, (a, b) => a ^ b) |
|
)); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region User-defined operators |
|
public static IMethod? LiftUserDefinedOperator(IMethod m) |
|
{ |
|
if (IsComparisonOperator(m)) |
|
{ |
|
if (!m.ReturnType.IsKnownType(KnownTypeCode.Boolean)) |
|
return null; // cannot lift this operator |
|
} |
|
else |
|
{ |
|
if (!NullableType.IsNonNullableValueType(m.ReturnType)) |
|
return null; // cannot lift this operator |
|
} |
|
for (int i = 0; i < m.Parameters.Count; i++) |
|
{ |
|
if (!NullableType.IsNonNullableValueType(m.Parameters[i].Type)) |
|
return null; // cannot lift this operator |
|
} |
|
return new LiftedUserDefinedOperator(m); |
|
} |
|
|
|
internal static bool IsComparisonOperator(IMethod m) |
|
{ |
|
return m.IsOperator && m.Parameters.Count == 2 |
|
&& (OperatorDeclaration.GetOperatorType(m.Name)?.IsComparisonOperator() ?? false); |
|
} |
|
|
|
sealed class LiftedUserDefinedOperator : SpecializedMethod, ILiftedOperator |
|
{ |
|
internal readonly IParameterizedMember nonLiftedOperator; |
|
|
|
public LiftedUserDefinedOperator(IMethod nonLiftedMethod) |
|
: base((IMethod)nonLiftedMethod.MemberDefinition, nonLiftedMethod.Substitution) |
|
{ |
|
this.nonLiftedOperator = nonLiftedMethod; |
|
var compilation = nonLiftedMethod.Compilation; |
|
var substitution = nonLiftedMethod.Substitution; |
|
this.Parameters = base.CreateParameters( |
|
type => NullableType.Create(compilation, type.AcceptVisitor(substitution))); |
|
// Comparison operators keep the 'bool' return type even when lifted. |
|
if (IsComparisonOperator(nonLiftedMethod)) |
|
this.ReturnType = nonLiftedMethod.ReturnType; |
|
else |
|
this.ReturnType = NullableType.Create(compilation, nonLiftedMethod.ReturnType.AcceptVisitor(substitution)); |
|
} |
|
|
|
public IReadOnlyList<IParameter> NonLiftedParameters => nonLiftedOperator.Parameters; |
|
public IType NonLiftedReturnType => nonLiftedOperator.ReturnType; |
|
|
|
public override bool Equals(object? obj) |
|
{ |
|
LiftedUserDefinedOperator? op = obj as LiftedUserDefinedOperator; |
|
return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator); |
|
} |
|
|
|
public override int GetHashCode() |
|
{ |
|
return nonLiftedOperator.GetHashCode() ^ 0x7191254; |
|
} |
|
} |
|
#endregion |
|
} |
|
|
|
/// <summary> |
|
/// Implement this interface to give overload resolution a hint that the member represents a lifted operator, |
|
/// which is used in the tie-breaking rules. |
|
/// </summary> |
|
public interface ILiftedOperator : IParameterizedMember |
|
{ |
|
IType NonLiftedReturnType { get; } |
|
IReadOnlyList<IParameter> NonLiftedParameters { get; } |
|
} |
|
}
|
|
|