return new LiftedNull (nullable, loc);
}
- public static Expression CreateFromExpression (ResolveContext ec, Expression e)
+ public static Constant CreateFromExpression (ResolveContext ec, Expression e)
{
ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
TypeManager.CSharpName (e.Type));
public class LiftedBinaryOperator : Binary
{
Unwrap left_unwrap, right_unwrap;
- bool left_null_lifted, right_null_lifted;
Expression left_orig, right_orig;
Expression user_operator;
MethodSpec wrap_ctor;
{
}
+ bool IsBitwiseBoolean {
+ get {
+ return (Oper & Operator.BitwiseMask) != 0 &&
+ ((left_unwrap != null && left_unwrap.Type == TypeManager.bool_type) ||
+ (right_unwrap != null && right_unwrap.Type == TypeManager.bool_type));
+ }
+ }
+
+ bool IsLeftNullLifted {
+ get {
+ return (state & State.LeftNullLifted) != 0;
+ }
+ }
+
+ bool IsRightNullLifted {
+ get {
+ return (state & State.RightNullLifted) != 0;
+ }
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
if (user_operator != null)
// with the null literal *outside* of a generics context and
// inlines that as true or false.
//
- Expression CreateNullConstant (ResolveContext ec, Expression expr)
+ Constant CreateNullConstant (ResolveContext ec, Expression expr)
{
// FIXME: Handle side effect constants
Constant c = new BoolConstant (Oper == Operator.Inequality, loc).Resolve (ec);
//
if (left_orig.IsNull) {
left = right;
- left_null_lifted = true;
+ state |= State.LeftNullLifted;
type = TypeManager.bool_type;
}
if (right_orig.IsNull) {
right = left;
- right_null_lifted = true;
+ state |= State.RightNullLifted;
type = TypeManager.bool_type;
}
Label load_right = ec.DefineLabel ();
Label end_label = ec.DefineLabel ();
+ // null & value, null | value
+ if (left_unwrap == null) {
+ left_unwrap = right_unwrap;
+ right_unwrap = null;
+ right = left;
+ }
+
left_unwrap.Emit (ec);
ec.Emit (OpCodes.Brtrue_S, load_right);
- right_unwrap.Emit (ec);
- ec.Emit (OpCodes.Brtrue_S, load_left);
+ // value & null, value | null
+ if (right_unwrap != null) {
+ right_unwrap.Emit (ec);
+ ec.Emit (OpCodes.Brtrue_S, load_left);
+ }
left_unwrap.EmitCheck (ec);
ec.Emit (OpCodes.Brfalse_S, load_right);
if (Oper == Operator.BitwiseAnd) {
left_unwrap.Load (ec);
} else {
- right_unwrap.Load (ec);
- right_unwrap = left_unwrap;
+ if (right_unwrap == null) {
+ right.Emit (ec);
+ if (right is EmptyConstantCast)
+ ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
+ } else {
+ right_unwrap.Load (ec);
+ right_unwrap = left_unwrap;
+ }
}
ec.Emit (OpCodes.Br_S, end_label);
// load right
ec.MarkLabel (load_right);
- right_unwrap.Load (ec);
+ if (right_unwrap == null) {
+ if (Oper == Operator.BitwiseAnd) {
+ right.Emit (ec);
+ if (right is EmptyConstantCast)
+ ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
+ } else {
+ left_unwrap.Load (ec);
+ }
+ } else {
+ right_unwrap.Load (ec);
+ }
ec.MarkLabel (end_label);
}
//
// Either left or right is null
//
- if (left_unwrap != null && (right_null_lifted || right.IsNull)) {
+ if (left_unwrap != null && (IsRightNullLifted || right.IsNull)) {
left_unwrap.EmitCheck (ec);
if (Oper == Binary.Operator.Equality) {
ec.Emit (OpCodes.Ldc_I4_0);
return;
}
- if (right_unwrap != null && (left_null_lifted || left.IsNull)) {
+ if (right_unwrap != null && (IsLeftNullLifted || left.IsNull)) {
right_unwrap.EmitCheck (ec);
if (Oper == Binary.Operator.Equality) {
ec.Emit (OpCodes.Ldc_I4_0);
base.EmitOperator (ec, l);
}
- bool IsBitwiseBoolean {
- get {
- return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
- left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
- }
- }
-
Expression LiftResult (ResolveContext ec, Expression res_expr)
{
TypeExpr lifted_type;
//
// Avoid double conversion
//
- if (left_unwrap == null || left_null_lifted || left_unwrap.Type != left.Type || (left_unwrap != null && right_null_lifted)) {
+ if (left_unwrap == null || IsLeftNullLifted || left_unwrap.Type != left.Type || (left_unwrap != null && IsRightNullLifted)) {
lifted_type = new NullableType (left.Type, loc);
lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
if (lifted_type == null)
left = EmptyCast.Create (left, lifted_type.Type);
}
- if (left != right && (right_unwrap == null || right_null_lifted || right_unwrap.Type != right.Type || (right_unwrap != null && left_null_lifted))) {
+ if (left != right && (right_unwrap == null || IsRightNullLifted || right_unwrap.Type != right.Type || (right_unwrap != null && IsLeftNullLifted))) {
lifted_type = new NullableType (right.Type, loc);
lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
if (lifted_type == null)
type = res_expr.Type = lifted_type.Type;
}
- if (left_null_lifted) {
+ if (IsLeftNullLifted) {
left = LiftedNull.Create (right.Type, left.Location);
+ //
+ // Special case for bool?, the result depends on both null right side and left side value
+ //
+ if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) {
+ return res_expr;
+ }
+
if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
return LiftedNull.CreateFromExpression (ec, res_expr);
return CreateNullConstant (ec, right_orig).Resolve (ec);
}
- if (right_null_lifted) {
+ if (IsRightNullLifted) {
right = LiftedNull.Create (left.Type, right.Location);
+ //
+ // Special case for bool?, the result depends on both null right side and left side value
+ //
+ if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) {
+ return res_expr;
+ }
+
if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
return LiftedNull.CreateFromExpression (ec, res_expr);
// (in unlifted or lifted form) exists for the operation.
//
if (e == null && (Oper & Operator.EqualityMask) != 0) {
- if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
+ if ((IsLeftNullLifted && right_unwrap != null) || (IsRightNullLifted && left_unwrap != null))
return LiftResult (ec, this);
}
return e;
}
- protected override Expression ResolveUserOperator (ResolveContext ec, TypeSpec l, TypeSpec r)
+ protected override Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
{
- Expression expr = base.ResolveUserOperator (ec, l, r);
+ State orig_state = state;
+
+ // Try original types first
+ Expression expr = base.ResolveUserOperator (ec, left_orig, right_orig);
+ if (expr == null) {
+ if (left_unwrap != null || right_unwrap != null) {
+ // One side is nullable type, try underlying types
+ expr = base.ResolveUserOperator (ec, left, right);
+ } else if ((state & (State.RightNullLifted | State.LeftNullLifted)) != 0) {
+ expr = base.ResolveUserOperator (ec, left, right);
+ }
+ }
+
if (expr == null)
return null;
+ if (state != orig_state)
+ return expr;
+
expr = LiftResult (ec, expr);
if (expr is Constant)
return expr;