mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
The resolver comments cited section numbers from the C# 4.0 spec (and a few from C# 9.0 drafts), which no longer match the published ECMA-334 standard. Renumber them against dotnet/csharpstandard draft-v11; every reference was checked against the actual section headings. The old 'better conversion from type' subclause (7.5.3.4) no longer exists as such and its rules live in 12.6.4.5-12.6.4.7, so that comment now says so instead of pointing at a dead number. Assisted-by: Claude:claude-fable-5:Claude Code
1176 lines
37 KiB
C#
1176 lines
37 KiB
C#
// 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# spec (draft-v11): §12.9.4 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; }
|
|
}
|
|
}
|