Merge pull request #1063 from esdrubal/bug18482
[mono.git] / mcs / mcs / ecore.cs
index ab0f85f0bc6fe1117c39e8c7906c9782630374be..97fe44d239d5dc4213cce585784c0fbeab194858 100644 (file)
@@ -429,6 +429,18 @@ namespace Mono.CSharp {
                {
                        return type.GetDefinition ().GetSignatureForError ();
                }
+
+               protected static bool IsNullPropagatingValid (TypeSpec type)
+               {
+                       return (TypeSpec.IsReferenceType (type) && type != InternalType.NullLiteral) || type.IsNullableType;
+               }
+
+               protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type)
+               {
+                       return TypeSpec.IsValueType (type) && !type.IsNullableType ?
+                               Nullable.NullableInfo.MakeType (rc.Module, type) :
+                               type;
+               }
               
                /// <summary>
                ///   Resolves an expression and performs semantic analysis on it.
@@ -936,6 +948,11 @@ namespace Mono.CSharp {
                        ec.Report.Error (1944, loc, "An expression tree cannot contain an unsafe pointer operation");
                }
 
+               protected void Error_NullShortCircuitInsideExpressionTree (ResolveContext rc)
+               {
+                       rc.Report.Error (8072, loc, "An expression tree cannot contain a null propagating operator");
+               }
+
                public virtual void FlowAnalysis (FlowAnalysisContext fc)
                {
                }
@@ -3151,6 +3168,8 @@ namespace Mono.CSharp {
                        get;
                }
 
+               public bool NullShortCircuit { get; set; }
+
                protected abstract TypeSpec DeclaringType {
                        get;
                }
