[mcs] All returns with values from async block with finally need to be captured....
authorMarek Safar <marek.safar@gmail.com>
Sat, 5 Mar 2016 10:28:36 +0000 (11:28 +0100)
committerMarek Safar <marek.safar@gmail.com>
Sat, 5 Mar 2016 10:31:11 +0000 (11:31 +0100)
mcs/mcs/async.cs
mcs/mcs/statement.cs
mcs/tests/test-async-82.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_x.xml

index 46cac378e67921acedb2cfaaff9d407be4f24df3..22e4c1b4b8c3efd233b937c9de66f7ad722b6ca8 100644 (file)
@@ -90,6 +90,11 @@ namespace Mono.CSharp
                        if (!stmt.Resolve (bc))
                                return null;
 
+                       if (rc.HasSet (ResolveContext.Options.FinallyScope) && rc.CurrentAnonymousMethod != null) {
+                               var ats = (AsyncTaskStorey)rc.CurrentAnonymousMethod.Storey;
+                               ats.HasAwaitInsideFinally = true;
+                       }
+
                        type = stmt.ResultType;
                        eclass = ExprClass.Variable;
                        return this;
@@ -536,6 +541,8 @@ namespace Mono.CSharp
 
                #region Properties
 
+               public bool HasAwaitInsideFinally { get; set; }
+
                public Expression HoistedReturnValue { get; set; }
 
                public TypeSpec ReturnType {
@@ -689,6 +696,18 @@ namespace Mono.CSharp
 
                        builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location));
 
+                       Field rfield;
+                       if (has_task_return_type && HasAwaitInsideFinally) {
+                               //
+                               // Special case async block with return value from finally clause. In such case
+                               // we rewrite all return expresison stores to stfld to $return. Instead of treating
+                               // returns outside of finally and inside of finally differently.
+                               //
+                               rfield = AddCompilerGeneratedField ("$return", new TypeExpression (bt.TypeArguments [0], Location));
+                       } else {
+                               rfield = null;
+                       }
+
                        var set_state_machine = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Void, Location),
                                Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN | Modifiers.PUBLIC,
                                new MemberName ("SetStateMachine"),
@@ -726,7 +745,13 @@ namespace Mono.CSharp
                        set_state_machine.Block.AddStatement (new StatementExpression (new Invocation (mg, args)));
 
                        if (has_task_return_type) {
-                               HoistedReturnValue = TemporaryVariableReference.Create (bt.TypeArguments [0], StateMachineMethod.Block, Location);
+                               if (rfield != null) {
+                                       HoistedReturnValue = new FieldExpr (rfield, Location) {
+                                               InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null)
+                                       };
+                               } else {
+                                       HoistedReturnValue = TemporaryVariableReference.Create (bt.TypeArguments [0], StateMachineMethod.Block, Location);
+                               }
                        }
 
                        return true;
index c768ff1cd9ed5cf91da43cf60f81f5dbf0342bbb..9c84398ee3e4d52c01e8a3d25fd83adff75e96e3 100644 (file)
@@ -1291,10 +1291,6 @@ namespace Mono.CSharp {
                                                // Special case hoisted return value (happens in try/finally scenario)
                                                //
                                                if (ec.TryFinallyUnwind != null) {
-                                                       if (storey.HoistedReturnValue is VariableReference) {
-                                                               storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type);
-                                                       }
-
                                                        exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
                                                }
 
diff --git a/mcs/tests/test-async-82.cs b/mcs/tests/test-async-82.cs
new file mode 100644 (file)
index 0000000..ba5025b
--- /dev/null
@@ -0,0 +1,30 @@
+using System;
+using System.Threading.Tasks;
+
+class X
+{
+       public static int Main ()
+       {
+               if (new X ().Test (false).Result != true)
+                       return 1;
+
+               if (new X ().Test (true).Result != true)
+                       return 2;
+
+               return 0;
+       }
+
+       public async Task<bool> Test(bool TrueOrFalse)
+       {
+               if (TrueOrFalse)
+                       return true;
+
+               try {
+                       return true;
+               }
+               finally
+               {
+                       await Task.Yield ();
+               }
+       }
+}
\ No newline at end of file
index c54b3c39b082d3b1bb960e4a14d9196cdef33a8d..702908fa939bc229550ee0ca428253144160d534 100644 (file)
       </method>
     </type>
   </test>
+  <test name="test-async-82.cs">
+    <type name="X">
+      <method name="Int32 Main()" attrs="150">
+        <size>66</size>
+      </method>
+      <method name="System.Threading.Tasks.Task`1[System.Boolean] Test(Boolean)" attrs="134">
+        <size>41</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="X+&lt;Test&gt;c__async0">
+      <method name="Void MoveNext()" attrs="486">
+        <size>269</size>
+      </method>
+      <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">
+        <size>13</size>
+      </method>
+    </type>
+  </test>
   <test name="test-cls-00.cs">
     <type name="CLSCLass_6">
       <method name="Void add_Disposed(Delegate)" attrs="2182">