[mcs] Add support for await in catch clause
authorMarek Safar <marek.safar@gmail.com>
Mon, 2 Jun 2014 12:53:12 +0000 (14:53 +0200)
committerMarek Safar <marek.safar@gmail.com>
Mon, 2 Jun 2014 12:53:12 +0000 (14:53 +0200)
mcs/errors/cs1985-2.cs [deleted file]
mcs/errors/cs1985.cs [deleted file]
mcs/errors/cs7094.cs [new file with mode: 0644]
mcs/mcs/async.cs
mcs/mcs/statement.cs
mcs/tests/test-async-63.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_5.xml

diff --git a/mcs/errors/cs1985-2.cs b/mcs/errors/cs1985-2.cs
deleted file mode 100644 (file)
index 788d0e7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-// CS1985: The `await' operator cannot be used in a catch clause
-// Line: 18
-
-using System;
-using System.Threading.Tasks;
-
-class X
-{
-       public static void Main ()
-       {
-       }
-
-       static async Task Test ()
-       {
-               int x = 4;
-               try {
-                       throw null;
-               } catch (NullReferenceException) if (await Foo ()) {
-                       return;
-               }
-       }
-
-       static Task<bool> Foo ()
-       {
-               throw new NotImplementedException ();
-       }
-}
\ No newline at end of file
diff --git a/mcs/errors/cs1985.cs b/mcs/errors/cs1985.cs
deleted file mode 100644 (file)
index 62d3642..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// CS1985: The `await' operator cannot be used in a catch clause
-// Line: 13
-
-using System;
-using System.Threading.Tasks;
-
-class C
-{
-       public async Task Test ()
-       {
-               try {
-               } catch {
-                       await Call ();
-               }
-       }
-       
-       static Task Call ()
-       {
-               return null;
-       }
-}
diff --git a/mcs/errors/cs7094.cs b/mcs/errors/cs7094.cs
new file mode 100644 (file)
index 0000000..93cd637
--- /dev/null
@@ -0,0 +1,15 @@
+// CS7094: The `await' operator cannot be used in the filter expression of a catch clause
+// Line: 12
+
+using System.Threading.Tasks;
+
+class Test
+{
+       async static Task M1 ()
+       {
+               try {
+               }
+               catch if (await Task.Factory.StartNew (() => false)) {
+               }
+       }
+}
\ No newline at end of file
index 7ed03d2bad47e4cdbaf8d118c1636b9b310d2756..0c18052f91b54f3559207496fedd28f04ee72da0 100644 (file)
@@ -244,7 +244,7 @@ namespace Mono.CSharp
                        var fe_awaiter = new FieldExpr (awaiter, loc);
                        fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
 
-                               Label skip_continuation = ec.DefineLabel ();
+                       Label skip_continuation = ec.DefineLabel ();
 
                        using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
                                //
@@ -324,10 +324,6 @@ namespace Mono.CSharp
                                return false;
                        }
 
-                       if (bc.HasSet (ResolveContext.Options.CatchScope)) {
-                               bc.Report.Error (1985, loc, "The `await' operator cannot be used in a catch clause");
-                       }
-
                        if (!base.Resolve (bc))
                                return false;
 
index cfcf334197132489b4a1ff7637f4022f46182267..07da0a80cbe35cb9f2609cf345b0c1e08ac00350 100644 (file)
@@ -6316,9 +6316,16 @@ namespace Mono.CSharp {
                        public override bool Resolve (BlockContext bc)
                        {
                                ctch.Filter = ctch.Filter.Resolve (bc);
-                               var c = ctch.Filter as Constant;
-                               if (c != null && !c.IsDefaultValue) {
-                                       bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
+
+                               if (ctch.Filter != null) {
+                                       if (ctch.Filter.ContainsEmitWithAwait ()) {
+                                               bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
+                                       }
+
+                                       var c = ctch.Filter as Constant;
+                                       if (c != null && !c.IsDefaultValue) {
+                                               bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
+                                       }
                                }
 
                                return true;
@@ -6403,7 +6410,8 @@ namespace Mono.CSharp {
                                }
                        }
 
-                       Block.Emit (ec);
+                       if (!Block.HasAwait)
+                               Block.Emit (ec);
                }
 
                void EmitCatchVariableStore (EmitContext ec)
@@ -6424,19 +6432,19 @@ namespace Mono.CSharp {
                        }
                }
 
