[652390] Return type inference can inflate using type parameters out of the reach
[mono.git] / mcs / mcs / nullable.cs
index 5c9d1940ab2a7e7b025555dc0cc7efd60c080a02..cd35b319e5985cb26f29215a501a002aaad296eb 100644 (file)
@@ -573,7 +573,7 @@ namespace Mono.CSharp.Nullable
 
                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));
                        }
@@ -647,14 +647,18 @@ namespace Mono.CSharp.Nullable
                        // 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;
                        }
@@ -696,7 +700,7 @@ namespace Mono.CSharp.Nullable
                        } 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);
@@ -710,7 +714,7 @@ namespace Mono.CSharp.Nullable
                        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);
@@ -885,8 +889,12 @@ namespace Mono.CSharp.Nullable
                                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);
                        }
@@ -966,12 +974,37 @@ namespace Mono.CSharp.Nullable
                        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;
@@ -1036,20 +1069,47 @@ namespace Mono.CSharp.Nullable
                                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
@@ -1058,7 +1118,7 @@ namespace Mono.CSharp.Nullable
                                                return ReducedExpression.Create (lc != null ? right : left, this);
 
                                        right = Convert.ImplicitConversion (ec, right, ltype, loc);
-                                       type = left.Type;
+                                       type = ltype;
                                        return this;
                                }
                        } else {