[mcs] Fixes codegen for pattern probing with value-type variables
authorMarek Safar <marek.safar@gmail.com>
Thu, 5 Oct 2017 08:28:06 +0000 (10:28 +0200)
committerMarek Safar <marek.safar@gmail.com>
Thu, 5 Oct 2017 20:42:07 +0000 (22:42 +0200)
mcs/mcs/expression.cs
mcs/mcs/statement.cs
mcs/tests/test-pattern-10.cs [new file with mode: 0644]
mcs/tests/test-pattern-11.cs [new file with mode: 0644]
mcs/tests/test-pattern-12.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_x.xml

index 89207a3b54a26868eeadd5b13ed7110a4e9f768e..732ee3ee934c355a27e73efa58f02e1f32bfb803 100644 (file)
@@ -1676,8 +1676,6 @@ namespace Mono.CSharp
 
                void EmitLoad (EmitContext ec)
                {
-                       Label no_value_label = new Label ();
-
                        if (expr_unwrap != null) {
                                expr_unwrap.EmitCheck (ec);
 
@@ -1685,44 +1683,76 @@ namespace Mono.CSharp
                                        return;
 
                                ec.Emit (OpCodes.Dup);
-                               no_value_label = ec.DefineLabel ();
+                               var no_value_label = ec.DefineLabel ();
                                ec.Emit (OpCodes.Brfalse_S, no_value_label);
 
                                if (Variable.HoistedVariant != null)
                                        ec.EmitThis ();
 
                                expr_unwrap.Emit (ec);
-                       } else {
-                               if (Variable?.HoistedVariant != null)
-                                       ec.EmitThis ();
 
-                               expr.Emit (ec);
+                               if (Variable.HoistedVariant != null) {
+                                       Variable.HoistedVariant.EmitAssignFromStack (ec);
+                               } else {
+                                       //
+                                       // It's ok to have variable builder created out of order. It simplifies emit
+                                       // of statements like while (condition) { }
+                                       //
+                                       if (!Variable.Created)
+                                               Variable.CreateBuilder (ec);
 
-                               // Only to make verifier happy
-                               if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type))
-                                       ec.Emit (OpCodes.Box, expr.Type);
+                                       Variable.EmitAssign (ec);
+                               }
 
-                               ec.Emit (OpCodes.Isinst, probe_type_expr);
+                               ec.MarkLabel (no_value_label);
+                               return;
                        }
 
+                       expr.Emit (ec);
+
+                       bool vtype_variable = Variable != null && (probe_type_expr.IsGenericParameter || TypeSpec.IsValueType (ProbeType.Type));
+                       LocalBuilder expr_copy = null;
+
+                       if (vtype_variable && !ExpressionAnalyzer.IsInexpensiveLoad (expr)) {
+                               expr_copy = ec.GetTemporaryLocal (expr.Type);
+                               ec.Emit (OpCodes.Stloc, expr_copy);
+                               ec.Emit (OpCodes.Ldloc, expr_copy);
+                       } else if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type)) {
+                               //
+                               // Only to make verifier happy
+                               //
+                               ec.Emit (OpCodes.Box, expr.Type);
+                       }
+
+                       ec.Emit (OpCodes.Isinst, probe_type_expr);
+
                        if (Variable != null) {
-                               bool value_on_stack;
-                               if (probe_type_expr.IsGenericParameter || probe_type_expr.IsNullableType) {
-                                       ec.Emit (OpCodes.Dup);
+                               ec.Emit (OpCodes.Dup);
+
+                               var nonmatching_label = ec.DefineLabel ();
+                               ec.Emit (OpCodes.Brfalse_S, nonmatching_label);
+
+                               if (vtype_variable) {
+                                       if (expr_copy != null) {
+                                               ec.Emit (OpCodes.Ldloc, expr_copy);
+                                               ec.FreeTemporaryLocal (expr_copy, expr.Type);
+                                       } else {
+                                               expr.Emit (ec);
+                                       }
+
                                        ec.Emit (OpCodes.Unbox_Any, probe_type_expr);
-                                       value_on_stack = true;
                                } else {
-                                       value_on_stack = false;
+                                       // Already on the stack
                                }
 
                                if (Variable.HoistedVariant != null) {
-                                       Variable.HoistedVariant.EmitAssignFromStack (ec);
+                                       var temp = new LocalTemporary (ProbeType.Type);
+                                       temp.Store (ec);
+                                       Variable.HoistedVariant.EmitAssign (ec, temp, false, false);
+                                       temp.Release (ec);
 
-                                       if (expr_unwrap != null) {
-                                               ec.MarkLabel (no_value_label);
-                                       } else if (!value_on_stack) {
+                                       if (!vtype_variable)
                                                Variable.HoistedVariant.Emit (ec);
-                                       }
                                } else {
                                        //
                                        // It's ok to have variable builder created out of order. It simplifies emit
@@ -1733,12 +1763,11 @@ namespace Mono.CSharp
 
                                        Variable.EmitAssign (ec);
 
-                                       if (expr_unwrap != null) {
-                                               ec.MarkLabel (no_value_label);
-                                       } else if (!value_on_stack) {
+                                       if (!vtype_variable)
                                                Variable.Emit (ec);
-                                       }
                                }
+
+                               ec.MarkLabel (nonmatching_label);
                        }
                }
 