-               public override bool Resolve (BlockContext ec)
+               public override bool Resolve (BlockContext bc)
                {
-                       using (ec.Set (ResolveContext.Options.CatchScope)) {
+                       using (bc.Set (ResolveContext.Options.CatchScope)) {
                                if (type_expr != null) {
-                                       type = type_expr.ResolveAsType (ec);
+                                       type = type_expr.ResolveAsType (bc);
                                        if (type == null)
                                                return false;
 
-                                       if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
-                                               ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
+                                       if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
+                                               bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
                                        } else if (li != null) {
                                                li.Type = type;
-                                               li.PrepareAssignmentAnalysis (ec);
+                                               li.PrepareAssignmentAnalysis (bc);
 
                                                // source variable is at the top of the stack
                                                Expression source = new EmptyExpression (li.Type);
@@ -6456,7 +6464,7 @@ namespace Mono.CSharp {
                                }
 
                                Block.SetCatchBlock ();
-                               return Block.Resolve (ec);
+                               return Block.Resolve (bc);
                        }
                }
 
@@ -6602,6 +6610,7 @@ namespace Mono.CSharp {
                public Block Block;
                List<Catch> clauses;
                readonly bool inside_try_finally;
+               List<Catch> catch_sm;
 
                public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
                        : base (l)
@@ -6646,6 +6655,13 @@ namespace Mono.CSharp {
 
                                ok &= c.Resolve (bc);
 
+                               if (c.Block.HasAwait) {
+                                       if (catch_sm == null)
+                                               catch_sm = new List<Catch> ();
+
+                                       catch_sm.Add (c);
+                               }
+
                                if (c.Filter != null)
                                        continue;
 
@@ -6702,11 +6718,49 @@ namespace Mono.CSharp {
 
                        Block.Emit (ec);
 
-                       foreach (Catch c in clauses)
+                       LocalBuilder state_variable = null;
+                       foreach (Catch c in clauses) {
                                c.Emit (ec);
 
+                               if (catch_sm != null) {
+                                       if (state_variable == null)
+                                               state_variable = ec.GetTemporaryLocal (ec.Module.Compiler.BuiltinTypes.Int);
+
+                                       var index = catch_sm.IndexOf (c);
+                                       if (index < 0)
+                                               continue;
+
+                                       ec.EmitInt (index + 1);
+                                       ec.Emit (OpCodes.Stloc, state_variable);
+                               }
+                       }
+
                        if (!inside_try_finally)
                                ec.EndExceptionBlock ();
+
+                       if (state_variable != null) {
+                               ec.Emit (OpCodes.Ldloc, state_variable);
+
+                               var labels = new Label [catch_sm.Count + 1];
+                               for (int i = 0; i < labels.Length; ++i) {
+                                       labels [i] = ec.DefineLabel ();
+                               }
+
+                               var end = ec.DefineLabel ();
+                               ec.Emit (OpCodes.Switch, labels);
+
+                               // 0 value is default label
+                               ec.MarkLabel (labels [0]);
+
+                               for (int i = 0; i < catch_sm.Count; ++i) {
+                                       ec.Emit (OpCodes.Br, end);
+
+                                       ec.MarkLabel (labels [i + 1]);
+                                       catch_sm [i].Block.Emit (ec);
+                               }
+
+                               ec.MarkLabel (end);
+                       }
                }
 
                protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
diff --git a/mcs/tests/test-async-63.cs b/mcs/tests/test-async-63.cs
new file mode 100644 (file)
index 0000000..bd3e5f0
--- /dev/null
@@ -0,0 +1,85 @@
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+       static int counter;
+       public static async Task TestSingleAwait (bool throwException)
+       {
+               try {
+                       if (throwException)
+                               throw new ApplicationException ();
+               } catch (ApplicationException ex) {
+                       Console.WriteLine ("x1a");
+                       ++counter;
+                       await Call ();
+                       Console.WriteLine ("x2a");
+                       ++counter;
+               } catch {
+                       throw;
+               }
+
+               Console.WriteLine ("end");
+       }
+       
+       public static async Task TestDoubleAwait (bool throwException)
+       {
+               try {
+                       if (throwException)
+                               throw new ApplicationException ();
+               } catch (ApplicationException ex) {
+                       Console.WriteLine ("x1a");
+                       ++counter;
+                       await Call ();
+                       Console.WriteLine ("x2a");
+                       ++counter;
+               } catch {
+                       Console.WriteLine ("x1b");
+                       counter += 4;
+                       await Call ();
+                       Console.WriteLine ("x2b");
+                       counter += 7;
+               }
+
+               Console.WriteLine ("end");
+       }
+
+       static Task Call ()
+       {
+               return Task.Factory.StartNew (() => false);
+       }
+
+       void HH ()
+       {
+               try {
+                               throw new ApplicationException ();
+               } catch {
+                       throw;
+               }
+       }
+
+       public static int Main ()
+       {
+               TestSingleAwait (true).Wait ();
+               Console.WriteLine (counter);
+               if (counter != 2)
+                       return 1;
+
+               TestSingleAwait (false).Wait ();
+               if (counter != 2)
+                       return 1;
+
+               counter = 0;
+
+               TestDoubleAwait (true).Wait ();
+               Console.WriteLine (counter);
+               if (counter != 2)
+                       return 3;
+
+               TestDoubleAwait (false).Wait ();
+               if (counter != 2)
+                       return 4;               
+
+               return 0;
+       }
+}
index e6ca47f73d763c3078d2279e31225edaff6bc50c..5b6cef1c6103e13a2cb5f97737c91980d7d9dccb 100644 (file)
       </method>\r
     </type>\r
   </test>\r
+  <test name="test-async-63.cs">\r
+    <type name="C">\r
+      <method name="System.Threading.Tasks.Task TestSingleAwait(Boolean)" attrs="150">\r
+        <size>41</size>\r
+      </method>\r
+      <method name="System.Threading.Tasks.Task TestDoubleAwait(Boolean)" attrs="150">\r
+        <size>41</size>\r
+      </method>\r
+      <method name="System.Threading.Tasks.Task Call()" attrs="145">\r
+        <size>48</size>\r
+      </method>\r
+      <method name="Void HH()" attrs="129">\r
+        <size>12</size>\r
+      </method>\r
+      <method name="Int32 Main()" attrs="150">\r
+        <size>152</size>\r
+      </method>\r
+      <method name="Boolean &lt;Call&gt;m__0()" attrs="145">\r
+        <size>9</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+    <type name="C+&lt;TestSingleAwait&gt;c__async0">\r
+      <method name="Void MoveNext()" attrs="486">\r
+        <size>274</size>\r
+      </method>\r
+      <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">\r
+        <size>13</size>\r
+      </method>\r
+    </type>\r
+    <type name="C+&lt;TestDoubleAwait&gt;c__async1">\r
+      <method name="Void MoveNext()" attrs="486">\r
+        <size>410</size>\r
+      </method>\r
+      <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">\r
+        <size>13</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
   <test name="test-cls-00.cs">\r
     <type name="CLSCLass_6">\r
       <method name="Void add_Disposed(Delegate)" attrs="2182">\r