Merge pull request #2720 from mono/fix-39325
[mono.git] / mcs / mcs / statement.cs
index 1d431c4b7580805193a4105b2dc296c7780ffdfa..9c84398ee3e4d52c01e8a3d25fd83adff75e96e3 100644 (file)
@@ -1222,7 +1222,7 @@ namespace Mono.CSharp {
                                                //
                                                // The return type is actually Task<T> type argument
                                                //
-                                               if (expr.Type == async_type) {
+                                               if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
                                                        ec.Report.Error (4016, loc,
                                                                "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
                                                                ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
@@ -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);
                                                }
 
@@ -1408,6 +1404,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 +1424,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 +1442,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 +1479,6 @@ namespace Mono.CSharp {
                string name;
                bool defined;
                bool referenced;
-               bool finalTarget;
                Label label;
                Block block;
                
@@ -1525,9 +1525,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 +1546,7 @@ namespace Mono.CSharp {
                        return rc;
                }
 
-               public void AddGotoReference (Reachability rc, bool finalTarget)
+               public void AddGotoReference (Reachability rc)
                {
                        if (referenced)
                                return;
@@ -1557,17 +1554,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);
                }
 
@@ -2456,6 +2442,9 @@ namespace Mono.CSharp {
                        get {
                                return (flags & Flags.FixedVariable) != 0;
                        }
+                       set {
+                               flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
+                       }
                }
 
                bool INamedBlockVariable.IsParameter {
@@ -2989,18 +2978,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);
+                                                       }
                                                }
                                        }
 
