[mcs] Add codegen for try-finally block with unreachable closing brace. Fixes #35401
authorMarek Safar <marek.safar@gmail.com>
Mon, 2 Nov 2015 13:44:21 +0000 (14:44 +0100)
committerMarek Safar <marek.safar@gmail.com>
Mon, 2 Nov 2015 13:45:28 +0000 (14:45 +0100)
mcs/mcs/statement.cs
mcs/tests/test-930.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_x.xml

index f3bde1607cbb59d7728eccb21143b870dda1fc0f..ba58d1f1380ebbb95642eab2d2d03541b839967e 100644 (file)
@@ -1408,6 +1408,10 @@ namespace Mono.CSharp {
 
                protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
                {
+                       // Goto to unreachable label
+                       if (label == null)
+                               return true;
+
                        if (fc.AddReachedLabel (label))
                                return true;
 
@@ -1424,12 +1428,12 @@ namespace Mono.CSharp {
 
                        if (try_finally != null) {
                                if (try_finally.FinallyBlock.HasReachableClosingBrace) {
-                                       label.AddGotoReference (rc, false);
+                                       label.AddGotoReference (rc);
                                } else {
-                                       label.AddGotoReference (rc, true);
+                                       label = null;
                                }
                        } else {
-                               label.AddGotoReference (rc, false);
+                               label.AddGotoReference (rc);
                        }
 
                        return Reachability.CreateUnreachable ();
@@ -1442,8 +1446,9 @@ namespace Mono.CSharp {
 
                protected override void DoEmit (EmitContext ec)
                {
+                       // This should only happen for goto from try block to unrechable label
                        if (label == null)
-                               throw new InternalErrorException ("goto emitted before target resolved");
+                               return;
 
                        Label l = label.LabelTarget (ec);
 
@@ -1478,7 +1483,6 @@ namespace Mono.CSharp {
                string name;
                bool defined;
                bool referenced;
-               bool finalTarget;
                Label label;
                Block block;
                
@@ -1525,9 +1529,6 @@ namespace Mono.CSharp {
                {
                        LabelTarget (ec);
                        ec.MarkLabel (label);
-
-                       if (finalTarget)
-                               ec.Emit (OpCodes.Br_S, label);
                }
 
                protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
@@ -1549,7 +1550,7 @@ namespace Mono.CSharp {
                        return rc;
                }
 
-               public void AddGotoReference (Reachability rc, bool finalTarget)
+               public void AddGotoReference (Reachability rc)
                {
                        if (referenced)
                                return;
@@ -1557,17 +1558,6 @@ namespace Mono.CSharp {
                        referenced = true;
                        MarkReachable (rc);
 
-                       //
-                       // Label is final target when goto jumps out of try block with
-                       // finally clause. In that case we need leave with target but in C#
-                       // terms the label is unreachable. Using finalTarget we emit
-                       // explicit label not just marker
-                       //
-                       if (finalTarget) {
-                               this.finalTarget = true;
-                               return;
-                       }
-
                        block.ScanGotoJump (this);
                }
 
@@ -2992,18 +2982,30 @@ namespace Mono.CSharp {
                                if (end_unreachable) {
                                        bool after_goto_case = goto_flow_analysis && s is GotoCase;
 
-                                       for (++startIndex; startIndex < statements.Count; ++startIndex) {
-                                               s = statements[startIndex];
-                                               if (s is SwitchLabel) {
-                                                       if (!after_goto_case)
-                                                               s.FlowAnalysis (fc);
+                                       var f = s as TryFinally;
+                                       if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
+                                               //
+                                               // Special case for try-finally with unreachable code after
+                                               // finally block. Try block has to include leave opcode but there is
+                                               // no label to leave to after unreachable finally block closing
+                                               // brace. This sentinel ensures there is always IL instruction to
+                                               // leave to even if we know it'll never be reached.
+                                               //
+                                               statements.Insert (startIndex + 1, new SentinelStatement ());
+                                       } else {
+                                               for (++startIndex; startIndex < statements.Count; ++startIndex) {
+                                                       s = statements [startIndex];
+                                                       if (s is SwitchLabel) {
+                                                               if (!after_goto_case)
+                                                                       s.FlowAnalysis (fc);
 
-                                                       break;
-                                               }
+                                                               break;
+                                                       }
 
-                                               if (s.IsUnreachable) {
-                                                       s.FlowAnalysis (fc);
-                                                       statements [startIndex] = RewriteUnreachableStatement (s);
+                                                       if (s.IsUnreachable) {
+                                                               s.FlowAnalysis (fc);
+                                                               statements [startIndex] = RewriteUnreachableStatement (s);
+                                                       }
                                                }
                                        }
 
@@ -3043,7 +3045,7 @@ namespace Mono.CSharp {
                        // L:
                        //      v = 1;
 
-                       if (s is BlockVariable || s is EmptyStatement)
+                       if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
                                return s;
 
                        return new EmptyStatement (s.loc);
@@ -8210,4 +8212,23 @@ namespace Mono.CSharp {
                        return visitor.Visit (this);
                }
        }
+
+       class SentinelStatement: Statement
+       {
+               protected override void CloneTo (CloneContext clonectx, Statement target)
+               {
+               }
+
+               protected override void DoEmit (EmitContext ec)
+               {
+                       var l = ec.DefineLabel ();
+                       ec.MarkLabel (l);
+                       ec.Emit (OpCodes.Br_S, l);
+               }
+
+               protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+               {
+                       throw new NotImplementedException ();
+               }
+       }
 }
diff --git a/mcs/tests/test-930.cs b/mcs/tests/test-930.cs
new file mode 100644 (file)
index 0000000..daf7b2a
--- /dev/null
@@ -0,0 +1,63 @@
+using System;
+
+class X
+{
+       public static int Main ()
+       {
+               try {
+                       Test1 ();
+                       return 1;
+               } catch (ApplicationException) {
+               }
+
+               try {
+                       Test2 ();
+                       return 2;
+               } catch (ApplicationException) {
+               }
+
+               try {
+                       Test3 ();
+                       return 3;
+               } catch (ApplicationException) {
+               }
+
+               return 0;
+       }
+
+       static void Test1 ()
+       {
+               try
+               {
+               }
+               finally
+               {
+                       throw new ApplicationException ();
+               }
+       }
+
+       static void Test2 ()
+       {
+               try
+               {
+               }
+               catch
+               {
+               }
+               finally
+               {
+                       throw new ApplicationException ();
+               }
+       }
+
+       static void Test3 ()
+       {
+               try
+               {
+                       throw new ApplicationException ();
+               }
+               finally
+               {
+               }
+       }
+}
\ No newline at end of file
index 261a258e1b0ce0b8e5e8b48e5bb7363f79e55fef..b56a09422e550b974ee2f5f34930c23ebb56c00e 100644 (file)
   <test name="test-505.cs">
     <type name="T">
       <method name="Int32 f()" attrs="145">
-        <size>20</size>
+        <size>22</size>
       </method>
       <method name="Void Main()" attrs="150">
         <size>37</size>
       </method>
     </type>
   </test>
+  <test name="test-930.cs">
+    <type name="X">
+      <method name="Int32 Main()" attrs="150">
+        <size>73</size>
+      </method>
+      <method name="Void Test1()" attrs="145">
+        <size>17</size>
+      </method>
+      <method name="Void Test2()" attrs="145">
+        <size>25</size>
+      </method>
+      <method name="Void Test3()" attrs="145">
+        <size>11</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="test-94.cs">
     <type name="Base">
       <method name="Int32 IVehicle.Start()" attrs="481">
     </type>
     <type name="Tester+&lt;TestException_4&gt;c__async3">
       <method name="Void MoveNext()" attrs="486">
-        <size>239</size>
+        <size>236</size>
       </method>
     </type>
     <type name="Tester+&lt;TestException_5&gt;c__async4">