bool IsBitwiseBoolean {
get {
- return (Oper & Operator.BitwiseMask) != 0 &&
+ return (Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) &&
((left_unwrap != null && left_unwrap.Type == TypeManager.bool_type) ||
(right_unwrap != null && right_unwrap.Type == TypeManager.bool_type));
}
// Arguments can be lifted for equal operators when the return type is bool and both
// arguments are of same type
//
- if (left_orig.IsNull) {
+ if (left_orig is NullLiteral) {
left = right;
state |= State.LeftNullLifted;
type = TypeManager.bool_type;
}
if (right_orig.IsNull) {
- right = left;
+ if ((Oper & Operator.ShiftMask) != 0)
+ right = new EmptyExpression (TypeManager.int32_type);
+ else
+ right = left;
+
state |= State.RightNullLifted;
type = TypeManager.bool_type;
}
} else {
if (right_unwrap == null) {
right.Emit (ec);
- if (right is EmptyConstantCast)
+ if (right is EmptyConstantCast || right is EmptyCast)
ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
} else {
right_unwrap.Load (ec);
if (right_unwrap == null) {
if (Oper == Operator.BitwiseAnd) {
right.Emit (ec);
- if (right is EmptyConstantCast)
+ if (right is EmptyConstantCast || right is EmptyCast)
ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
} else {
left_unwrap.Load (ec);
if (lifted_type == null)
return null;
- if (right is UserCast || right is TypeCast)
- right.Type = lifted_type.Type;
+ var r = right;
+ if (r is ReducedExpression)
+ r = ((ReducedExpression) r).OriginalExpression;
+
+ if (r is UserCast || r is TypeCast)
+ r.Type = lifted_type.Type;
else
right = EmptyCast.Create (right, lifted_type.Type);
}
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);
+ //
+ // Try original types first for exact match without unwrapping
+ //
+ Expression expr = base.ResolveUserOperator (ec, left_orig, right_orig);
+ if (expr != null)
+ return expr;
+
+ State orig_state = state;
+
+ //
+ // One side is a nullable type, try to match underlying types
+ //
+ if (left_unwrap != null || right_unwrap != null || (state & (State.RightNullLifted | State.LeftNullLifted)) != 0) {
+ expr = base.ResolveUserOperator (ec, left, right);
+ }
+
if (expr == null)
return null;
+ //
+ // Lift the result in the case it can be null and predefined or user operator
+ // result type is of a value type
+ //
+ if (!TypeManager.IsValueType (expr.Type))
+ return null;
+
+ if (state != orig_state)
+ return expr;
+
expr = LiftResult (ec, expr);
if (expr is Constant)
return expr;
if (unwrap == null)
return null;
+ //
+ // Reduce (left ?? null) to left
+ //
+ if (right.IsNull)
+ return ReducedExpression.Create (left, this);
+
if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
left = unwrap;
- type = left.Type;
- right = Convert.ImplicitConversion (ec, right, type, loc);
+ ltype = left.Type;
+
+ //
+ // If right is a dynamic expression, the result type is dynamic
+ //
+ if (right.Type == InternalType.Dynamic) {
+ type = right.Type;
+
+ // Need to box underlying value type
+ left = Convert.ImplicitBoxingConversion (left, ltype, type);
+ return this;
+ }
+
+ right = Convert.ImplicitConversion (ec, right, ltype, loc);
+ type = ltype;
return this;
}
} else if (TypeManager.IsReferenceType (ltype)) {
if (Convert.ImplicitConversionExists (ec, right, ltype)) {
//
- // Reduce (constant ?? expr) to constant
+ // If right is a dynamic expression, the result type is dynamic
+ //
+ if (right.Type == InternalType.Dynamic) {
+ type = right.Type;
+ return this;
+ }
+
+ //
+ // Reduce ("foo" ?? expr) to expression
//
Constant lc = left as Constant;
if (lc != null && !lc.IsDefaultValue)
- return new SideEffectConstant (lc, right, loc).Resolve (ec);
+ return ReducedExpression.Create (lc, this).Resolve (ec);
//
// Reduce (left ?? null) to left OR (null-constant ?? right) to right
return ReducedExpression.Create (lc != null ? right : left, this);
right = Convert.ImplicitConversion (ec, right, ltype, loc);
- type = left.Type;
+ type = ltype;
return this;
}
} else {