// 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.

using System;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework;

namespace ICSharpCode.NRefactory.CSharp.Resolver
{
	// assign short name to the fake reflection type
	using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic;
	
	[TestFixture]
	public unsafe class BinaryOperatorTests : ResolverTestBase
	{
		CSharpResolver resolver;
		
		public override void SetUp()
		{
			base.SetUp();
			resolver = new CSharpResolver(compilation);
		}
		
		[Test]
		public void Multiplication()
		{
			TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Multiply, MakeResult(typeof(int)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(int));
			
			TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Multiply, MakeConstant(0.0f),
			             Conversion.ImplicitNumericConversion, Conversion.IdentityConversion, typeof(float));
			
			AssertConstant(3.0f, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Multiply, MakeConstant(1.5f), MakeConstant(2)));
			
			AssertConstant(6, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Multiply, MakeConstant((byte)2), MakeConstant((byte)3)));
			
			TestOperator(MakeResult(typeof(uint?)), BinaryOperatorType.Multiply, MakeResult(typeof(int?)),
			             Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(long?));
			
			AssertError(typeof(decimal), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Multiply, MakeResult(typeof(float)), MakeResult(typeof(decimal))));
		}
		
		[Test]
		public void Addition()
		{
			TestOperator(MakeResult(typeof(short)), BinaryOperatorType.Add, MakeResult(typeof(byte?)),
			             Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?));
			
			AssertConstant(3.0, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant(1.0f), MakeConstant(2.0)));
			
			AssertConstant("Text", resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant("Te"), MakeConstant("xt")));
			
			AssertConstant("", resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant(null), resolver.ResolveCast(ResolveType(typeof(string)), MakeConstant(null))));
			
			AssertError(typeof(ReflectionHelper.Null), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant(null), MakeConstant(null)));
			
			TestOperator(MakeResult(typeof(int?)), BinaryOperatorType.Add, MakeResult(typeof(uint?)),
			             Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(long?));
			
			TestOperator(MakeResult(typeof(ushort?)), BinaryOperatorType.Add, MakeResult(typeof(ushort?)),
			             Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?));
			
			TestOperator(MakeConstant(1), BinaryOperatorType.Add, MakeConstant(null),
			             Conversion.ImplicitNullableConversion, Conversion.NullLiteralConversion, typeof(int?));
		}
		
		[Test]
		public void StringPlusNull()
		{
			ResolveResult left = MakeResult(typeof(string));
			var rr = (OperatorResolveResult)resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, left, MakeConstant(null));
			AssertType(typeof(string), rr);
			Assert.AreSame(left, rr.Operands[0]);
			Assert.AreEqual("System.String", rr.Operands[1].Type.FullName);
			Assert.IsTrue(rr.Operands[1].IsCompileTimeConstant);
			Assert.IsNull(rr.Operands[1].ConstantValue);
		}
		
		[Test]
		public void DelegateAddition()
		{
			TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Add, MakeResult(typeof(Action)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(Action));
			
			TestOperator(MakeResult(typeof(Action<object>)), BinaryOperatorType.Add, MakeResult(typeof(Action<string>)),
			             Conversion.ImplicitReferenceConversion, Conversion.IdentityConversion, typeof(Action<string>));
			
			TestOperator(MakeResult(typeof(Action<string>)), BinaryOperatorType.Add, MakeResult(typeof(Action<object>)),
			             Conversion.IdentityConversion, Conversion.ImplicitReferenceConversion, typeof(Action<string>));
			
			Assert.IsTrue(resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeResult(typeof(Action<int>)), MakeResult(typeof(Action<long>))).IsError);
		}
		
		
		[Test]
		public void EnumAddition()
		{
			AssertConstant(StringComparison.Ordinal, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant(StringComparison.InvariantCulture), MakeConstant(2)));
			
			AssertConstant(StringComparison.OrdinalIgnoreCase, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant((short)3), MakeConstant(StringComparison.InvariantCulture)));
			
			TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Add, MakeResult(typeof(int)),
			             Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(StringComparison?));
			
			TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Add, MakeResult(typeof(int?)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(StringComparison?));
			
			TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Add, MakeResult(typeof(StringComparison?)),
			             Conversion.ImplicitNullableConversion, Conversion.IdentityConversion, typeof(StringComparison?));
			
			TestOperator(MakeResult(typeof(int?)), BinaryOperatorType.Add, MakeResult(typeof(StringComparison?)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(StringComparison?));
		}
		
		[Test]
		public void PointerAddition()
		{
			TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Add, MakeConstant(1),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(int*));
			
			TestOperator(MakeResult(typeof(long)), BinaryOperatorType.Add, MakeResult(typeof(byte*)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(byte*));
		}
		
		[Test]
		public void AdditionWithOverflow()
		{
			AssertConstant(int.MinValue, resolver.WithCheckForOverflow(false).ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant(int.MaxValue), MakeConstant(1)));
			
			AssertError(typeof(int), resolver.WithCheckForOverflow(true).ResolveBinaryOperator(
				BinaryOperatorType.Add, MakeConstant(int.MaxValue), MakeConstant(1)));
		}
		
		
		[Test]
		public void Subtraction()
		{
			TestOperator(MakeResult(typeof(short)), BinaryOperatorType.Subtract, MakeResult(typeof(byte?)),
			             Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?));
			
			TestOperator(MakeResult(typeof(float)), BinaryOperatorType.Subtract, MakeResult(typeof(long)),
			             Conversion.IdentityConversion, Conversion.ImplicitNumericConversion, typeof(float));
			
			AssertConstant(-1.0, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeConstant(1.0f), MakeConstant(2.0)));
			
			Assert.IsTrue(resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeConstant("Te"), MakeConstant("xt")).IsError);
		}
		
		[Test]
		public void EnumSubtraction()
		{
			AssertConstant(StringComparison.InvariantCulture, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeConstant(StringComparison.Ordinal), MakeConstant(2)));
			
			AssertConstant(3, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeConstant(StringComparison.OrdinalIgnoreCase), MakeConstant(StringComparison.InvariantCulture)));
			
			TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Subtract, MakeResult(typeof(int)),
			             Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(StringComparison?));
			
			TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Subtract, MakeResult(typeof(StringComparison)),
			             Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(int?));
			
			Assert.IsTrue(resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeResult(typeof(int?)), MakeResult(typeof(StringComparison))).IsError);
		}
		
		[Test]
		public void DelegateSubtraction()
		{
			TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Subtract, MakeResult(typeof(Action)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(Action));
			
			TestOperator(MakeResult(typeof(Action<object>)), BinaryOperatorType.Subtract, MakeResult(typeof(Action<string>)),
			             Conversion.ImplicitReferenceConversion, Conversion.IdentityConversion, typeof(Action<string>));
			
			TestOperator(MakeResult(typeof(Action<string>)), BinaryOperatorType.Subtract, MakeResult(typeof(Action<object>)),
			             Conversion.IdentityConversion, Conversion.ImplicitReferenceConversion, typeof(Action<string>));
			
			Assert.IsTrue(resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeResult(typeof(Action<int>)), MakeResult(typeof(Action<long>))).IsError);
		}
		
		[Test]
		public void PointerSubtraction()
		{
			TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Subtract, MakeConstant(1),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(int*));
			
			TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(uint)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(byte*));
			
			TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(short)),
			             Conversion.IdentityConversion, Conversion.ImplicitNumericConversion, typeof(byte*));
			
			TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(byte*)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(long));
			
			AssertError(typeof(long), resolver.ResolveBinaryOperator(BinaryOperatorType.Subtract, MakeResult(typeof(byte*)), MakeResult(typeof(int*))));
		}
		
		[Test]
		public void ShiftTest()
		{
			AssertConstant(6, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ShiftLeft, MakeConstant(3), MakeConstant(1)));
			
			AssertConstant(ulong.MaxValue >> 2, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ShiftRight, MakeConstant(ulong.MaxValue), MakeConstant(2)));
			
			TestOperator(MakeResult(typeof(ushort?)), BinaryOperatorType.ShiftLeft, MakeConstant(1),
			             Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?));
			
			TestOperator(MakeConstant(null), BinaryOperatorType.ShiftLeft, MakeConstant(1),
			             Conversion.NullLiteralConversion, Conversion.ImplicitNullableConversion, typeof(int?));
			
			TestOperator(MakeResult(typeof(long)), BinaryOperatorType.ShiftLeft, MakeConstant(null),
			             Conversion.ImplicitNullableConversion, Conversion.NullLiteralConversion, typeof(long?));
			
			TestOperator(MakeConstant(null), BinaryOperatorType.ShiftLeft,  MakeConstant(null),
			             Conversion.NullLiteralConversion, Conversion.NullLiteralConversion, typeof(int?));
		}
		
		[Test]
		public void ConstantEquality()
		{
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(3), MakeConstant(3)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(3), MakeConstant(3.0)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(2.9), MakeConstant(3)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(double.NaN), MakeConstant(double.NaN)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(float.NaN), MakeConstant(float.NaN)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant("A"), MakeConstant("B")));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant("A"), MakeConstant("A")));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(""), MakeConstant(null)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(null), MakeConstant(null)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(1), MakeConstant(null)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(null), MakeConstant('a')));
		}
		
		[Test]
		public void Equality()
		{
			TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Equality, MakeResult(typeof(uint*)),
			             Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(bool));
			
			TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Equality, MakeResult(typeof(int?)),
			             Conversion.ImplicitNullableConversion, Conversion.IdentityConversion, typeof(bool));
			
			TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Equality, MakeResult(typeof(float)),
			             Conversion.ImplicitNumericConversion, Conversion.IdentityConversion, typeof(bool));
			
			AssertType(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeResult(typeof(int)), MakeConstant(null)));
			
			AssertError(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeResult(typeof(int)), MakeResult(typeof(string))));
			
			AssertError(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeResult(typeof(int)), MakeResult(typeof(object))));
		}
		
		[Test]
		public void Inequality()
		{
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(3), MakeConstant(3)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(3), MakeConstant(3.0)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(2.9), MakeConstant(3)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(double.NaN), MakeConstant(double.NaN)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(float.NaN), MakeConstant(double.NaN)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(float.NaN), MakeConstant(float.NaN)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant("A"), MakeConstant("B")));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant("A"), MakeConstant("A")));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(""), MakeConstant(null)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(null), MakeConstant(null)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(1), MakeConstant(null)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeConstant(null), MakeConstant('a')));
			
			AssertType(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeResult(typeof(int*)), MakeResult(typeof(uint*))));
			
			AssertType(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.InEquality, MakeResult(typeof(bool?)), MakeConstant(null)));
		}
		
		[Test]
		public void EqualityEnum()
		{
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(0), MakeConstant(StringComparison.Ordinal)));
			
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(0), MakeConstant(StringComparison.CurrentCulture)));
			
			Assert.IsFalse(resolver.ResolveBinaryOperator(
				BinaryOperatorType.Equality, MakeConstant(StringComparison.Ordinal), MakeConstant(1)).IsCompileTimeConstant);
		}
		
		[Test]
		public void RelationalOperators()
		{
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.LessThan, MakeConstant(0), MakeConstant(0)));
			
			AssertType(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.LessThan, MakeResult(typeof(int*)), MakeResult(typeof(uint*))));
			
			TestOperator(MakeResult(typeof(int?)), BinaryOperatorType.LessThan, MakeResult(typeof(int)),
			             Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(bool));
		}
		
		[Test]
		public void RelationalEnum()
		{
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.LessThan, MakeConstant(0), MakeConstant(StringComparison.Ordinal)));
			
			AssertError(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.LessThanOrEqual, MakeConstant(1), MakeConstant(StringComparison.Ordinal)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.GreaterThan, MakeConstant(StringComparison.CurrentCultureIgnoreCase), MakeConstant(StringComparison.Ordinal)));
			
			AssertType(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.GreaterThan, MakeResult(typeof(StringComparison?)), MakeResult(typeof(StringComparison?))));
		}
		
		[Test]
		public void BitAnd()
		{
			AssertConstant(5, resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeConstant(7), MakeConstant(13)));
			
			AssertType(typeof(int?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeConstant(null), MakeConstant((short)13)));
			
			AssertType(typeof(long?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeResult(typeof(uint?)), MakeConstant((short)13)));
			
			AssertType(typeof(uint?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeResult(typeof(uint?)), MakeConstant((int)13)));
			
			AssertType(typeof(ulong?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeResult(typeof(ulong?)), MakeConstant((long)13)));
			
			Assert.IsTrue(resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeResult(typeof(ulong?)), MakeConstant((short)13)).IsError);
		}
		
		[Test]
		public void BitXor()
		{
			AssertConstant(6L ^ 3, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ExclusiveOr, MakeConstant(6L), MakeConstant(3)));
			
			AssertConstant(6UL ^ 3L, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ExclusiveOr, MakeConstant(6UL), MakeConstant(3L)));
			
			AssertError(typeof(ulong), resolver.ResolveBinaryOperator(
				BinaryOperatorType.ExclusiveOr, MakeConstant(6UL), MakeConstant(-3L)));
		}
		
		[Test]
		public void BitwiseEnum()
		{
			AssertConstant(AttributeTargets.Field | AttributeTargets.Property, resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseOr, MakeConstant(AttributeTargets.Field), MakeConstant(AttributeTargets.Property)));
			
			AssertConstant(AttributeTargets.Field & AttributeTargets.All, resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeConstant(AttributeTargets.Field), MakeConstant(AttributeTargets.All)));
			
			AssertConstant(AttributeTargets.Field & 0, resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseAnd, MakeConstant(AttributeTargets.Field), MakeConstant(0)));
			
			AssertConstant(0 | AttributeTargets.Field, resolver.ResolveBinaryOperator(
				BinaryOperatorType.BitwiseOr, MakeConstant(0), MakeConstant(AttributeTargets.Field)));
		}
		
		[Test]
		public void LogicalAnd()
		{
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ConditionalAnd, MakeConstant(true), MakeConstant(true)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ConditionalAnd, MakeConstant(false), MakeConstant(true)));
			
			AssertError(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.ConditionalAnd, MakeConstant(false), MakeResult(typeof(bool?))));
		}
		
		[Test]
		public void LogicalOr()
		{
			AssertConstant(true, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeConstant(true)));
			
			AssertConstant(false, resolver.ResolveBinaryOperator(
				BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeConstant(false)));
			
			AssertError(typeof(bool), resolver.ResolveBinaryOperator(
				BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeResult(typeof(bool?))));
		}
		
		[Test]
		public void NullCoalescing()
		{
			AssertType(typeof(int), resolver.ResolveBinaryOperator(
				BinaryOperatorType.NullCoalescing, MakeResult(typeof(int?)), MakeResult(typeof(short))));
			
			AssertType(typeof(int?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.NullCoalescing, MakeResult(typeof(int?)), MakeResult(typeof(short?))));
			
			AssertType(typeof(object), resolver.ResolveBinaryOperator(
				BinaryOperatorType.NullCoalescing, MakeResult(typeof(string)), MakeResult(typeof(object))));
			
			AssertError(typeof(string), resolver.ResolveBinaryOperator(
				BinaryOperatorType.NullCoalescing, MakeResult(typeof(string)), MakeResult(typeof(int))));
			
			AssertType(typeof(dynamic), resolver.ResolveBinaryOperator(
				BinaryOperatorType.NullCoalescing, MakeResult(typeof(dynamic)), MakeResult(typeof(string))));
			
			AssertType(typeof(dynamic), resolver.ResolveBinaryOperator(
				BinaryOperatorType.NullCoalescing, MakeResult(typeof(string)), MakeResult(typeof(dynamic))));
		}
		
		[Test]
		public void LiftedUserDefined()
		{
			AssertType(typeof(TimeSpan), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeResult(typeof(DateTime)), MakeResult(typeof(DateTime))));
			AssertType(typeof(TimeSpan?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeResult(typeof(DateTime?)), MakeResult(typeof(DateTime))));
			AssertType(typeof(TimeSpan?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeResult(typeof(DateTime)), MakeResult(typeof(DateTime?))));
			AssertType(typeof(TimeSpan?), resolver.ResolveBinaryOperator(
				BinaryOperatorType.Subtract, MakeResult(typeof(DateTime?)), MakeResult(typeof(DateTime?))));
		}
		
		[Test]
		public void ByteEnumSubtraction()
		{
			string program = @"enum E : byte {
  A = 1,
  B = 2
}
class Test {
	const int $j = E.A - E.B$;
}
";
			// result is 255 in C# 2.0 and later (was -1 in C# 1.x)
			var rr = Resolve<MemberResolveResult>(program);
			Assert.AreEqual(255, rr.ConstantValue);
		}
		
		[Test]
		public void UserDefinedNeedsLiftingDueToImplicitConversion()
		{
			string program = @"struct S {}
struct A {
	public static implicit operator S?(A a) { return null; }
	
	public static S operator +(A a, S s) { return s; }
}
class Test {
	void M(A a) {
		var s = $a + a$;
	}
}
";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.IsTrue(irr.IsLiftedOperator);
			Assert.IsTrue(irr.UserDefinedOperatorMethod is OverloadResolution.ILiftedOperator);
			Assert.AreEqual("A.op_Addition", irr.UserDefinedOperatorMethod.FullName);
			Assert.AreEqual("System.Nullable`1[[S]]", irr.Type.ReflectionName);
			Assert.AreEqual("System.Nullable`1[[S]]", irr.UserDefinedOperatorMethod.ReturnType.ReflectionName);
			
			Conversion lhsConv = ((ConversionResolveResult)irr.Operands[0]).Conversion;
			Conversion rhsConv = ((ConversionResolveResult)irr.Operands[1]).Conversion;
			Assert.AreEqual(Conversion.ImplicitNullableConversion, lhsConv);
			Assert.IsTrue(rhsConv.IsUserDefined);
			Assert.AreEqual("A.op_Implicit", rhsConv.Method.FullName);
		}
		
		[Test]
		public void ThereAreNoLiftedOperatorsForClasses()
		{
			string program = @"struct S {}
class A {
	public static implicit operator S?(A a) { return null; }
	
	public static S operator +(A a, S s) { return s; }
}
class Test {
	void M(A a) {
		var s = $a + a$;
	}
}
";
			var irr = Resolve<CSharpInvocationResolveResult>(program);
			Assert.IsTrue(irr.IsError); // cannot convert from A to S
			Assert.AreEqual("A.op_Addition", irr.Member.FullName);
			Assert.AreEqual("S", irr.Type.ReflectionName);
		}
		
		[Test]
		public void CompoundAssign_String_Char()
		{
			string program = @"
class Test {
	string text;
	void Append(char c) {
		$text += c$;
	}
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.AreEqual(System.Linq.Expressions.ExpressionType.AddAssign, irr.OperatorType);
			Assert.IsNull(irr.UserDefinedOperatorMethod);
			Assert.AreEqual("System.String", irr.Type.ReflectionName);
		}
		
		[Test]
		public void CompoundAssign_Byte_Literal1()
		{
			string program = @"
class Test {
	byte c;
	void Inc() {
		$c += 1$;
	}
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.AreEqual(System.Linq.Expressions.ExpressionType.AddAssign, irr.OperatorType);
			Assert.IsNull(irr.UserDefinedOperatorMethod);
			Assert.AreEqual("System.Byte", irr.Type.ReflectionName);
		}
		
		[Test]
		public void CompareDateTimeWithNullLiteral()
		{
			string program = @"using System;
class Test {
	static void Inc(DateTime x) {
		var c = $x == null$;
	}
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.IsTrue(irr.IsLiftedOperator);
			Assert.IsNotNull(irr.UserDefinedOperatorMethod);
			Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type);
		}
		
		[Test]
		public void CompareStructWithNullLiteral()
		{
			string program = @"
struct X { }
class Test {
	static void Inc(X x) {
		var c = $x == null$;
	}
}";
			var irr = Resolve(program);
			Assert.IsTrue(irr.IsError);
			Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type);
		}
		
		[Test]
		public void CompareNullableStructWithNullLiteral()
		{
			string program = @"
struct X { }
class Test {
	static void Inc(X? x) {
		var c = $x == null$;
	}
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type);
		}
		
		[Test]
		public void CompareUnrestrictedTypeParameterWithNullLiteral()
		{
			string program = @"
class Test {
	static void Inc<X>(X x) {
		var c = $x == null$;
	}
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type);
		}
		
		[Test]
		public void LiftedEqualityOperator()
		{
			string program = @"
struct X {
	public static bool operator ==(X a, X b) {}
}
class Test {
	static void Inc(X? x) {
		var c = $x == x$;
	}
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type);
		}
		
		[Test]
		public void IsLiftedProperty()
		{
			string program = @"
class Test {
    static void Inc() {
        int? a = 0, b = 0;
        int? c = $a + b$;
    }
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.IsTrue(irr.IsLiftedOperator);
		}
		
		[Test]
		public void IsLiftedProperty2()
		{
			string program = @"
class Test {
    static void Inc() {
        int? a = 0, b = 0;
        $b += a$;
    }
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.IsTrue(irr.IsLiftedOperator);
		}

		[Ignore("Resolver bug. Fixme!")]
		[Test]
		public void TestLiftedOperatorBug()
		{
			string program = @"
using System;

struct C<T>
{
	public static C<T> operator+(C<T> u, C<T> u2)
	{
		return u;
	}

	public C ()
	{
		int? foo = 4;
		var a = $new C<int> () + foo$;
	}
}";
			var irr = Resolve<OperatorResolveResult>(program);
			Assert.IsFalse(irr.IsError);
			Assert.IsTrue(irr.IsLiftedOperator);
		}



	}
}
