void EmitLoad (EmitContext ec)
{
- Label no_value_label = new Label ();
-
if (expr_unwrap != null) {
expr_unwrap.EmitCheck (ec);
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
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);
}
}
ec.Emit (OpCodes.Br, test);
ec.MarkLabel (loop);
+
+ Condition?.EmitPrepare (ec);
Statement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
<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+<Test>c__Iterator0">
<method name="Boolean MoveNext()" attrs="486">
- <size>124</size>
+ <size>129</size>
</method>
<method name="Int32 System.Collections.Generic.IEnumerator<int>.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+<Test1>c__Iterator0">
+ <method name="Boolean MoveNext()" attrs="486">
+ <size>106</size>
+ </method>
+ <method name="System.Object System.Collections.Generic.IEnumerator<object>.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<object>.GetEnumerator()" attrs="481">
+ <size>40</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="X+<Test2>c__Iterator1">
+ <method name="Boolean MoveNext()" attrs="486">
+ <size>99</size>
+ </method>
+ <method name="System.Object System.Collections.Generic.IEnumerator<object>.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<object>.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">