@@ -3040,7 +3041,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);
@@ -3114,6 +3115,7 @@ namespace Mono.CSharp {
        public class ExplicitBlock : Block
        {
                protected AnonymousMethodStorey am_storey;
+               int debug_scope_index;
 
                public ExplicitBlock (Block parent, Location start, Location end)
                        : this (parent, (Flags) 0, start, end)
@@ -3221,8 +3223,10 @@ namespace Mono.CSharp {
 
                public override void Emit (EmitContext ec)
                {
-                       if (Parent != null)
-                               ec.BeginScope ();
+                       if (Parent != null) {
+                               // TODO: It's needed only when scope has variable (normal or lifted)
+                               ec.BeginScope (GetDebugSymbolScopeIndex ());
+                       }
 
                        EmitScopeInitialization (ec);
 
@@ -3367,7 +3371,7 @@ namespace Mono.CSharp {
                                                                var parent_this_block = pb;
                                                                while (parent_this_block.Parent != null) {
                                                                        parent_this_block = parent_this_block.Parent.ParametersBlock;
-                                                                       if (parent_this_block.StateMachine != null) {
+                                                                       if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
                                                                                break;
                                                                        }
                                                                }
@@ -3424,6 +3428,14 @@ namespace Mono.CSharp {
                        storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
                }
 
+               public int GetDebugSymbolScopeIndex ()
+               {
+                       if (debug_scope_index == 0)
+                               debug_scope_index = ++ParametersBlock.debug_scope_index;
+
+                       return debug_scope_index;
+               }
+
                public void RegisterAsyncAwait ()
                {
                        var block = this;
@@ -3876,8 +3888,10 @@ namespace Mono.CSharp {
                        return new ParameterReference (parameter_info[index], loc);
                }
 
-               public Statement PerformClone ()
+               public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
                {
+                       undeclaredVariables = TopBlock.GetUndeclaredVariables ();
+
                        CloneContext clonectx = new CloneContext ();
                        return Clone (clonectx);
                }
@@ -4391,6 +4405,63 @@ namespace Mono.CSharp {
                        base.CheckControlExit (fc, dat);
                }
 
+               public HashSet<LocalVariable> GetUndeclaredVariables ()
+               {
+                       if (names == null)
+                               return null;
+
+                       HashSet<LocalVariable> variables = null;
+
+                       foreach (var entry in names) {
+                               var complex = entry.Value as List<INamedBlockVariable>;
+                               if (complex != null) {
+                                       foreach (var centry in complex) {
+                                               if (IsUndeclaredVariable (centry)) {
+                                                       if (variables == null)
+                                                               variables = new HashSet<LocalVariable> ();
+
+                                                       variables.Add ((LocalVariable) centry);
+                                               }
+                                       }
+                               } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
+                                       if (variables == null)
+                                               variables = new HashSet<LocalVariable> ();
+
+                                       variables.Add ((LocalVariable)entry.Value);                                     
+                               }
+                       }
+
+                       return variables;
+               }
+
+               static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
+               {
+                       var lv = namedBlockVariable as LocalVariable;
+                       return lv != null && !lv.IsDeclared;
+               }
+
+               public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
+               {
+                       if (names == null)
+                               return;
+                       
+                       foreach (var entry in names) {
+                               var complex = entry.Value as List<INamedBlockVariable>;
+                               if (complex != null) {
+                                       foreach (var centry in complex) {
+                                               var lv = centry as LocalVariable;
+                                               if (lv != null && undeclaredVariables.Contains (lv)) {
+                                                       lv.Type = null;
+                                               }
+                                       }
+                               } else {
+                                       var lv = entry.Value as LocalVariable;
+                                       if (lv != null && undeclaredVariables.Contains (lv))
+                                               lv.Type = null;
+                               }
+                       }
+               }
+
                public override void Emit (EmitContext ec)
                {
                        if (Report.Errors > 0)
@@ -6248,9 +6319,9 @@ namespace Mono.CSharp {
                        }
                }
 
-               class ExpressionEmitter : Emitter {
-                       public ExpressionEmitter (Expression converted, LocalVariable li) :
-                               base (converted, li)
+               sealed class ExpressionEmitter : Emitter {
+                       public ExpressionEmitter (Expression converted, LocalVariable li)
+                               base (converted, li)
                        {
                        }
 
@@ -6290,6 +6361,7 @@ namespace Mono.CSharp {
                                        LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
                                        vi.Location);
                                pinned_string.Type = rc.BuiltinTypes.String;
+                               vi.IsFixed = false;
 
                                eclass = ExprClass.Variable;
                                type = rc.BuiltinTypes.Int;
@@ -6348,8 +6420,9 @@ namespace Mono.CSharp {
                                //
                                // Case 1: Array
                                //
-                               if (res.Type.IsArray) {
-                                       TypeSpec array_type = TypeManager.GetElementType (res.Type);
+                               var ac = res.Type as ArrayContainer;
+                               if (ac != null) {
+                                       TypeSpec array_type = ac.Element;
 
                                        //
                                        // Provided that array_type is unmanaged,
@@ -6357,6 +6430,15 @@ namespace Mono.CSharp {
                                        if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
                                                return null;
 
+                                       Expression res_init;
+                                       if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
+                                               res_init = res;
+                                       } else {
+                                               var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
+                                               res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
+                                               res = expr_variable.CreateReferenceExpression (bc, loc);
+                                       }
+
                                        //
                                        // and T* is implicitly convertible to the
                                        // pointer type given in the fixed statement.
@@ -6371,7 +6453,7 @@ namespace Mono.CSharp {
                                        // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
                                        //
                                        converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
-                                               new Binary (Binary.Operator.Equality, res, new NullLiteral (loc)),
+                                               new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
                                                new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
                                                        new NullLiteral (loc),
                                                        converted, loc);
@@ -7711,7 +7793,11 @@ namespace Mono.CSharp {
 
                                for_each.variable.Type = var_type;
 
+                               var prev_block = ec.CurrentBlock;
+                               ec.CurrentBlock = variables_block;
                                var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
+                               ec.CurrentBlock = prev_block;
+
                                if (variable_ref == null)
                                        return false;
 
@@ -8000,7 +8086,10 @@ namespace Mono.CSharp {
                                                return false;
                                }
 
+                               var prev_block = ec.CurrentBlock;
+                               ec.CurrentBlock = for_each.variable.Block;
                                var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
+                               ec.CurrentBlock = prev_block;
                                if (variable_ref == null)
                                        return false;
 
@@ -8158,7 +8247,7 @@ namespace Mono.CSharp {
                        ec.LoopEnd = ec.DefineLabel ();
 
                        if (!(Statement is Block))
-                               ec.BeginCompilerScope ();
+                               ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
 
                        variable.CreateBuilder (ec);
 
@@ -8196,4 +8285,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 ();
+               }
+       }
 }