index 369343a5df7703515bec4e4f8963e4f71772467e..58ba2795e4b956ffd546f6da75ee39b945fe52c4 100644 (file)
@@ -774,6 +774,8 @@ namespace Mono.CSharp {
 
                        ec.Emit (OpCodes.Br, test);
                        ec.MarkLabel (loop);
+
+                       Condition?.EmitPrepare (ec);
                        Statement.Emit (ec);
 
                        ec.MarkLabel (ec.LoopBegin);
diff --git a/mcs/tests/test-pattern-10.cs b/mcs/tests/test-pattern-10.cs
new file mode 100644 (file)
index 0000000..967600e
--- /dev/null
@@ -0,0 +1,31 @@
+using System;
+
+class X
+{
+       public static int Main ()
+       {
+               Test (null);
+               if (Test ((long) 0) != 1)
+                       return 1;
+
+               object o = "aa";
+               if (o != null) {
+                       if (o is long s) {
+                               Console.WriteLine (s);
+                       }
+               } else if (o is string s) {
+                       Console.WriteLine (s);
+               }
+
+               return 0;
+       }
+
+       static int Test (object o)
+       {
+               if (o is long s) {
+                       return 1;
+               }
+
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/mcs/tests/test-pattern-11.cs b/mcs/tests/test-pattern-11.cs
new file mode 100644 (file)
index 0000000..bc88951
--- /dev/null
@@ -0,0 +1,15 @@
+using System;
+
+class X
+{
+       public static int Main ()
+       {
+               object o = null;
+               for (o = "abcd"; o is String s; o = null) {
+                       Console.WriteLine (s);
+               }
+
+               
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/mcs/tests/test-pattern-12.cs b/mcs/tests/test-pattern-12.cs
new file mode 100644 (file)
index 0000000..19f39f1
--- /dev/null
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+class X
+{
+       public static int Main ()
+       {
+               foreach (var x in Test1 ("2"))
+               {
+                       Console.WriteLine (x);
+                       return 1;
+               }
+
+               foreach (var x in Test2 (2))
+               {
+                       Console.WriteLine (x);
+                       return 2;
+               }
+
+               return 0;
+       }
+
+       public static IEnumerable<object> Test1 (object expr)
+       {
+               if (expr is short list)
+               {
+                       yield return "list.Length";
+               }
+       }
+
+       public static IEnumerable<object> Test2 (object expr)
+       {
+               if (expr is string list)
+               {
+                       yield return "list.Length";
+               }
+       }
+}
\ No newline at end of file
index 636afd18a008618d542007d52c3a340bb8697323..c2c981233057bfb16b5ca3e59613dc6f57e77981 100644 (file)
   <test name="test-pattern-01.cs">
     <type name="TypePattern">
       <method name="Int32 Main()" attrs="150">
-        <size>118</size>
+        <size>124</size>
       </method>
       <method name="Void .ctor()" attrs="6278">
         <size>7</size>
       </method>
       <method name="Void Test1(System.Object)" attrs="145">
-        <size>24</size>
+        <size>32</size>
       </method>
       <method name="System.String Test2(System.Object)" attrs="145">
-        <size>39</size>
+        <size>42</size>
       </method>
     </type>
   </test>
     </type>
     <type name="X+&lt;Test&gt;c__Iterator0">
       <method name="Boolean MoveNext()" attrs="486">
-        <size>124</size>
+        <size>129</size>
       </method>
       <method name="Int32 System.Collections.Generic.IEnumerator&lt;int&gt;.get_Current()" attrs="2529">
         <size>14</size>
       </method>
     </type>
   </test>
+  <test name="test-pattern-10.cs">
+    <type name="X">
+      <method name="Int32 Main()" attrs="150">
+        <size>114</size>
+      </method>
+      <method name="Int32 Test(System.Object)" attrs="145">
+        <size>39</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
+  <test name="test-pattern-11.cs">
+    <type name="X">
+      <method name="Int32 Main()" attrs="150">
+        <size>49</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
+  <test name="test-pattern-12.cs">
+    <type name="X">
+      <method name="Int32 Main()" attrs="150">
+        <size>160</size>
+      </method>
+      <method name="System.Collections.Generic.IEnumerable`1[System.Object] Test1(System.Object)" attrs="150">
+        <size>30</size>
+      </method>
+      <method name="System.Collections.Generic.IEnumerable`1[System.Object] Test2(System.Object)" attrs="150">
+        <size>30</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="X+&lt;Test1&gt;c__Iterator0">
+      <method name="Boolean MoveNext()" attrs="486">
+        <size>106</size>
+      </method>
+      <method name="System.Object System.Collections.Generic.IEnumerator&lt;object&gt;.get_Current()" attrs="2529">
+        <size>14</size>
+      </method>
+      <method name="System.Object System.Collections.IEnumerator.get_Current()" attrs="2529">
+        <size>14</size>
+      </method>
+      <method name="Void Dispose()" attrs="486">
+        <size>15</size>
+      </method>
+      <method name="Void Reset()" attrs="486">
+        <size>6</size>
+      </method>
+      <method name="System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()" attrs="481">
+        <size>14</size>
+      </method>
+      <method name="System.Collections.Generic.IEnumerator`1[System.Object] System.Collections.Generic.IEnumerable&lt;object&gt;.GetEnumerator()" attrs="481">
+        <size>40</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="X+&lt;Test2&gt;c__Iterator1">
+      <method name="Boolean MoveNext()" attrs="486">
+        <size>99</size>
+      </method>
+      <method name="System.Object System.Collections.Generic.IEnumerator&lt;object&gt;.get_Current()" attrs="2529">
+        <size>14</size>
+      </method>
+      <method name="System.Object System.Collections.IEnumerator.get_Current()" attrs="2529">
+        <size>14</size>
+      </method>
+      <method name="Void Dispose()" attrs="486">
+        <size>15</size>
+      </method>
+      <method name="Void Reset()" attrs="486">
+        <size>6</size>
+      </method>
+      <method name="System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()" attrs="481">
+        <size>14</size>
+      </method>
+      <method name="System.Collections.Generic.IEnumerator`1[System.Object] System.Collections.Generic.IEnumerable&lt;object&gt;.GetEnumerator()" attrs="481">
+        <size>40</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="test-pragma-unrecognized.cs">
     <type name="C">
       <method name="Void Main()" attrs="150">