[mcs] C#7 tuples var deconstruct
authorMarek Safar <marek.safar@gmail.com>
Fri, 30 Jun 2017 22:34:37 +0000 (00:34 +0200)
committerMarek Safar <marek.safar@gmail.com>
Fri, 30 Jun 2017 22:34:37 +0000 (00:34 +0200)
mcs/errors/cs0128-5.cs [new file with mode: 0644]
mcs/errors/cs8130.cs [new file with mode: 0644]
mcs/errors/cs8132-2.cs [new file with mode: 0644]
mcs/errors/cs8132.cs
mcs/errors/cs8199.cs [new file with mode: 0644]
mcs/errors/known-issues-net_4_x
mcs/mcs/assign.cs
mcs/mcs/expression.cs
mcs/mcs/tuples.cs
mcs/tests/test-tuple-03.cs
mcs/tests/ver-il-net_4_x.xml

diff --git a/mcs/errors/cs0128-5.cs b/mcs/errors/cs0128-5.cs
new file mode 100644 (file)
index 0000000..405537d
--- /dev/null
@@ -0,0 +1,11 @@
+// CS0128: A local variable named `xx' is already defined in this scope
+// Line: 9
+
+class X
+{
+       public static void Main ()
+       {
+               short xx;
+               var (xx, yy) = (1, 'g');
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8130.cs b/mcs/errors/cs8130.cs
new file mode 100644 (file)
index 0000000..de6d131
--- /dev/null
@@ -0,0 +1,10 @@
+// CS8130: Cannot infer the type of implicitly-typed deconstruction variable `yy'
+// Line: 8
+
+class X
+{
+       public static void Main ()
+       {
+               var (xx, yy) = (1, Main);
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8132-2.cs b/mcs/errors/cs8132-2.cs
new file mode 100644 (file)
index 0000000..5658fa9
--- /dev/null
@@ -0,0 +1,18 @@
+// CS8132: Cannot deconstruct a tuple of `2' elements into `3' variables
+// Line: 11
+
+class X
+{
+       static int xx;
+       static long yy, zz;
+
+       public static void Main ()
+       {
+               (xx, yy, zz) = Foo ();
+       }
+
+       static (int, long) Foo ()
+       {
+               return (1, 3);
+       }
+}
\ No newline at end of file
index be9e522aec42a80056f0d61219b5d4502e4ed06c..3855d31159c77a4b14520b09e2e5158a1e5016c2 100644 (file)
@@ -1,4 +1,4 @@
-// CS8132:
+// CS8132: Cannot deconstruct a tuple of `2' elements into `3' variables
 // Line: 8
 
 class C
diff --git a/mcs/errors/cs8199.cs b/mcs/errors/cs8199.cs
new file mode 100644 (file)
index 0000000..3d89c68
--- /dev/null
@@ -0,0 +1,10 @@
+// CS8199: The syntax `var (...)' as an lvalue is reserved
+// Line: 8
+
+class X
+{
+       public static void Main ()
+       {
+               var (x.a, y) = (1, 'g');
+       }
+}
\ No newline at end of file
index 9282fd4adb9ca50b0c36ae5f0994044de814604f..40f81ded9703c88ae78fa849f4db1d028545e0bf 100644 (file)
@@ -19,7 +19,6 @@ cs0457-2.cs
 cs0457.cs
 
 cs8129.cs NO ERROR
-cs8132.cs NO ERROR
 cs8141.cs
 cs8141-2.cs
 cs8144.cs
index 31231e6bf336f013a66dc7ce1b26cdbf9656e019..a07c8c0ef397d1e2d597680f035cf078aa732489 100644 (file)
@@ -502,6 +502,12 @@ namespace Mono.CSharp {
                                pe.SetBackingFieldAssigned (fc);
                                return;
                        }
+
+                       var td = target as TupleDeconstruct;
+                       if (td != null) {
+                               td.SetGeneratedFieldAssigned (fc);
+                               return;
+                       }
                }
 
                public override Reachability MarkReachable (Reachability rc)
index 74f0aadd45b33f0f86b7ae297cb653f7a00cc1a3..b9f47b5a361054a9b3675508668144a484c51de8 100644 (file)
@@ -7118,7 +7118,24 @@ namespace Mono.CSharp
                {
                        var sn = expr as SimpleName;
                        if (sn != null && sn.Name == "var" && sn.Arity == 0 && arguments?.Count > 1) {
-                               throw new NotImplementedException ("var deconstruct");
+                               var targets = new List<Expression> (arguments.Count);
+                               var variables = new List<LocalVariable> (arguments.Count);
+                               foreach (var arg in arguments) {
+                                       var arg_sn = arg.Expr as SimpleName;
+                                       if (arg_sn == null || arg_sn.Arity != 0) {
+                                               rc.Report.Error (8199, loc, "The syntax `var (...)' as an lvalue is reserved");
+                                               return ErrorExpression.Instance;
+                                       }
+
+                                       var lv = new LocalVariable (rc.CurrentBlock, arg_sn.Name, arg.Expr.Location);
+                                       rc.CurrentBlock.AddLocalName (lv);
+                                       variables.Add (lv);
+
+                                       targets.Add (new LocalVariableReference (lv, arg_sn.Location));
+                               }
+
+                               var res = new TupleDeconstruct (targets, variables, right_side, loc);
+                               return res.Resolve (rc);
                        }
 
                        return base.DoResolveLValue (rc, right_side);
index e4f56ca131b6a8443f087312a8316477c3540101..559c2f3ee43b32b2a6861129f397ea459fde7a11 100644 (file)
@@ -428,10 +428,12 @@ namespace Mono.CSharp
                }
        }
 
-       class TupleDeconstruct : ExpressionStatement
+       class TupleDeconstruct : ExpressionStatement, IAssignMethod
        {
                Expression source;
                List<Expression> targetExprs;
+               List<LocalVariable> variablesToInfer;
+               Expression instance;
 
                public TupleDeconstruct (List<Expression> targetExprs, Expression source, Location loc)
                {
@@ -440,6 +442,12 @@ namespace Mono.CSharp
                        this.loc = loc;
                }
 
+               public TupleDeconstruct (List<Expression> targetExprs, List<LocalVariable> variables, Expression source, Location loc)
+                       : this (targetExprs, source, loc)
+               {
+                       this.variablesToInfer = variables;
+               }
+
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
                        ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
@@ -458,16 +466,40 @@ namespace Mono.CSharp
                                return null;
                        }
 
-                       var tupleLiteral = src as TupleLiteral;
-                       if (tupleLiteral != null) {
-                               if (tupleLiteral.Elements.Count != targetExprs.Count) {
-                                       rc.Report.Error (8132, loc, "Cannot deconstruct a tuple of `{0}' elements into `{0}' variables",
-                                               tupleLiteral.Elements.Count.ToString (), targetExprs.Count.ToString ());
+                       var src_type = src.Type;
+
+                       if (src_type.IsTupleType) {
+                               if (src_type.Arity != targetExprs.Count) {
+                                       rc.Report.Error (8132, loc, "Cannot deconstruct a tuple of `{0}' elements into `{1}' variables",
+                                               src_type.Arity.ToString (), targetExprs.Count.ToString ());
                                        return null;
                                }
 
+                               var tupleLiteral = src as TupleLiteral;
+                               if (tupleLiteral == null && !ExpressionAnalyzer.IsInexpensiveLoad (src)) {
+                                       var expr_variable = LocalVariable.CreateCompilerGenerated (source.Type, rc.CurrentBlock, loc);
+                                       source = new CompilerAssign (expr_variable.CreateReferenceExpression (rc, loc), source, loc);
+                                       instance = expr_variable.CreateReferenceExpression (rc, loc);
+                               }
+
                                for (int i = 0; i < targetExprs.Count; ++i) {
-                                       targetExprs [i] = new SimpleAssign (targetExprs [i], tupleLiteral.Elements [i].Expr).Resolve (rc);
+                                       var tle = src_type.TypeArguments [i];
+
+                                       var lv = variablesToInfer? [i];
+                                       if (lv != null) {
+                                               if (InternalType.HasNoType (tle)) {
+                                                       rc.Report.Error (8130, Location, "Cannot infer the type of implicitly-typed deconstruction variable `{0}'", lv.Name);
+                                                       lv.Type = InternalType.ErrorType;
+                                                       continue;
+                                               }
+
+                                               lv.Type = tle;
+                                               lv.PrepareAssignmentAnalysis ((BlockContext) rc);
+                                       }
+
+
+                                       var element_src = tupleLiteral == null ? new MemberAccess (instance, NamedTupleSpec.GetElementPropertyName (i)) : tupleLiteral.Elements [i].Expr;
+                                       targetExprs [i] = new SimpleAssign (targetExprs [i], element_src).Resolve (rc);
                                }
 
                                eclass = ExprClass.Value;
@@ -475,12 +507,22 @@ namespace Mono.CSharp
                                return this;
                        }
 
-                       if (src.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
+                       if (src_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
                                rc.Report.Error (8133, loc, "Cannot deconstruct dynamic objects");
                                return null;
                        }
 
-                       throw new NotImplementedException ("Deconstruct assignment");
+                       /*
+                       var args = new Arguments (targetExprs.Count);
+                       foreach (var t in targetExprs) {
+                               args.Add (new Argument (t, Argument.AType.Out));
+                       }
+
+                       var invocation = new Invocation (new MemberAccess (src, "Deconstruct"), args);
+                       var res = invocation.Resolve (rc);
+                       */
+
+                       throw new NotImplementedException ("Custom deconstruct");
                }
 
                public override void Emit (EmitContext ec)
@@ -490,14 +532,42 @@ namespace Mono.CSharp
 
                public override void EmitStatement (EmitContext ec)
                {
+                       if (instance != null)
+                               ((ExpressionStatement) source).EmitStatement (ec);
+                       
                        foreach (ExpressionStatement expr in targetExprs)
                                expr.EmitStatement (ec);
                }
 
+               public void Emit (EmitContext ec, bool leave_copy)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
+               {
+                       if (leave_copy)
+                               throw new NotImplementedException ();
+
+                       foreach (var lv in variablesToInfer)
+                               lv.CreateBuilder (ec);
+
+                       EmitStatement (ec);
+               }
+
                public override void FlowAnalysis (FlowAnalysisContext fc)
                {
                        foreach (var expr in targetExprs)
                                expr.FlowAnalysis (fc);
                }
+
+               public void SetGeneratedFieldAssigned (FlowAnalysisContext fc)
+               {
+                       if (variablesToInfer == null)
+                               return;
+
+                       foreach (var lv in variablesToInfer)
+                               fc.SetVariableAssigned (lv.VariableInfo);
+               }
        }
 }
\ No newline at end of file
index 7eb45a893ca0919d7d0df06343ad2c1e15c64dd8..dfc07de4a7ecc932e6aac7bae2377a3ba35e7158 100644 (file)
@@ -3,24 +3,32 @@ using System.Linq.Expressions;
 
 class TupleDeconstruct
 {
+       static int s_xx;
+       static long s_yy;
+
        public static int Main ()
        {
-//             var (xx, yy) = (1, 2);
-//             if (xx != 1)
-//                     return 1;
+               var (xx, yy) = (1, 2);
+               if (xx != 1)
+                       return 1;
 
-//             if (yy != 2)
-//                     return 2;
+               if (yy != 2)
+                       return 2;
 
                int x, y;
                (x, y) = (1, 2);
                if (x != 1)
-                       return 1;
+                       return 3;
 
                if (y != 2)
-                       return 2;
+                       return 4;
+
+               (s_xx, s_yy) = Test3 ();
+               if (s_xx != 1)
+                       return 5;
 
-//             var (l1, l2) = ('a', 'b');
+               if (s_yy != 3)
+                       return 6;
 
 //             var cwd = new ClassWithDeconstruct ();
 //             var (m1, m2) = cwd;
@@ -36,8 +44,9 @@ class TupleDeconstruct
                (c.Prop1, c.Prop2) = (1, 2);
        }
 
-       static void var1 (object o1, object o2)
+       static (int, long) Test3 ()
        {
+               return (1, 3);
        }
 
        static void TestCustom ()
index 77879db1c1e3bf2634c5118ca8556b6350456991..5eac11969e1220f675083b69ce0a2e414135e6e0 100644 (file)
   <test name="test-tuple-03.cs">
     <type name="TupleDeconstruct">
       <method name="Int32 Main()" attrs="150">
-        <size>42</size>
+        <size>144</size>
       </method>
       <method name="Void Test2()" attrs="145">
         <size>22</size>
       </method>
-      <method name="Void var1(System.Object, System.Object)" attrs="145">
-        <size>2</size>
-      </method>
       <method name="Void TestCustom()" attrs="145">
         <size>7</size>
       </method>
         <size>7</size>
       </method>
     </type>
+    <type name="TupleDeconstruct">
+      <method name="System.ValueTuple`2[System.Int32,System.Int64] Test3()" attrs="145">
+        <size>17</size>
+      </method>
+    </type>
   </test>
   <test name="test-tuple-04.cs">
     <type name="Test">