[mcs] null propagating operator with nested receiver
authorMarek Safar <marek.safar@gmail.com>
Thu, 7 Aug 2014 09:03:33 +0000 (11:03 +0200)
committerMarek Safar <marek.safar@gmail.com>
Thu, 7 Aug 2014 09:04:16 +0000 (11:04 +0200)
mcs/mcs/codegen.cs
mcs/mcs/context.cs
mcs/mcs/cs-parser.jay
mcs/mcs/delegate.cs
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/tests/test-null-operator-01.cs
mcs/tests/test-null-operator-02.cs
mcs/tests/test-null-operator-05.cs
mcs/tests/test-null-operator-06.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_5.xml

index b346a5bbc4f43c1a6f218202b6d1bd7829086bcf..9509a449db4f8adc5c453757e80d66d561fba206 100644 (file)
@@ -121,6 +121,8 @@ namespace Mono.CSharp
                        }
                }
 
+               public ConditionalAccessContext ConditionalAccess { get; set; }
+
                public TypeSpec CurrentType {
                        get { return member_context.CurrentType; }
                }
@@ -350,6 +352,15 @@ namespace Mono.CSharp
 #endif
                }
 
+               public void CloseConditionalAccess (TypeSpec type)
+               {
+                       if (type != null)
+                               Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
+
+                       MarkLabel (ConditionalAccess.EndLabel);
+                       ConditionalAccess = null;
+               }
+
                //
                // Creates a nested container in this context for all dynamic compiler generated stuff
                //
@@ -978,6 +989,19 @@ namespace Mono.CSharp
                }
        }
 
