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.
		
		
		
		
		
			
		
			
				
					
					
						
							1156 lines
						
					
					
						
							36 KiB
						
					
					
				
			
		
		
	
	
							1156 lines
						
					
					
						
							36 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) | 
						|
					)); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
		#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; } | 
						|
	} | 
						|
}
 | 
						|
 |