Merge pull request #2543 from ermshiperete/Xamarin-31021
[mono.git] / mcs / mcs / async.cs
index 6cd75bc9af503de909d7d9c17c0a90e6a05e5cf6..b3efae3241024e39237558fbf7b0634d32f8a761 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;
@@ -145,7 +150,7 @@ namespace Mono.CSharp
 
                        public bool ProbingMode { get; set; }
 
-                       protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
+                       public override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
                        {
                                Error_OperatorCannotBeApplied (rc, type);
                        }
@@ -159,7 +164,7 @@ namespace Mono.CSharp
                                if (invocation != null && invocation.MethodGroup != null && (invocation.MethodGroup.BestCandidate.Modifiers & Modifiers.ASYNC) != 0) {
                                        rc.Report.Error (4008, loc, "Cannot await void method `{0}'. Consider changing method return type to `Task'",
                                                invocation.GetSignatureForError ());
-                               } else {
+                               } else if (type != InternalType.ErrorType) {
                                        rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
                                }
                        }
@@ -344,7 +349,11 @@ namespace Mono.CSharp
 
                        var errors_printer = new SessionReportPrinter ();
                        var old = bc.Report.SetPrinter (errors_printer);
-                       ama = new Invocation (ama, args).Resolve (bc);
+
+                       //
+                       // The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult(). 
+                       //
+                       ama = new Invocation (new ParenthesizedExpression (ama, Location.Null), args).Resolve (bc);
                        bc.Report.SetPrinter (old);
 
                        if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) {
@@ -421,8 +430,6 @@ namespace Mono.CSharp
        public class AsyncInitializer : StateMachineInitializer
        {
                TypeInferenceContext return_inference;
-               List<Label> redirected_jumps;
-               FieldExpr HoistedReturnState;
 
                public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType)
                        : base (block, host, returnType)
@@ -441,6 +448,10 @@ namespace Mono.CSharp
                        get; set;
                }
 
+               public StackFieldExpr HoistedReturnState {
+                       get; set;
+               }
+
                public override bool IsIterator {
                        get {
                                return false;
@@ -472,6 +483,24 @@ namespace Mono.CSharp
                        throw new NotImplementedException ();
                }
 
+               public void EmitCatchBlock (EmitContext ec)
+               {
+                       var catch_value = LocalVariable.CreateCompilerGenerated (ec.Module.Compiler.BuiltinTypes.Exception, block, Location);
+
+                       ec.BeginCatchBlock (catch_value.Type);
+                       catch_value.EmitAssign (ec);
+
+                       ec.EmitThis ();
+                       ec.EmitInt ((int) IteratorStorey.State.After);
+                       ec.Emit (OpCodes.Stfld, storey.PC.Spec);
+
+                       ((AsyncTaskStorey) Storey).EmitSetException (ec, new LocalVariableReference (catch_value, Location));
+
+                       ec.Emit (OpCodes.Leave, move_next_ok);
+                       ec.EndExceptionBlock ();
+
+               }
+
                protected override void EmitMoveNextEpilogue (EmitContext ec)
                {
                        var storey = (AsyncTaskStorey) Storey;
@@ -485,57 +514,6 @@ namespace Mono.CSharp
                        ec.Emit (OpCodes.Ret);
                }
 
-               //
-               // Emits state table of jumps outside of try block and reload of return
-               // value when try block returns value
-               //
-               public void EmitRedirectedJumpsTable (EmitContext ec, StackFieldExpr returnResult, Label localReturn)
-               {
-                       if (redirected_jumps == null)
-                               return;
-
-                       int ret_index = redirected_jumps.IndexOf (localReturn);
-                       if (ret_index >= 0) {
-                               redirected_jumps [ret_index] = ec.DefineLabel ();
-                       }
-
-                       HoistedReturnState.Emit (ec);
-                       ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
-
-                       if (ret_index >= 0) {
-                               ec.MarkLabel (redirected_jumps [ret_index]);
-                               var s = (AsyncTaskStorey)storey;
-                               ((IAssignMethod)s.HoistedReturnValue).EmitAssign (ec, returnResult, false, false);
-
-                               ec.Emit (OpCodes.Leave, BodyEnd);
-                       }
-
-                       // Mark fallthrough label
-                       ec.MarkLabel (redirected_jumps [0]);
-               }
-
-               public void EmitRedirectedJump (EmitContext ec, Label label)
-               {
-                       if (redirected_jumps == null) {
-                               redirected_jumps = new List<Label> ();
-
-                               // Add fallthrough label
-                               redirected_jumps.Add (ec.DefineLabel ());
-                               HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int);
-                       }
-
-                       int index = redirected_jumps.IndexOf (label);
-                       if (index < 0) {
-                               redirected_jumps.Add (label);
-                               index = redirected_jumps.Count - 1;
-                       }
-
-                       //
-                       // Indicates we have return value captured
-                       //
-                       HoistedReturnState.EmitAssign (ec, new IntConstant (HoistedReturnState.Type, index, Location.Null), false, false);
-               }
-
                public override void MarkReachable (Reachability rc)
                {
                        //
@@ -567,6 +545,8 @@ namespace Mono.CSharp
 
                #region Properties
 
+               public bool HasAwaitInsideFinally { get; set; }
+
                public Expression HoistedReturnValue { get; set; }
 
                public TypeSpec ReturnType {
@@ -616,7 +596,7 @@ namespace Mono.CSharp
                        return field;
                }
 
-               public Field AddCapturedLocalVariable (TypeSpec type)
+               public Field AddCapturedLocalVariable (TypeSpec type, bool requiresUninitialized = false)
                {
                        if (mutator != null)
                                type = mutator.Mutate (type);
@@ -624,7 +604,7 @@ namespace Mono.CSharp
                        List<Field> existing_fields = null;
                        if (stack_fields == null) {
                                stack_fields = new Dictionary<TypeSpec, List<Field>> ();
-                       } else if (stack_fields.TryGetValue (type, out existing_fields)) {
+                       } else if (stack_fields.TryGetValue (type, out existing_fields) && !requiresUninitialized) {
                                foreach (var f in existing_fields) {
                                        if (f.IsAvailableForReuse) {
                                                f.IsAvailableForReuse = false;
@@ -720,6 +700,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"),
@@ -757,7 +749,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;
@@ -850,7 +848,7 @@ namespace Mono.CSharp
                        args.Add (new Argument (awaiter, Argument.AType.Ref));
                        args.Add (new Argument (new CompilerGeneratedThis (CurrentType, Location), Argument.AType.Ref));
                        using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
-                               mg.EmitCall (ec, args);
+                               mg.EmitCall (ec, args, true);
                        }
                }
 
@@ -928,7 +926,7 @@ namespace Mono.CSharp
                        args.Add (new Argument (exceptionVariable));
 
                        using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
-                               mg.EmitCall (ec, args);
+                               mg.EmitCall (ec, args, true);
                        }
                }
 
@@ -952,7 +950,7 @@ namespace Mono.CSharp
                        }
 
                        using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
-                               mg.EmitCall (ec, args);
+                               mg.EmitCall (ec, args, true);
                        }
                }