[mcs] Allow properties and indexers of by-ref values to be set without setter
[mono.git] / mcs / mcs / ecore.cs
index 4d472070a006bf87d04ff839d8260f1a6d20a894..34ff9a30dab3cb423f4b9f64b7a7640d67a2b56a 100644 (file)
@@ -45,7 +45,7 @@ namespace Mono.CSharp {
                PropertyAccess,
                EventAccess,
                IndexerAccess,
-               Nothing, 
+               Nothing,
        }
 
        /// <remarks>
@@ -239,6 +239,15 @@ namespace Mono.CSharp {
                        return null;
                }
 
+               protected void CheckExpressionVariable (ResolveContext rc)
+               {
+                       if (rc.HasAny (ResolveContext.Options.BaseInitializer | ResolveContext.Options.FieldInitializerScope)) {
+                               rc.Report.Error (8200, loc, "Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers");
+                       } else if (rc.HasSet (ResolveContext.Options.QueryClauseScope)) {
+                               rc.Report.Error (8201, loc, "Out variable and pattern variable declarations are not allowed within a query clause");
+                       }
+               }
+
                public static void ErrorIsInaccesible (IMemberContext rc, string member, Location loc)
                {
                        rc.Module.Compiler.Report.Error (122, loc, "`{0}' is inaccessible due to its protection level", member);
@@ -294,7 +303,12 @@ namespace Mono.CSharp {
                                return;
 
                        string from_type = type.GetSignatureForError ();
+                       if (type.Kind == MemberKind.ByRef)
+                               from_type = "ref " + from_type;
                        string to_type = target.GetSignatureForError ();
+                       if (target.Kind == MemberKind.ByRef)
+                               to_type = "ref " + to_type;
+
                        if (from_type == to_type) {
                                from_type = type.GetSignatureForErrorIncludingAssemblyName ();
                                to_type = target.GetSignatureForErrorIncludingAssemblyName ();
@@ -550,18 +564,18 @@ namespace Mono.CSharp {
                public Expression ResolveLValue (ResolveContext ec, Expression right_side)
                {
                        int errors = ec.Report.Errors;
-                       bool out_access = right_side == EmptyExpression.OutAccess;
+                       //bool out_access = right_side == EmptyExpression.OutAccess;
 
                        Expression e = DoResolveLValue (ec, right_side);
 
-                       if (e != null && out_access && !(e is IMemoryLocation)) {
+                       //if (e != null && out_access && !(e is IMemoryLocation)) {
                                // FIXME: There's no problem with correctness, the 'Expr = null' handles that.
                                //        Enabling this 'throw' will "only" result in deleting useless code elsewhere,
 
                                //throw new InternalErrorException ("ResolveLValue didn't return an IMemoryLocation: " +
                                //                                e.GetType () + " " + e.GetSignatureForError ());
-                               e = null;
-                       }
+                       //      e = null;
+                       //}
 
                        if (e == null) {
                                if (errors == ec.Report.Errors) {
@@ -638,6 +652,10 @@ namespace Mono.CSharp {
                        ec.Emit (OpCodes.Pop);
                }
 
+               public virtual void EmitPrepare (EmitContext ec)
+               {
+               }
+
                //
                // Emits the expression into temporary field variable. The method
                // should be used for await expressions only
@@ -863,6 +881,15 @@ namespace Mono.CSharp {
                                        return MemberLookupToExpression (rc, members, errorMode, queried_type, name, arity, restrictions, loc);
                        }
 
+                       if ((restrictions & MemberLookupRestrictions.InvocableOnly) == 0) {
+                               var ntuple = queried_type as NamedTupleSpec;
+                               if (ntuple != null) {
+                                       var ms = ntuple.FindElement (rc, name, loc);
+                                       if (ms != null)
+                                               return ExprClassFromMemberInfo (ms, loc);
+                               }
+                       }
+
                        return null;
                }
 
@@ -995,6 +1022,11 @@ namespace Mono.CSharp {
                {
                }
 
+               public virtual Reachability MarkReachable (Reachability rc)
+               {
+                       return rc;
+               }
+
                //
                // Special version of flow analysis for expressions which can return different
                // on-true and on-false result. Used by &&, ||, ?: expressions
@@ -1295,10 +1327,6 @@ namespace Mono.CSharp {
        /// </summary>
        public abstract class ExpressionStatement : Expression
        {
-               public virtual void MarkReachable (Reachability rc)
-               {
-               }
-
                public virtual ExpressionStatement ResolveStatement (BlockContext ec)
                {
                        Expression e = Resolve (ec);
@@ -1460,6 +1488,11 @@ namespace Mono.CSharp {
 #endif
                }
 
+               public override Reachability MarkReachable (Reachability rc)
+               {
+                       return child.MarkReachable (rc);
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression t)
                {
                        // Nothing to clone
@@ -2401,13 +2434,23 @@ namespace Mono.CSharp {
 
                public override void FlowAnalysis (FlowAnalysisContext fc)
                {
-                       expr.FlowAnalysis (fc);
+                       orig_expr.FlowAnalysis (fc);
+               }
+
+               public override void FlowAnalysisConditional (FlowAnalysisContext fc)
+               {
+                       orig_expr.FlowAnalysisConditional (fc);
                }
 
                public override SLE.Expression MakeExpression (BuilderContext ctx)
                {
                        return orig_expr.MakeExpression (ctx);
                }
+
+               public override Reachability MarkReachable (Reachability rc)
+               {
+                       return expr.MarkReachable (rc);
+               }
        }
 
        //
@@ -4021,6 +4064,11 @@ namespace Mono.CSharp {
                        throw new NotSupportedException ();
                }
 
+               public override void EmitPrepare (EmitContext ec)
+               {
+                       InstanceExpression?.EmitPrepare (ec);
+               }
+
                public void EmitCall (EmitContext ec, Arguments arguments, bool statement)
                {
                        var call = new CallEmitter ();
@@ -5374,6 +5422,11 @@ namespace Mono.CSharp {
                                        if (arg_type == InternalType.VarOutType)
                                                return 0;
 
+                                       var ref_arg_type = arg_type as ReferenceContainer;
+                                       if (ref_arg_type != null) {
+                                               arg_type = ref_arg_type.Element;
+                                       }
+
                                        //
                                        // Do full equality check after quick path
                                        //
@@ -5939,13 +5992,13 @@ namespace Mono.CSharp {
                        int arg_count = args == null ? 0 : args.Count;
 
                        for (; a_idx < arg_count; a_idx++, ++a_pos) {
-                               a = args[a_idx];
+                               a = args [a_idx];
                                if (a == null)
                                        continue;
 
                                if (p_mod != Parameter.Modifier.PARAMS) {
                                        p_mod = cpd.FixedParameters [a_idx].ModFlags;
-                                       pt = ptypes[a_idx];
+                                       pt = ptypes [a_idx];
                                        has_unsafe_arg |= pt.IsPointer;
 
                                        if (p_mod == Parameter.Modifier.PARAMS) {
@@ -5975,6 +6028,14 @@ namespace Mono.CSharp {
                                                continue;
                                        }
 
+                                       var ref_arg_type = arg_type as ReferenceContainer;
+                                       if (ref_arg_type != null) {
+                                               if (ref_arg_type.Element != pt)
+                                                       break;
+
+                                               return true;
+                                       }
+
                                        if (!TypeSpecComparer.IsEqual (arg_type, pt))
                                                break;
                                }
@@ -6482,12 +6543,12 @@ namespace Mono.CSharp {
                                                GetSignatureForError ());
                                }
 
-                               return null;
+                               return ErrorExpression.Instance;
                        }
 
                        if (right_side == EmptyExpression.LValueMemberAccess) {
                                // Already reported as CS1648/CS1650
-                               return null;
+                               return ErrorExpression.Instance;
                        }
 
                        if (right_side == EmptyExpression.LValueMemberOutAccess) {
@@ -6498,7 +6559,7 @@ namespace Mono.CSharp {
                                        rc.Report.Error (1649, loc, "Members of readonly field `{0}' cannot be passed ref or out (except in a constructor)",
                                                GetSignatureForError ());
                                }
-                               return null;
+                               return ErrorExpression.Instance;
                        }
 
                        if (IsStatic) {
@@ -6509,7 +6570,7 @@ namespace Mono.CSharp {
                                        GetSignatureForError ());
                        }
 
-                       return null;
+                       return ErrorExpression.Instance;
                }
 
                public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
@@ -7298,6 +7359,15 @@ namespace Mono.CSharp {
                                Error_NullPropagatingLValue (rc);
 
                        if (right_side == EmptyExpression.OutAccess) {
+                               if (best_candidate?.MemberType.Kind == MemberKind.ByRef) {
+                                       if (Arguments?.ContainsEmitWithAwait () == true) {
+                                               rc.Report.Error (8178, loc, "`await' cannot be used in an expression containing a call to `{0}' because it returns by reference",
+                                                       GetSignatureForError ());
+                                       }
+
+                                       return this;
+                               }
+
                                // TODO: best_candidate can be null at this point
                                INamedBlockVariable variable = null;
                                if (best_candidate != null && rc.CurrentBlock.ParametersBlock.TopBlock.GetLocalName (best_candidate.Name, rc.CurrentBlock, ref variable) && variable is Linq.RangeVariable) {
@@ -7324,6 +7394,11 @@ namespace Mono.CSharp {
                                if (ResolveAutopropertyAssignment (rc, right_side))
                                        return this;
 
+                               if (best_candidate.MemberType.Kind == MemberKind.ByRef) {
+                                       getter = CandidateToBaseOverride (rc, best_candidate.Get);
+                                       return ByRefDereference.Create(this).Resolve(rc);
+                               }
+
                                rc.Report.Error (200, loc, "Property or indexer `{0}' cannot be assigned to (it is read-only)",
                                        GetSignatureForError ());
                                return null;
@@ -7774,13 +7849,14 @@ namespace Mono.CSharp {
                {
                }
 
-               public bool InferType (ResolveContext ec, Expression right_side)
+               public bool InferType (ResolveContext ec, Expression rhs)
                {
                        if (type != null)
                                throw new InternalErrorException ("An implicitly typed local variable could not be redefined");
                        
-                       type = right_side.Type;
-                       if (type == InternalType.NullLiteral || type.Kind == MemberKind.Void || type == InternalType.AnonymousMethod || type == InternalType.MethodGroup) {
+                       type = rhs.Type;
+
+                       if (type.Kind == MemberKind.Void || InternalType.HasNoType (type) || (rhs is TupleLiteral && TupleLiteral.ContainsNoTypeElement (type))) {
                                ec.Report.Error (815, loc,
                                        "An implicitly typed local variable declaration cannot be initialized with `{0}'",
                                        type.GetSignatureForError ());