@@ -3494,7 +3513,7 @@ namespace Mono.CSharp {
 
                public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original)
                {
-                       if (left != null && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
+                       if (left != null && !NullShortCircuit && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
                                ec.Report.Warning (1720, 1, left.Location,
                                        "Expression will always cause a `{0}'", "System.NullReferenceException");
                        }
@@ -3503,30 +3522,16 @@ namespace Mono.CSharp {
                        return this;
                }
 
-               protected void EmitInstance (EmitContext ec, bool prepare_for_load)
+               protected InstanceEmitter EmitInstance (EmitContext ec, bool prepare_for_load)
                {
-                       TypeSpec instance_type = InstanceExpression.Type;
-                       if (TypeSpec.IsValueType (instance_type)) {
-                               if (InstanceExpression is IMemoryLocation) {
-                                       ((IMemoryLocation) InstanceExpression).AddressOf (ec, AddressOp.Load);
-                               } else {
-                                       // Cannot release the temporary variable when its address
-                                       // is required to be on stack for any parent
-                                       LocalTemporary t = new LocalTemporary (instance_type);
-                                       InstanceExpression.Emit (ec);
-                                       t.Store (ec);
-                                       t.AddressOf (ec, AddressOp.Store);
-                               }
-                       } else {
-                               InstanceExpression.Emit (ec);
-
-                               // Only to make verifier happy
-                               if (instance_type.IsGenericParameter && !(InstanceExpression is This) && TypeSpec.IsReferenceType (instance_type))
-                                       ec.Emit (OpCodes.Box, instance_type);
-                       }
+                       var inst = new InstanceEmitter (InstanceExpression, TypeSpec.IsValueType (InstanceExpression.Type));
+                       inst.NullShortCircuit = NullShortCircuit;
+                       inst.Emit (ec);
 
                        if (prepare_for_load)
                                ec.Emit (OpCodes.Dup);
+
+                       return inst;
                }
 
                public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
@@ -3823,7 +3828,10 @@ namespace Mono.CSharp {
                        if (IsConditionallyExcluded)
                                ec.Report.Error (765, loc,
                                        "Partial methods with only a defining declaration or removed conditional methods cannot be used in an expression tree");
-                       
+
+                       if (NullShortCircuit)
+                               Error_NullShortCircuitInsideExpressionTree (ec);
+
                        return new TypeOfMethod (best_candidate, loc);
                }
                
@@ -3845,11 +3853,15 @@ namespace Mono.CSharp {
                        throw new NotSupportedException ();
                }
                
-               public void EmitCall (EmitContext ec, Arguments arguments)
+               public void EmitCall (EmitContext ec, Arguments arguments, bool statement)
                {
                        var call = new CallEmitter ();
                        call.InstanceExpression = InstanceExpression;
-                       call.Emit (ec, best_candidate, arguments, loc);                 
+                       call.NullShortCircuit = NullShortCircuit;
+                       if (statement)
+                               call.EmitStatement (ec, best_candidate, arguments, loc);
+                       else
+                               call.Emit (ec, best_candidate, arguments, loc);
                }
 
                public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl)
@@ -3956,6 +3968,9 @@ namespace Mono.CSharp {
                        if (best_candidate_return.Kind == MemberKind.Void && best_candidate.IsConditionallyExcluded (ec))
                                Methods = Excluded;
 
+                       if (NullShortCircuit)
+                               best_candidate_return = LiftMemberType (ec, best_candidate_return);
+
                        return this;
                }
 
@@ -4247,7 +4262,6 @@ namespace Mono.CSharp {
                                if (!TypeSpecComparer.Equals (p_m.Parameters.Types, q_m.Parameters.Types))
                                        return 0;
 
-                               var orig_p = p;
                                p = p_m.ReturnType;
                                var orig_q = q;
                                q = q_m.ReturnType;
@@ -4277,33 +4291,34 @@ namespace Mono.CSharp {
                                                q = q.TypeArguments[0];
                                                p = p.TypeArguments[0];
                                        }
-                               } else if (q != p) {
+                               }
+
+                               if (q != p) {
                                        //
-                                       // LAMESPEC: Lambda expression returning dynamic type has identity (better) conversion to delegate returning object type
+                                       // An inferred return type X exists for E in the context of that parameter list, and 
+                                       // the conversion from X to Y1 is better than the conversion from X to Y2
                                        //
-                                       if (q.BuiltinType == BuiltinTypeSpec.Type.Object) {
-                                               var am_rt = am.InferReturnType (ec, null, orig_q);
-                                               if (am_rt != null && am_rt.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
-                                                       return 2;
-                                       } else if (p.BuiltinType == BuiltinTypeSpec.Type.Object) {
-                                               var am_rt = am.InferReturnType (ec, null, orig_p);
-                                               if (am_rt != null && am_rt.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
-                                                       return 1;
+                                       argument_type = am.InferReturnType (ec, null, orig_q);
+                                       if (argument_type == null) {
+                                               // TODO: Can this be hit?
+                                               return 1;
                                        }
+
+                                       if (argument_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
+                                               argument_type = ec.BuiltinTypes.Object;
                                }
+                       }
 
-                               //
-                               // The parameters are identicial and return type is not void, use better type conversion
-                               // on return type to determine better one
-                               //
-                       } else {
-                               if (argument_type == p)
-                                       return 1;
+                       if (argument_type == p)
+                               return 1;
 
-                               if (argument_type == q)
-                                       return 2;
-                       }
+                       if (argument_type == q)
+                               return 2;
 
+                       //
+                       // The parameters are identicial and return type is not void, use better type conversion
+                       // on return type to determine better one
+                       //
                        return BetterTypeConversion (ec, p, q);
                }
 
@@ -5561,7 +5576,8 @@ namespace Mono.CSharp {
                bool VerifyArguments (ResolveContext ec, ref Arguments args, MemberSpec member, IParametersMember pm, bool chose_params_expanded)
                {
                        var pd = pm.Parameters;
-                       TypeSpec[] ptypes = ((IParametersMember) member).Parameters.Types;
+                       var cpd = ((IParametersMember) member).Parameters;
+                       var ptypes = cpd.Types;
 
                        Parameter.Modifier p_mod = 0;
                        TypeSpec pt = null;
@@ -5577,7 +5593,7 @@ namespace Mono.CSharp {
                                        continue;
 
                                if (p_mod != Parameter.Modifier.PARAMS) {
-                                       p_mod = pd.FixedParameters[a_idx].ModFlags;
+                                       p_mod = cpd.FixedParameters [a_idx].ModFlags;
                                        pt = ptypes[a_idx];
                                        has_unsafe_arg |= pt.IsPointer;
 
@@ -5892,6 +5908,10 @@ namespace Mono.CSharp {
 
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
+                       if (NullShortCircuit) {
+                               Error_NullShortCircuitInsideExpressionTree (ec);
+                       }
+
                        return CreateExpressionTree (ec, true);
                }
 
@@ -5985,6 +6005,13 @@ namespace Mono.CSharp {
                                variable_info = var.VariableInfo.GetStructFieldInfo (Name);
                        }
 
+                       if (NullShortCircuit) {
+                               type = LiftMemberType (ec, type);
+
+                               if (InstanceExpression.IsNull)
+                                       return Constant.CreateConstantFromValue (type, null, loc);
+                       }
+
                        eclass = ExprClass.Variable;
                        return this;
                }
@@ -6079,8 +6106,11 @@ namespace Mono.CSharp {
                        return null;
                }
 
-               override public Expression DoResolveLValue (ResolveContext ec, Expression right_side)
+               public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
                {
+                       if (NullShortCircuit)
+                               throw new NotSupportedException ("null propagating operator assignment");
+
                        if (spec is FixedFieldSpec) {
                                // It could be much better error message but we want to be error compatible
                                Error_ValueAssignment (ec, right_side);
@@ -6191,8 +6221,11 @@ namespace Mono.CSharp {
 
                                ec.Emit (OpCodes.Ldsfld, spec);
                        } else {
+                               InstanceEmitter ie;
                                if (!prepared)
-                                       EmitInstance (ec, false);
+                                       ie = EmitInstance (ec, false);
+                               else
+                                       ie = new InstanceEmitter ();
 
                                // Optimization for build-in types
                                if (type.IsStruct && type == ec.CurrentType && InstanceExpression.Type == type) {
@@ -6207,6 +6240,10 @@ namespace Mono.CSharp {
                                                        ec.Emit (OpCodes.Volatile);
 
                                                ec.Emit (OpCodes.Ldfld, spec);
+
+                                               if (NullShortCircuit) {
+                                                       ie.EmitResultLift (ec, spec.MemberType, false);
+                                               }
                                        }
                                }
                        }
@@ -6228,6 +6265,9 @@ namespace Mono.CSharp {
                        }
 
                        if (IsInstance) {
+                               if (NullShortCircuit)
+                                       throw new NotImplementedException ("null operator assignment");
+
                                if (has_await_source)
                                        source = source.EmitToField (ec);
 
@@ -6450,6 +6490,10 @@ namespace Mono.CSharp {
 
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
+                       if (NullShortCircuit) {
+                               Error_NullShortCircuitInsideExpressionTree (ec);
+                       }
+
                        Arguments args;
                        if (IsSingleDimensionalArrayLength ()) {
                                args = new Arguments (1);
@@ -6517,9 +6561,14 @@ namespace Mono.CSharp {
                        // Special case: length of single dimension array property is turned into ldlen
                        //
                        if (IsSingleDimensionalArrayLength ()) {
-                               EmitInstance (ec, false);
+                               var inst = EmitInstance (ec, false);
+
                                ec.Emit (OpCodes.Ldlen);
                                ec.Emit (OpCodes.Conv_I4);
+
+                               if (NullShortCircuit)
+                                       inst.EmitResultLift (ec, ec.BuiltinTypes.Int, false);
+
                                return;
                        }
 
@@ -6575,8 +6624,15 @@ namespace Mono.CSharp {
                        call.InstanceExpression = InstanceExpression;
                        if (args == null)
                                call.InstanceExpressionOnStack = true;
+                       if (NullShortCircuit) {
+                               call.NullShortCircuit = true;
+                               call.NullOperatorLabel = null_operator_label;
+                       }
 
-                       call.Emit (ec, Setter, args, loc);
+                       if (leave_copy)
+                               call.Emit (ec, Setter, args, loc);
+                       else
+                               call.EmitStatement (ec, Setter, args, loc);
 
                        if (temp != null) {
                                temp.Emit (ec);
@@ -6641,6 +6697,7 @@ namespace Mono.CSharp {
                protected LocalTemporary temp;
                protected bool emitting_compound_assignment;
                protected bool has_await_arguments;
+               protected Label null_operator_label;
 
                protected PropertyOrIndexerExpr (Location l)
                {
@@ -6678,6 +6735,10 @@ namespace Mono.CSharp {
                                if (expr == null)
                                        return null;
 
+                               if (NullShortCircuit && !ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
+                                       type = LiftMemberType (ec, type);
+                               }
+
                                if (expr != this)
                                        return expr.Resolve (ec);
                        }
@@ -6690,6 +6751,9 @@ namespace Mono.CSharp {
 
                public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
                {
+                       if (NullShortCircuit)
+                               throw new NotSupportedException ("null propagating operator assignment");
+
                        if (right_side == EmptyExpression.OutAccess) {
                                // TODO: best_candidate can be null at this point
                                INamedBlockVariable variable = null;
@@ -6715,7 +6779,15 @@ namespace Mono.CSharp {
 
                        if (!ResolveSetter (ec))
                                return null;
-
+/*
+                       if (NullShortCircuit && ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
+                               var lifted_type = LiftMemberType (ec, type);
+                               if (type != lifted_type) {
+                                       // TODO: Workaround to disable codegen for now
+                                       return Nullable.Wrap.Create (this, lifted_type);
+                               }
+                       }
+*/
                        return this;
                }
 
@@ -6725,6 +6797,7 @@ namespace Mono.CSharp {
                public virtual void Emit (EmitContext ec, bool leave_copy)
                {
                        var call = new CallEmitter ();
+                       call.NullShortCircuit = NullShortCircuit;
                        call.InstanceExpression = InstanceExpression;
                        if (has_await_arguments)
                                call.HasAwaitArguments = true;
@@ -6739,6 +6812,9 @@ namespace Mono.CSharp {
                                has_await_arguments = true;
                        }
 
+                       if (NullShortCircuit && emitting_compound_assignment)
+                               null_operator_label = call.NullOperatorLabel;
+
                        if (leave_copy) {
                                ec.Emit (OpCodes.Dup);
                                temp = new LocalTemporary (Type);
@@ -6962,7 +7038,8 @@ namespace Mono.CSharp {
 
                        var call = new CallEmitter ();
                        call.InstanceExpression = InstanceExpression;
-                       call.Emit (ec, op, args, loc);
+                       call.NullShortCircuit = NullShortCircuit;
+                       call.EmitStatement (ec, op, args, loc);
                }
 
                #endregion