+       public class ConditionalAccessContext
+       {
+               public ConditionalAccessContext (TypeSpec type, Label endLabel)
+               {
+                       Type = type;
+                       EndLabel = endLabel;
+               }
+
+               public bool Statement { get; set; }
+               public Label EndLabel { get; private set; }
+               public TypeSpec Type { get; private set; }
+       }
+
        struct CallEmitter
        {
                public Expression InstanceExpression;
@@ -998,7 +1022,7 @@ namespace Mono.CSharp
                //
                public bool HasAwaitArguments;
 
-               public bool NullShortCircuit;
+               public bool ConditionalAccess;
 
                //
                // When dealing with await arguments the original arguments are converted
@@ -1031,32 +1055,22 @@ namespace Mono.CSharp
 
                        OpCode call_op;
                        LocalTemporary lt = null;
-                       InstanceEmitter ie = new InstanceEmitter ();
 
                        if (method.IsStatic) {
                                call_op = OpCodes.Call;
                        } else {
-                               if (IsVirtualCallRequired (InstanceExpression, method)) {
-                                       call_op = OpCodes.Callvirt;
-                               } else {
-                                       call_op = OpCodes.Call;
-                               }
+                               call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
 
                                if (HasAwaitArguments) {
                                        instance_copy = InstanceExpression.EmitToField (ec);
-                                       ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+                                       var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
 
                                        if (Arguments == null) {
                                                ie.EmitLoad (ec);
                                        }
                                } else if (!InstanceExpressionOnStack) {
-                                       ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
-                                       ie.NullShortCircuit = NullShortCircuit;
-                                       ie.Emit (ec);
-
-                                       if (NullShortCircuit) {
-                                               NullOperatorLabel = ie.NullOperatorLabel;
-                                       }
+                                       var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
+                                       ie.Emit (ec, ConditionalAccess);
 
                                        if (DuplicateArguments) {
                                                ec.Emit (OpCodes.Dup);
@@ -1073,8 +1087,8 @@ namespace Mono.CSharp
                                EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
                                if (EmittedArguments != null) {
                                        if (instance_copy != null) {
-                                               ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
-                                               ie.Emit (ec);
+                                               var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+                                               ie.Emit (ec, ConditionalAccess);
 
                                                if (lt != null)
                                                        lt.Release (ec);
@@ -1120,10 +1134,6 @@ namespace Mono.CSharp
                        //
                        if (statement && method.ReturnType.Kind != MemberKind.Void)
                                ec.Emit (OpCodes.Pop);
-
-                       if (NullShortCircuit && !DuplicateArguments) {
-                               ie.EmitResultLift (ec, method.ReturnType, statement);
-                       }
                }
 
                static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
@@ -1178,51 +1188,59 @@ namespace Mono.CSharp
        {
                readonly Expression instance;
                readonly bool addressRequired;
-               bool value_on_stack;
-
-               public bool NullShortCircuit;
-               public Label NullOperatorLabel;
 
                public InstanceEmitter (Expression instance, bool addressLoad)
                {
                        this.instance = instance;
                        this.addressRequired = addressLoad;
-                       NullShortCircuit = false;
-                       NullOperatorLabel = new Label ();
-                       value_on_stack = false;
                }
 
-               public void Emit (EmitContext ec)
+               public void Emit (EmitContext ec, bool conditionalAccess)
                {
+                       Label NullOperatorLabel;
                        Nullable.Unwrap unwrap;
 
-                       if (NullShortCircuit) {
+                       if (conditionalAccess) {
                                NullOperatorLabel = ec.DefineLabel ();
                                unwrap = instance as Nullable.Unwrap;
                        } else {
+                               NullOperatorLabel = new Label ();
                                unwrap = null;
                        }
 
                        if (unwrap != null) {
                                unwrap.Store (ec);
                                unwrap.EmitCheck (ec);
-                               ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
-                               unwrap.Emit (ec);
-                               var tmp = ec.GetTemporaryLocal (unwrap.Type);
-                               ec.Emit (OpCodes.Stloc, tmp);
-                               ec.Emit (OpCodes.Ldloca, tmp);
-                               ec.FreeTemporaryLocal (tmp, unwrap.Type);
-                               return;
+                               ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+                       } else {
+                               EmitLoad (ec);
+
+                               if (conditionalAccess) {
+                                       ec.Emit (OpCodes.Dup);
+                                       ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+                                       ec.Emit (OpCodes.Pop);
+                               }
                        }
 
-                       EmitLoad (ec);
+                       if (conditionalAccess) {
+                               if (!ec.ConditionalAccess.Statement) {
+                                       if (ec.ConditionalAccess.Type.IsNullableType)
+                                               Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
+                                       else
+                                               ec.EmitNull ();
+                               }
+
+                               ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
+                               ec.MarkLabel (NullOperatorLabel);
 
-                       if (NullShortCircuit) {
-                               ec.Emit (OpCodes.Dup);
-                               ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
+                               if (unwrap != null) {
+                                       unwrap.Emit (ec);
+                                       var tmp = ec.GetTemporaryLocal (unwrap.Type);
+                                       ec.Emit (OpCodes.Stloc, tmp);
+                                       ec.Emit (OpCodes.Ldloca, tmp);
+                                       ec.FreeTemporaryLocal (tmp, unwrap.Type);
+                               }
                        }
-
-                       value_on_stack = true;
                }
 
                public void EmitLoad (EmitContext ec)
@@ -1263,44 +1281,6 @@ namespace Mono.CSharp
                        }
                }
 
-               public void EmitResultLift (EmitContext ec, TypeSpec type, bool statement)
-               {
-                       if (!NullShortCircuit)
-                               throw new InternalErrorException ();
-
-                       bool value_rt = TypeSpec.IsValueType (type);
-                       TypeSpec lifted;
-                       if (value_rt) {
-                               if (type.IsNullableType)
-                                       lifted = type;
-                               else {
-                                       lifted = Nullable.NullableInfo.MakeType (ec.Module, type);
-                                       ec.Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (lifted));
-                               }
-                       } else {
-                               lifted = null;
-                       }
-
-                       var end = ec.DefineLabel ();
-                       if (value_on_stack || !statement) {
-                               ec.Emit (OpCodes.Br_S, end);
-                       }
-
-                       ec.MarkLabel (NullOperatorLabel);
-
-                       if (value_on_stack)
-                               ec.Emit (OpCodes.Pop);
-
-                       if (!statement) {
-                               if (value_rt)
-                                       Nullable.LiftedNull.Create (lifted, Location.Null).Emit (ec);
-                               else
-                                       ec.EmitNull ();
-                       }
-
-                       ec.MarkLabel (end);
-               }
-
                public TypeSpec GetStackType (EmitContext ec)
                {
                        var instance_type = instance.Type;
index 873c92d24ef8c000ad3c23291f68920cabc437e2..cb69736036b1d0f4e316ee0e82901040efead7d6 100644 (file)
@@ -188,6 +188,8 @@ namespace Mono.CSharp
 
                        TryWithCatchScope = 1 << 15,
 
+                       ConditionalAccessReceiver = 1 << 16,
+
                        ///
                        /// Indicates the current context is in probing mode, no errors are reported. 
                        ///
index 693f95ce599197b0a21a67a67fcd0fea2c830cf8..825bfb2eee763fc3a9614e481800ae0c5c85fbfd 100644 (file)
@@ -3303,7 +3303,7 @@ member_access
                        FeatureIsNotAvailable (GetLocation ($2), "null propagating operator");
 
                var lt = (LocatedToken) $4;
-               $$ = new NullMemberAccess ((Expression) $1, lt.Value, (TypeArguments) $5, lt.Location);
+               $$ = new ConditionalMemberAccess ((Expression) $1, lt.Value, (TypeArguments) $5, lt.Location);
                lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3));
          }
        | builtin_types DOT identifier_inside_body opt_type_argument_list
@@ -3567,7 +3567,7 @@ element_access
                        FeatureIsNotAvailable (GetLocation ($2), "null propagating operator");
 
                $$ = new ElementAccess ((Expression) $1, (Arguments) $4, GetLocation ($3)) {
-                       NullPropagating = true
+                       ConditionalAccess = true
                };
 
                lbag.AddLocation ($$, GetLocation ($2), GetLocation ($5));
index b083cfd46d23d36cc51f84433941f566fe6ac941..35ea582c2d77fa22fc8939ba309bb717eaccedc9 100644 (file)
@@ -439,6 +439,7 @@ namespace Mono.CSharp {
        //
        public abstract class DelegateCreation : Expression, OverloadResolver.IErrorHandler
        {
+               bool conditional_access_receiver;
                protected MethodSpec constructor_method;
                protected MethodGroupExpr method_group;
 
@@ -507,8 +508,19 @@ namespace Mono.CSharp {
 
                        var invoke_method = Delegate.GetInvokeMethod (type);
 
+                       if (!ec.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+                               if (method_group.HasConditionalAccess ()) {
+                                       conditional_access_receiver = true;
+                                       ec.Set (ResolveContext.Options.ConditionalAccessReceiver);
+                               }
+                       }
+
                        Arguments arguments = CreateDelegateMethodArguments (ec, invoke_method.Parameters, invoke_method.Parameters.Types, loc);
                        method_group = method_group.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.CovariantDelegate);
+
+                       if (conditional_access_receiver)
+                               ec.With (ResolveContext.Options.ConditionalAccessReceiver, false);
+
                        if (method_group == null)
                                return null;
 
@@ -564,14 +576,14 @@ namespace Mono.CSharp {
                
                public override void Emit (EmitContext ec)
                {
-                       InstanceEmitter ie;
+                       if (conditional_access_receiver)
+                               ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
                        if (method_group.InstanceExpression == null) {
-                               ie = new InstanceEmitter ();
                                ec.EmitNull ();
                        } else {
-                               ie = new InstanceEmitter (method_group.InstanceExpression, false);
-                               ie.NullShortCircuit = method_group.NullShortCircuit;
-                               ie.Emit (ec);
+                               var ie = new InstanceEmitter (method_group.InstanceExpression, false);
+                               ie.Emit (ec, method_group.ConditionalAccess);
                        }
 
                        var delegate_method = method_group.BestCandidate;
@@ -586,9 +598,8 @@ namespace Mono.CSharp {
 
                        ec.Emit (OpCodes.Newobj, constructor_method);
 
-                       if (method_group.NullShortCircuit) {
-                               ie.EmitResultLift (ec, type, false);
-                       }
+                       if (conditional_access_receiver)
+                               ec.CloseConditionalAccess (null);
                }
 
                public override void FlowAnalysis (FlowAnalysisContext fc) {
index 97fe44d239d5dc4213cce585784c0fbeab194858..98f025f9c1fccec0a0f70554fde838ccbd014215 100644 (file)
@@ -435,6 +435,11 @@ namespace Mono.CSharp {
                        return (TypeSpec.IsReferenceType (type) && type != InternalType.NullLiteral) || type.IsNullableType;
                }
 
+               public virtual bool HasConditionalAccess ()
+               {
+                       return false;
+               }
+
                protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type)
                {
                        return TypeSpec.IsValueType (type) && !type.IsNullableType ?
@@ -3130,6 +3135,8 @@ namespace Mono.CSharp {
        /// </summary>
        public abstract class MemberExpr : Expression, OverloadResolver.IInstanceQualifier
        {
+               protected bool conditional_access_receiver;
+
                //
                // An instance expression associated with this member, if it's a
                // non-static member
@@ -3168,7 +3175,7 @@ namespace Mono.CSharp {
                        get;
                }
 
-               public bool NullShortCircuit { get; set; }
+               public bool ConditionalAccess { get; set; }
 
                protected abstract TypeSpec DeclaringType {
                        get;
@@ -3308,6 +3315,11 @@ namespace Mono.CSharp {
                        return InstanceExpression != null && InstanceExpression.ContainsEmitWithAwait ();
                }
 
+               public override bool HasConditionalAccess ()
+               {
+                       return ConditionalAccess || (InstanceExpression != null && InstanceExpression.HasConditionalAccess ());
+               }
+
                static bool IsSameOrBaseQualifier (TypeSpec type, TypeSpec qtype)
                {
                        do {
@@ -3367,6 +3379,16 @@ namespace Mono.CSharp {
                                InstanceExpression.FlowAnalysis (fc);
                }
 
+               protected void ResolveConditionalAccessReceiver (ResolveContext rc)
+               {
+                       if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+                               if (HasConditionalAccess ()) {
+                                       conditional_access_receiver = true;
+                                       rc.Set (ResolveContext.Options.ConditionalAccessReceiver);
+                               }
+                       }
+               }
+
                public bool ResolveInstanceExpression (ResolveContext rc, Expression rhs)
                {
                        if (!ResolveInstanceExpressionCore (rc, rhs))
@@ -3513,7 +3535,7 @@ namespace Mono.CSharp {
 
                public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original)
                {
-                       if (left != null && !NullShortCircuit && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
+                       if (left != null && !ConditionalAccess && left.IsNull && TypeSpec.IsReferenceType (left.Type)) {
                                ec.Report.Warning (1720, 1, left.Location,
                                        "Expression will always cause a `{0}'", "System.NullReferenceException");
                        }
@@ -3522,16 +3544,13 @@ namespace Mono.CSharp {
                        return this;
                }
 
-               protected InstanceEmitter EmitInstance (EmitContext ec, bool prepare_for_load)
+               protected void EmitInstance (EmitContext ec, bool prepare_for_load)
                {
                        var inst = new InstanceEmitter (InstanceExpression, TypeSpec.IsValueType (InstanceExpression.Type));
-                       inst.NullShortCircuit = NullShortCircuit;
-                       inst.Emit (ec);
+                       inst.Emit (ec, ConditionalAccess);
 
                        if (prepare_for_load)
                                ec.Emit (OpCodes.Dup);
-
-                       return inst;
                }
 
                public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
@@ -3829,7 +3848,7 @@ namespace Mono.CSharp {
                                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)
+                       if (ConditionalAccess)
                                Error_NullShortCircuitInsideExpressionTree (ec);
 
                        return new TypeOfMethod (best_candidate, loc);
@@ -3852,18 +3871,30 @@ namespace Mono.CSharp {
                {
                        throw new NotSupportedException ();
                }
-               
+
                public void EmitCall (EmitContext ec, Arguments arguments, bool statement)
                {
                        var call = new CallEmitter ();
                        call.InstanceExpression = InstanceExpression;
-                       call.NullShortCircuit = NullShortCircuit;
+                       call.ConditionalAccess = ConditionalAccess;
+
                        if (statement)
                                call.EmitStatement (ec, best_candidate, arguments, loc);
                        else
                                call.Emit (ec, best_candidate, arguments, loc);
                }
 
+               public void EmitCall (EmitContext ec, Arguments arguments, TypeSpec conditionalAccessReceiver, bool statement)
+               {
+                       ec.ConditionalAccess = new ConditionalAccessContext (conditionalAccessReceiver, ec.DefineLabel ()) {
+                               Statement = statement
+                       };
+
+                       EmitCall (ec, arguments, statement);
+
+                       ec.CloseConditionalAccess (!statement && best_candidate_return != conditionalAccessReceiver && conditionalAccessReceiver.IsNullableType ? conditionalAccessReceiver : null);
+               }
+
                public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl)
                {
                        ec.Report.Error (428, loc, "Cannot convert method group `{0}' to non-delegate type `{1}'. Consider using parentheses to invoke the method",
@@ -3968,9 +3999,6 @@ 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;
                }
 
@@ -5908,7 +5936,7 @@ namespace Mono.CSharp {
 
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
-                       if (NullShortCircuit) {
+                       if (ConditionalAccess) {
                                Error_NullShortCircuitInsideExpressionTree (ec);
                        }
 
@@ -5954,6 +5982,8 @@ namespace Mono.CSharp {
                        bool lvalue_instance = rhs != null && IsInstance && spec.DeclaringType.IsStruct;
 
                        if (rhs != this) {
+                               ResolveConditionalAccessReceiver (ec);
+
                                if (ResolveInstanceExpression (ec, rhs)) {
                                        // Resolve the field's instance expression while flow analysis is turned
                                        // off: when accessing a field "a.b", we must check whether the field
@@ -5975,6 +6005,9 @@ namespace Mono.CSharp {
                                }
 
                                DoBestMemberChecks (ec, spec);
+
+                               if (conditional_access_receiver)
+                                       ec.With (ResolveContext.Options.ConditionalAccessReceiver, false);
                        }
 
                        var fb = spec as FixedFieldSpec;
@@ -6005,8 +6038,9 @@ namespace Mono.CSharp {
                                variable_info = var.VariableInfo.GetStructFieldInfo (Name);
                        }
 
-                       if (NullShortCircuit) {
-                               type = LiftMemberType (ec, type);
+                       if (ConditionalAccess) {
+                               if (conditional_access_receiver)
+                                       type = LiftMemberType (ec, type);
 
                                if (InstanceExpression.IsNull)
                                        return Constant.CreateConstantFromValue (type, null, loc);
@@ -6108,7 +6142,7 @@ namespace Mono.CSharp {
 
                public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
                {
-                       if (NullShortCircuit)
+                       if (ConditionalAccess)
                                throw new NotSupportedException ("null propagating operator assignment");
 
                        if (spec is FixedFieldSpec) {
@@ -6221,11 +6255,12 @@ namespace Mono.CSharp {
 
                                ec.Emit (OpCodes.Ldsfld, spec);
                        } else {
-                               InstanceEmitter ie;
-                               if (!prepared)
-                                       ie = EmitInstance (ec, false);
-                               else
-                                       ie = new InstanceEmitter ();
+                               if (!prepared) {
+                                       if (conditional_access_receiver)
+                                               ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
+                                       EmitInstance (ec, false);
+                               }
 
                                // Optimization for build-in types
                                if (type.IsStruct && type == ec.CurrentType && InstanceExpression.Type == type) {
@@ -6240,12 +6275,12 @@ namespace Mono.CSharp {
                                                        ec.Emit (OpCodes.Volatile);
 
                                                ec.Emit (OpCodes.Ldfld, spec);
-
-                                               if (NullShortCircuit) {
-                                                       ie.EmitResultLift (ec, spec.MemberType, false);
-                                               }
                                        }
                                }
+
+                               if (conditional_access_receiver) {
+                                       ec.CloseConditionalAccess (type.IsNullableType && type != spec.MemberType ? type : null);
+                               }
                        }
 
                        if (leave_copy) {
@@ -6265,7 +6300,7 @@ namespace Mono.CSharp {
                        }
 
                        if (IsInstance) {
-                               if (NullShortCircuit)
+                               if (ConditionalAccess)
                                        throw new NotImplementedException ("null operator assignment");
 
                                if (has_await_source)
@@ -6490,7 +6525,7 @@ namespace Mono.CSharp {
 
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
-                       if (NullShortCircuit) {
+                       if (ConditionalAccess) {
                                Error_NullShortCircuitInsideExpressionTree (ec);
                        }
 
@@ -6561,13 +6596,18 @@ namespace Mono.CSharp {
                        // Special case: length of single dimension array property is turned into ldlen
                        //
                        if (IsSingleDimensionalArrayLength ()) {
-                               var inst = EmitInstance (ec, false);
+                               if (conditional_access_receiver) {
+                                       ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+                               }
+
+                               EmitInstance (ec, false);
 
                                ec.Emit (OpCodes.Ldlen);
                                ec.Emit (OpCodes.Conv_I4);
 
-                               if (NullShortCircuit)
-                                       inst.EmitResultLift (ec, ec.BuiltinTypes.Int, false);
+                               if (conditional_access_receiver) {
+                                       ec.CloseConditionalAccess (type);
+                               }
 
                                return;
                        }
@@ -6624,9 +6664,9 @@ namespace Mono.CSharp {
                        call.InstanceExpression = InstanceExpression;
                        if (args == null)
                                call.InstanceExpressionOnStack = true;
-                       if (NullShortCircuit) {
-                               call.NullShortCircuit = true;
-                               call.NullOperatorLabel = null_operator_label;
+
+                       if (ConditionalAccess) {
+                               call.ConditionalAccess = true;
                        }
 
                        if (leave_copy)
@@ -6697,7 +6737,6 @@ 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)
                {
@@ -6731,16 +6770,19 @@ namespace Mono.CSharp {
                protected override Expression DoResolve (ResolveContext ec)
                {
                        if (eclass == ExprClass.Unresolved) {
+                               ResolveConditionalAccessReceiver (ec);
+
                                var expr = OverloadResolve (ec, null);
                                if (expr == null)
                                        return null;
 
-                               if (NullShortCircuit && !ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
-                                       type = LiftMemberType (ec, type);
-                               }
-
                                if (expr != this)
                                        return expr.Resolve (ec);
+
+                               if (conditional_access_receiver) {
+                                       type = LiftMemberType (ec, type);
+                                       ec.With (ResolveContext.Options.ConditionalAccessReceiver, false);
+                               }
                        }
 
                        if (!ResolveGetter (ec))
@@ -6751,7 +6793,7 @@ namespace Mono.CSharp {
 
                public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
                {
-                       if (NullShortCircuit)
+                       if (ConditionalAccess)
                                throw new NotSupportedException ("null propagating operator assignment");
 
                        if (right_side == EmptyExpression.OutAccess) {
@@ -6779,32 +6821,36 @@ 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;
                }
 
+               void EmitConditionalAccess (EmitContext ec, ref CallEmitter call, MethodSpec method, Arguments arguments)
+               {
+                       ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
+                       call.Emit (ec, method, arguments, loc);
+
+                       ec.CloseConditionalAccess (method.ReturnType != type && type.IsNullableType ? type : null);
+               }
+
                //
                // Implements the IAssignMethod interface for assignments
                //
                public virtual void Emit (EmitContext ec, bool leave_copy)
                {
                        var call = new CallEmitter ();
-                       call.NullShortCircuit = NullShortCircuit;
+                       call.ConditionalAccess = ConditionalAccess;
                        call.InstanceExpression = InstanceExpression;
                        if (has_await_arguments)
                                call.HasAwaitArguments = true;
                        else
                                call.DuplicateArguments = emitting_compound_assignment;
 
-                       call.Emit (ec, Getter, Arguments, loc);
+                       if (conditional_access_receiver)
+                               EmitConditionalAccess (ec, ref call, Getter, Arguments);
+                       else
+                               call.Emit (ec, Getter, Arguments, loc);
 
                        if (call.HasAwaitArguments) {
                                InstanceExpression = call.InstanceExpression;
@@ -6812,9 +6858,6 @@ 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);
@@ -7036,10 +7079,18 @@ namespace Mono.CSharp {
                        Arguments args = new Arguments (1);
                        args.Add (new Argument (source));
 
+                       // TODO: Wrong, needs receiver
+//                     if (NullShortCircuit) {
+//                             ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+//                     }
+
                        var call = new CallEmitter ();
                        call.InstanceExpression = InstanceExpression;
-                       call.NullShortCircuit = NullShortCircuit;
+                       call.ConditionalAccess = ConditionalAccess;
                        call.EmitStatement (ec, op, args, loc);
+
+//                     if (NullShortCircuit)
+//                             ec.CloseConditionalAccess (null);
                }
 
                #endregion
index 69ab3e79db66103f96e06ae693285551848fd164..27a5811f0acb8ccc4d8c51a71d8f739300047600 100644 (file)
@@ -6119,6 +6119,7 @@ namespace Mono.CSharp
                protected Arguments arguments;
                protected Expression expr;
                protected MethodGroupExpr mg;
+               bool conditional_access_receiver;
                
                public Invocation (Expression expr, Arguments arguments)
                {
@@ -6231,7 +6232,21 @@ namespace Mono.CSharp
                        return CreateExpressionFactoryCall (ec, "Call", args);
                }
 
-               protected override Expression DoResolve (ResolveContext ec)
+               protected override Expression DoResolve (ResolveContext rc)
+               {
+                       if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+                               if (expr.HasConditionalAccess ()) {
+                                       conditional_access_receiver = true;
+                                       using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) {
+                                               return DoResolveInvocation (rc);
+                                       }
+                               }
+                       }
+
+                       return DoResolveInvocation (rc);
+               }
+
+               Expression DoResolveInvocation (ResolveContext ec)
                {
                        Expression member_expr;
                        var atn = expr as ATypeNameExpression;
@@ -6296,6 +6311,8 @@ namespace Mono.CSharp
 
                        var method = mg.BestCandidate;
                        type = mg.BestCandidateReturnType;
+                       if (conditional_access_receiver)
+                               type = LiftMemberType (ec, type);
                
                        if (arguments == null && method.DeclaringType.BuiltinType == BuiltinTypeSpec.Type.Object && method.Name == Destructor.MetadataName) {
                                if (mg.IsBase)
@@ -6388,6 +6405,11 @@ namespace Mono.CSharp
                        return mg.GetSignatureForError ();
                }
 
+               public override bool HasConditionalAccess ()
+               {
+                       return expr.HasConditionalAccess ();
+               }
+
                //
                // If a member is a method or event, or if it is a constant, field or property of either a delegate type
                // or the type dynamic, then the member is invocable
@@ -6426,7 +6448,10 @@ namespace Mono.CSharp
                        if (mg.IsConditionallyExcluded)
                                return;
 
-                       mg.EmitCall (ec, arguments, false);
+                       if (conditional_access_receiver)
+                               mg.EmitCall (ec, arguments, type, false);
+                       else
+                               mg.EmitCall (ec, arguments, false);
                }
                
                public override void EmitStatement (EmitContext ec)
@@ -6434,7 +6459,10 @@ namespace Mono.CSharp
                        if (mg.IsConditionallyExcluded)
                                return;
 
-                       mg.EmitCall (ec, arguments, true);
+                       if (conditional_access_receiver)
+                               mg.EmitCall (ec, arguments, type, true);
+                       else
+                               mg.EmitCall (ec, arguments, true);
                }
 
                public override SLE.Expression MakeExpression (BuilderContext ctx)
@@ -8742,6 +8770,11 @@ namespace Mono.CSharp
                        return alias + "::" + name;
                }
 
+               public override bool HasConditionalAccess ()
+               {
+                       return false;
+               }
+
                public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions)
                {
                        if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) {
@@ -8841,6 +8874,11 @@ namespace Mono.CSharp
                                expr.Error_OperatorCannotBeApplied (rc, loc, ".", type);
                }
 
+               public override bool HasConditionalAccess ()
+               {
+                       return expr.HasConditionalAccess ();
+               }
+
                public static bool IsValidDotExpression (TypeSpec type)
                {
                        const MemberKind dot_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.Delegate | MemberKind.Enum |
@@ -8902,7 +8940,7 @@ namespace Mono.CSharp
                                return new DynamicMemberBinder (Name, args, loc);
                        }
 
-                       if (this is NullMemberAccess) {
+                       if (this is ConditionalMemberAccess) {
                                if (!IsNullPropagatingValid (expr.Type)) {
                                        expr.Error_OperatorCannotBeApplied (rc, loc, "?", expr.Type);
                                        return null;
@@ -9002,8 +9040,8 @@ namespace Mono.CSharp
                                sn = null;
                        }
 
-                       if (this is NullMemberAccess) {
-                               me.NullShortCircuit = true;
+                       if (this is ConditionalMemberAccess) {
+                               me.ConditionalAccess = true;
                        }
 
                        me = me.ResolveMemberAccess (rc, expr, sn);
@@ -9177,12 +9215,17 @@ namespace Mono.CSharp
                }
        }
 
-       public class NullMemberAccess : MemberAccess
+       public class ConditionalMemberAccess : MemberAccess
        {
-               public NullMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
+               public ConditionalMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
                        : base (expr, identifier, args, loc)
                {
                }
+
+               public override bool HasConditionalAccess ()
+               {
+                       return true;
+               }
        }
 
        /// <summary>
@@ -9350,7 +9393,7 @@ namespace Mono.CSharp
                        this.Arguments = args;
                }
 
-               public bool NullPropagating { get; set; }
+               public bool ConditionalAccess { get; set; }
 
                public override Location StartLocation {
                        get {
@@ -9367,16 +9410,23 @@ namespace Mono.CSharp
                // We perform some simple tests, and then to "split" the emit and store
                // code we create an instance of a different class, and return that.
                //
-               Expression CreateAccessExpression (ResolveContext ec)
+               Expression CreateAccessExpression (ResolveContext ec, bool conditionalAccessReceiver)
                {
-                       if (NullPropagating && !IsNullPropagatingValid (type)) {
+                       Expr = Expr.Resolve (ec);
+                       if (Expr == null)
+                               return null;
+
+                       type = Expr.Type;
+
+                       if (ConditionalAccess && !IsNullPropagatingValid (type)) {
                                Error_OperatorCannotBeApplied (ec, loc, "?", type);
                                return null;
                        }
 
                        if (type.IsArray)
                                return new ArrayAccess (this, loc) {
-                                       NullShortCircuit = NullPropagating
+                                       ConditionalAccess = ConditionalAccess,
+                                       ConditionalAccessReceiver = conditionalAccessReceiver
                                };
 
                        if (type.IsPointer)
@@ -9392,9 +9442,14 @@ namespace Mono.CSharp
 
                        var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false);
                        if (indexers != null || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
-                               return new IndexerExpr (indexers, type, this) {
-                                       NullShortCircuit = NullPropagating
+                               var indexer = new IndexerExpr (indexers, type, this) {
+                                       ConditionalAccess = ConditionalAccess
                                };
+
+                               if (conditionalAccessReceiver)
+                                       indexer.SetConditionalAccessReceiver ();
+
+                               return indexer;
                        }
 
                        if (type != InternalType.ErrorType) {
@@ -9413,6 +9468,11 @@ namespace Mono.CSharp
                        return CreateExpressionFactoryCall (ec, "ArrayIndex", args);
                }
 
+               public override bool HasConditionalAccess ()
+               {
+                       return ConditionalAccess || Expr.HasConditionalAccess ();
+               }
+
                Expression MakePointerAccess (ResolveContext rc, TypeSpec type)
                {
                        if (Arguments.Count != 1){
@@ -9434,31 +9494,31 @@ namespace Mono.CSharp
                        return new Indirection (p, loc);
                }
                
-               protected override Expression DoResolve (ResolveContext ec)
+               protected override Expression DoResolve (ResolveContext rc)
                {
-                       Expr = Expr.Resolve (ec);
-                       if (Expr == null)
-                               return null;
+                       Expression expr;
+                       if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) {
+                               if (HasConditionalAccess ()) {
+                                       using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) {
+                                               expr = CreateAccessExpression (rc, true);
+                                               if (expr == null)
+                                                       return null;
 
-                       type = Expr.Type;
+                                               return expr.Resolve (rc);
+                                       }
+                               }
+                       }
 
-                       // TODO: Create 1 result for Resolve and ResolveLValue ?
-                       var res = CreateAccessExpression (ec);
-                       if (res == null)
+                       expr = CreateAccessExpression (rc, false);
+                       if (expr == null)
                                return null;
 
-                       return res.Resolve (ec);
+                       return expr.Resolve (rc);
                }
 
                public override Expression DoResolveLValue (ResolveContext ec, Expression rhs)
                {
-                       Expr = Expr.Resolve (ec);
-                       if (Expr == null)
-                               return null;
-
-                       type = Expr.Type;
-
-                       var res = CreateAccessExpression (ec);
+                       var res = CreateAccessExpression (ec, false);
                        if (res == null)
                                return null;
 
@@ -9520,7 +9580,9 @@ namespace Mono.CSharp
                        loc = l;
                }
 
-               public bool NullShortCircuit { get; set; }
+               public bool ConditionalAccess { get; set; }
+
+               public bool ConditionalAccessReceiver { get; set; }
 
                public void AddressOf (EmitContext ec, AddressOp mode)
                {
@@ -9540,7 +9602,7 @@ namespace Mono.CSharp
 
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
-                       if (NullShortCircuit)
+                       if (ConditionalAccess)
                                Error_NullShortCircuitInsideExpressionTree (ec);
 
                        return ea.CreateExpressionTree (ec);
@@ -9553,7 +9615,7 @@ namespace Mono.CSharp
 
                public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
                {
-                       if (NullShortCircuit)
+                       if (ConditionalAccess)
                                throw new NotSupportedException ("null propagating operator assignment");
 
                        return DoResolve (ec);
@@ -9578,7 +9640,7 @@ namespace Mono.CSharp
                                UnsafeError (ec, ea.Location);
                        }
 
-                       if (NullShortCircuit)
+                       if (ConditionalAccessReceiver)
                                type = LiftMemberType (ec, type);
 
                        foreach (Argument a in ea.Arguments) {
@@ -9607,16 +9669,13 @@ namespace Mono.CSharp
                //
                // Load the array arguments into the stack.
                //
-               InstanceEmitter LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait)
+               void LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait)
                {
-                       InstanceEmitter ie;
                        if (prepareAwait) {
-                               ie = new InstanceEmitter ();
                                ea.Expr = ea.Expr.EmitToField (ec);
                        } else {
-                               ie = new InstanceEmitter (ea.Expr, false);
-                               ie.NullShortCircuit = NullShortCircuit;
-                               ie.Emit (ec);
+                               var ie = new InstanceEmitter (ea.Expr, false);
+                               ie.Emit (ec, ConditionalAccess);
 
                                if (duplicateArguments) {
                                        ec.Emit (OpCodes.Dup);
@@ -9630,8 +9689,6 @@ namespace Mono.CSharp
                        var dup_args = ea.Arguments.Emit (ec, duplicateArguments, prepareAwait);
                        if (dup_args != null)
                                ea.Arguments = dup_args;
-
-                       return ie;
                }
 
                public void Emit (EmitContext ec, bool leave_copy)
@@ -9643,12 +9700,15 @@ namespace Mono.CSharp
                                        LoadInstanceAndArguments (ec, false, true);
                                }
 
+                               if (ConditionalAccessReceiver)
+                                       ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ());
+
                                var ac = (ArrayContainer) ea.Expr.Type;
-                               var inst = LoadInstanceAndArguments (ec, false, false);
+                               LoadInstanceAndArguments (ec, false, false);
                                ec.EmitArrayLoad (ac);
 
-                               if (NullShortCircuit)
-                                       inst.EmitResultLift (ec, ((ArrayContainer) ea.Expr.Type).Element, false);
+                               if (ConditionalAccessReceiver)
+                                       ec.CloseConditionalAccess (type.IsNullableType && type != ac.Element ? type : null);
                        }       
 
                        if (leave_copy) {
@@ -9837,7 +9897,7 @@ namespace Mono.CSharp
 
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
-                       if (NullShortCircuit) {
+                       if (ConditionalAccess) {
                                Error_NullShortCircuitInsideExpressionTree (ec);
                        }
 
@@ -10013,6 +10073,11 @@ namespace Mono.CSharp
                                target.arguments = arguments.Clone (clonectx);
                }
 
+               public void SetConditionalAccessReceiver ()
+               {
+                       conditional_access_receiver = true;
+               }
+
                public override void SetTypeArguments (ResolveContext ec, TypeArguments ta)
                {
                        Error_TypeArgumentsCannotBeUsed (ec, "indexer", GetSignatureForError (), loc);
index debd85fe757392efc4affe2dfb4b7048fe0efdc1..2c6784c93b6d67cffee8af92e08605fbd16ae412 100644 (file)
@@ -45,6 +45,14 @@ class C
         if (t3 != null)
             return 3;
 
+        var t4 = k?.GetLength (0).ToString () ?? "N";
+        if (t4 != "N")
+            return 4;
+
+        var t5 = k?.Length.ToString () ?? "N";
+        if (t5 != "N")
+            return 5;            
+
         k = new int[] { 3 };
         var t11 = k?.ToString ();
         if (t11.GetType () != typeof (string))
index 83cec417b91dbc733fd2e1fe2ba4eb8bf712aa46..c6dfa614d03de0fc1120726c2d5ac379582a73c1 100644 (file)
@@ -2,32 +2,45 @@ using System;
 
 class CI
 {
-    public long Field;
-    public sbyte? FieldNullable;
-    public object FieldReference;
+       public long Field;
+       public sbyte? FieldNullable;
+       public object FieldReference;
 
        public int Prop { get; set; }
-    public byte? PropNullable { get; set; }
-    public string PropReference { get; set; }
+       public byte? PropNullable { get; set; }
+       public object PropReference { get; set; }
 
-    public event Action ev1;
+       public event Action ev1;
 }
 
 class C
 {
-    static int TestProperty ()
-    {
-        CI ci = null;
-        var m1 = ci?.Prop;
-        var m2 = ci?.PropNullable;
-        var m3 = ci?.PropReference;
+       static int TestProperty ()
+       {
+               CI ci = null;
+
+               var m1 = ci?.Prop;
+               var m2 = ci?.PropNullable;
+               var m3 = ci?.PropReference;
+
+               var m4 = ci?.Prop.ToString () ?? "N";
+               if (m4 != "N")
+                       return 1;
+
+               var m5 = ci?.PropNullable.ToString () ?? "N";
+               if (m5 != "N")
+                       return 2;
+
+               var m6 = ci?.PropReference.ToString () ?? "N";
+               if (m6 != "N")
+                       return 3; 
 
 //        ci?.Prop = 6;
 
-        ci = new CI ();
-        m1 = ci?.Prop;
-        m2 = ci?.PropNullable;
-        m3 = ci?.PropReference;
+               ci = new CI ();
+               m1 = ci?.Prop;
+               m2 = ci?.PropNullable;
+               m3 = ci?.PropReference;
 
 //        ci?.Prop = 5;
 //        if (ci.Prop != 5)
@@ -38,22 +51,33 @@ class C
 //      var pp1 = ci?.Prop = 4;
 //      var pp2 = ci?.Prop += 4;
 
-        return 0;
-    }
+               return 0;
+       }
 
-    static int TestField ()
-    {
-        CI ci = null;
-        var m1 = ci?.Field;
-        var m2 = ci?.FieldNullable;
-        var m3 = ci?.FieldReference;
+       static int TestField ()
+       {
+               CI ci = null;
+               var m1 = ci?.Field;
+               var m2 = ci?.FieldNullable;
+               var m3 = ci?.FieldReference;
+               var m4 = ci?.Field.ToString () ?? "N";
+               if (m4 != "N")
+                       return 1;
+
+               var m5 = ci?.FieldNullable.ToString () ?? "N";
+               if (m5 != "N")
+                       return 2;
+
+               var m6 = ci?.FieldReference.ToString () ?? "N";
+               if (m6 != "N")
+                       return 3; 
 
 //        ci?.Field = 6;
 
-        ci = new CI ();
-        m1 = ci?.Field;
-        m2 = ci?.FieldNullable;
-        m3 = ci?.FieldReference;
+               ci = new CI ();
+               m1 = ci?.Field;
+               m2 = ci?.FieldNullable;
+               m3 = ci?.FieldReference;
 
 //        ci?.Field = 5;
 //        if (ci.Field != 5)
@@ -64,37 +88,37 @@ class C
 //      var pp1 = ci?.Field = 4;
 //      var pp2 = ci?.Field += 4;
 
-        return 0;
-    }
-
-    static int TestEvent ()
-    {
-        CI ci = null;
-        ci?.ev1 += null;
-
-        ci = new CI ();
-        ci?.ev1 += null;
-
-        return 0;
-    }
-
-    static int Main ()
-    {
-        int res;
-
-        res = TestProperty ();
-        if (res != 0)
-            return 10 + res;
-
-        res = TestField ();
-        if (res != 0)
-            return 20 + res;
-
-        res = TestEvent ();
-        if (res != 0)
-            return 30 + res;            
-
-       Console.WriteLine ("ok");
-        return 0;
-    }
+               return 0;
+       }
+/*
+       static int TestEvent ()
+       {
+               CI ci = null;
+               ci?.ev1 += null;
+
+               ci = new CI ();
+               ci?.ev1 += null;
+
+               return 0;
+       }
+*/
+       static int Main ()
+       {
+               int res;
+
+               res = TestProperty ();
+               if (res != 0)
+                       return 10 + res;
+
+               res = TestField ();
+               if (res != 0)
+                       return 20 + res;
+
+//             res = TestEvent ();
+//             if (res != 0)
+//                     return 30 + res;            
+
+               Console.WriteLine ("ok");
+               return 0;
+       }
 }
\ No newline at end of file
index 5fa5c6ff9b4ee213e5cf6d08f1cd88dbb6101ca6..e8d9cfffbf118c708687b337c40dff73a0d9b587 100644 (file)
@@ -20,6 +20,10 @@ class C
                if (v2 != null)
                        return 2;
 
+               var v3 = arr? [0].GetHashCode () ?? 724;
+               if (v3 != 724)
+                       return 3;
+
 // TODO: Disabled for now?
 //        arr? [0] += 2;
                return 0;
@@ -36,6 +40,10 @@ class C
                if (v2 != null)
                        return 2;
 
+               var v3 = ci? [0].GetHashCode () ?? 724;
+               if (v3 != 724)
+                       return 3;
+
 // TODO: Disabled for now?
 //       ci? [0] += 3;
                return 0;
diff --git a/mcs/tests/test-null-operator-06.cs b/mcs/tests/test-null-operator-06.cs
new file mode 100644 (file)
index 0000000..63cfb4a
--- /dev/null
@@ -0,0 +1,28 @@
+public class C
+{
+       static int Main ()
+       {
+               string x = null;
+               var t1 = x?.ToString ().ToString ().ToString () ?? "t1";
+               if (t1 != "t1")
+                       return 1;
+
+               var t2 = x?.ToString ().ToString ()?.ToString () ?? "t2";
+               if (t2 != "t2")
+                       return 2;
+
+               var t3 = x?.ToString ()?.ToString ()?.ToString () ?? "t3";
+               if (t3 != "t3")
+                       return 3;
+
+               var t4 = x?.ToString ().GetHashCode () ?? 9;
+               if (t4 != 9)
+                       return 4;
+
+               var t5 = x?.ToString ()?.GetHashCode () ?? 8;
+               if (t5 != 8)
+                       return 5;
+
+               return 0;
+       }
+}
\ No newline at end of file
index 9298139dbbdcf026ac1f275a450e1c29c554bbbf..92f9d2321d84cf3893d28c7da54501772e3e1c46 100644 (file)
         <size>22</size>\r
       </method>\r
       <method name="Int32 TestArray()" attrs="145">\r
-        <size>349</size>\r
+        <size>485</size>\r
       </method>\r
       <method name="Int32 TestReferenceType()" attrs="145">\r
         <size>231</size>\r
         <size>58</size>\r
       </method>\r
       <method name="Int32 TestNullable()" attrs="145">\r
-        <size>384</size>\r
+        <size>386</size>\r
       </method>\r
       <method name="Int32 Main()" attrs="145">\r
         <size>120</size>\r
       <method name="Void set_PropNullable(System.Nullable`1[System.Byte])" attrs="2182">\r
         <size>8</size>\r
       </method>\r
-      <method name="System.String get_PropReference()" attrs="2182">\r
-        <size>14</size>\r
-      </method>\r
-      <method name="Void set_PropReference(System.String)" attrs="2182">\r
-        <size>8</size>\r
-      </method>\r
       <method name="Void add_ev1(System.Action)" attrs="2182">\r
         <size>42</size>\r
       </method>\r
     </type>\r
     <type name="C">\r
       <method name="Int32 Main()" attrs="145">\r
-        <size>86</size>\r
+        <size>64</size>\r
       </method>\r
       <method name="Void .ctor()" attrs="6278">\r
         <size>7</size>\r
       </method>\r
       <method name="Int32 TestProperty()" attrs="145">\r
-        <size>168</size>\r
+        <size>368</size>\r
       </method>\r
       <method name="Int32 TestField()" attrs="145">\r
-        <size>168</size>\r
+        <size>360</size>\r
       </method>\r
-      <method name="Int32 TestEvent()" attrs="145">\r
-        <size>50</size>\r
+    </type>\r
+    <type name="CI">\r
+      <method name="System.Object get_PropReference()" attrs="2182">\r
+        <size>14</size>\r
+      </method>\r
+      <method name="Void set_PropReference(System.Object)" attrs="2182">\r
+        <size>8</size>\r
       </method>\r
     </type>\r
   </test>\r
     </type>\r
     <type name="C">\r
       <method name="Int32 TestArrayAccess()" attrs="145">\r
-        <size>114</size>\r
+        <size>208</size>\r
       </method>\r
       <method name="Int32 TestIndexerAccess()" attrs="145">\r
-        <size>93</size>\r
+        <size>191</size>\r
       </method>\r
       <method name="Int32 Main()" attrs="145">\r
         <size>64</size>\r
       </method>\r
     </type>\r
   </test>\r
+  <test name="test-null-operator-06.cs">\r
+    <type name="C">\r
+      <method name="Int32 Main()" attrs="145">\r
+        <size>409</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
   <test name="test-partial-01.cs">\r
     <type name="Foo.Hello">\r
       <method name="Void .ctor()" attrs="6278">\r