[csharp] repl using statement fix + support for --fatal
[mono.git] / mcs / mcs / statement.cs
index e0a9a97764e5b89ff5fd812d268c4e4ff54263b7..a639157ca423b056545d341371a7e0bbb9c73f82 100644 (file)
 //
 
 using System;
-using System.Text;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Diagnostics;
 using System.Collections.Generic;
 
+#if STATIC
+using IKVM.Reflection.Emit;
+#else
+using System.Reflection.Emit;
+#endif
+
 namespace Mono.CSharp {
        
        public abstract class Statement {
@@ -1181,6 +1183,12 @@ namespace Mono.CSharp {
                                this.initializer = initializer;
                        }
 
+                       public Declarator (Declarator clone, Expression initializer)
+                       {
+                               this.li = clone.li;
+                               this.initializer = initializer;
+                       }
+
                        #region Properties
 
                        public LocalVariable Variable {
@@ -1404,7 +1412,7 @@ namespace Mono.CSharp {
                        if (declarators != null) {
                                t.declarators = null;
                                foreach (var d in declarators)
-                                       t.AddDeclarator (new Declarator (d.Variable, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
+                                       t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
                        }
                }
        }
@@ -1460,6 +1468,7 @@ namespace Mono.CSharp {
                        FixedVariable = 1 << 6,
                        UsingVariable = 1 << 7,
 //                     DefinitelyAssigned = 1 << 8,
+                       IsLocked = 1 << 9,
 
                        ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
                }
@@ -1499,6 +1508,11 @@ namespace Mono.CSharp {
 
                #region Properties
 
+               public bool AddressTaken {
+                       get { return (flags & Flags.AddressTaken) != 0; }
+                       set { flags |= Flags.AddressTaken; }
+               }
+
                public Block Block {
                        get {
                                return block;
@@ -1538,6 +1552,15 @@ namespace Mono.CSharp {
                        }
                }
 
+               public bool IsLocked {
+                       get {
+                               return (flags & Flags.IsLocked) != 0;
+                       }
+                       set {
+                               flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
+                       }
+               }
+
                public bool IsThis {
                        get {
                                return (flags & Flags.IsThis) != 0;
@@ -1650,6 +1673,20 @@ namespace Mono.CSharp {
                        ec.Emit (OpCodes.Ldloca, builder);
                }
 
+               public string GetReadOnlyContext ()
+               {
+                       switch (flags & Flags.ReadonlyMask) {
+                       case Flags.FixedVariable:
+                               return "fixed variable";
+                       case Flags.ForeachVariable:
+                               return "foreach iteration variable";
+                       case Flags.UsingVariable:
+                               return "using variable";
+                       }
+
+                       throw new InternalErrorException ("Variable is not readonly");
+               }
+
                public bool IsThisAssigned (BlockContext ec, Block block)
                {
                        if (VariableInfo == null)
@@ -1689,29 +1726,10 @@ namespace Mono.CSharp {
                        flags |= Flags.Used;
                }
 
-               public bool AddressTaken {
-                       get { return (flags & Flags.AddressTaken) != 0; }
-                       set { flags |= Flags.AddressTaken; }
-               }
-
                public override string ToString ()
                {
                        return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
                }
-
-               public string GetReadOnlyContext ()
-               {
-                       switch (flags & Flags.ReadonlyMask) {
-                       case Flags.FixedVariable:
-                               return "fixed variable";
-                       case Flags.ForeachVariable:
-                               return "foreach iteration variable";
-                       case Flags.UsingVariable:
-                               return "using variable";
-                       }
-
-                       throw new InternalErrorException ("Variable is not readonly");
-               }
        }
 
        /// <summary>
@@ -2179,7 +2197,7 @@ namespace Mono.CSharp {
                {
                        if (am_storey != null) {
                                DefineAnonymousStorey (ec);
-                               am_storey.EmitStoreyInstantiation (ec);
+                               am_storey.EmitStoreyInstantiation (ec, this);
                        }
 
                        bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
@@ -2268,6 +2286,7 @@ namespace Mono.CSharp {
                        readonly ParametersBlock block;
                        readonly int index;
                        public VariableInfo VariableInfo;
+                       bool is_locked;
 
                        public ParameterInfo (ParametersBlock block, int index)
                        {
@@ -2289,6 +2308,15 @@ namespace Mono.CSharp {
                                }
                        }
 
+                       public bool IsLocked {
+                               get {
+                                       return is_locked;
+                               }
+                               set {
+                                       is_locked = value;
+                               }
+                       }
+
                        public Location Location {
                                get {
                                        return Parameter.Location;
@@ -2835,21 +2863,21 @@ namespace Mono.CSharp {
                        var label = value as LabeledStatement;
                        Block b = block;
                        if (label != null) {
-                               if (label.Block == b)
+                               if (label.Block == b.Original)
                                        return label;
 
                                // TODO: Temporary workaround for the switch block implicit label block
-                               if (label.Block.IsCompilerGenerated && label.Block.Parent == b)
+                               if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
                                        return label;
                        } else {
                                List<LabeledStatement> list = (List<LabeledStatement>) value;
                                for (int i = 0; i < list.Count; ++i) {
                                        label = list[i];
-                                       if (label.Block == b)
+                                       if (label.Block == b.Original)
                                                return label;
 
                                        // TODO: Temporary workaround for the switch block implicit label block
-                                       if (label.Block.IsCompilerGenerated && label.Block.Parent == b)
+                                       if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
                                                return label;
                                }
                        }
@@ -3062,6 +3090,9 @@ namespace Mono.CSharp {
 
                public SwitchLabel Clone (CloneContext clonectx)
                {
+                       if (label == null)
+                               return this;
+
                        return new SwitchLabel (label.Clone (clonectx), loc);
                }
        }
@@ -3080,7 +3111,7 @@ namespace Mono.CSharp {
                {
                        var cloned_labels = new List<SwitchLabel> ();
 
-                       foreach (SwitchLabel sl in cloned_labels)
+                       foreach (SwitchLabel sl in Labels)
                                cloned_labels.Add (sl.Clone (clonectx));
                        
                        return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
@@ -4063,7 +4094,8 @@ namespace Mono.CSharp {
 
        public class Lock : ExceptionStatement {
                Expression expr;
-               TemporaryVariableReference temp;
+               TemporaryVariableReference expr_copy;
+               TemporaryVariableReference lock_taken;
                        
                public Lock (Expression expr, Statement stmt, Location loc)
                        : base (stmt, loc)
@@ -4079,47 +4111,132 @@ namespace Mono.CSharp {
 
                        if (!TypeManager.IsReferenceType (expr.Type)){
                                ec.Report.Error (185, loc,
-                                             "`{0}' is not a reference type as required by the lock statement",
-                                             TypeManager.CSharpName (expr.Type));
-                               return false;
+                                       "`{0}' is not a reference type as required by the lock statement",
+                                       expr.Type.GetSignatureForError ());
+                       }
+
+                       if (expr.Type.IsGenericParameter) {
+                               expr = Convert.ImplicitTypeParameterConversion (expr, TypeManager.object_type);
+                       }
+
+                       VariableReference lv = expr as VariableReference;
+                       bool locked;
+                       if (lv != null) {
+                               locked = lv.IsLockedByStatement;
+                               lv.IsLockedByStatement = true;
+                       } else {
+                               lv = null;
+                               locked = false;
                        }
 
                        ec.StartFlowBranching (this);
-                       bool ok = Statement.Resolve (ec);
+                       Statement.Resolve (ec);
                        ec.EndFlowBranching ();
 
-                       ok &= base.Resolve (ec);
+                       if (lv != null) {
+                               lv.IsLockedByStatement = locked;
+                       }
 
-                       temp = TemporaryVariableReference.Create (expr.Type, ec.CurrentBlock.Parent, loc);
-                       temp.Resolve (ec);
+                       base.Resolve (ec);
 
-                       if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
-                               TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
-                               TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
-                                       monitor_type, "Enter", loc, TypeManager.object_type);
-                               TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
-                                       monitor_type, "Exit", loc, TypeManager.object_type);
+                       //
+                       // Have to keep original lock value around to unlock same location
+                       // in the case the original has changed or is null
+                       //
+                       expr_copy = TemporaryVariableReference.Create (TypeManager.object_type, ec.CurrentBlock.Parent, loc);
+                       expr_copy.Resolve (ec);
+
+                       //
+                       // Ensure Monitor methods are available
+                       //
+                       if (ResolvePredefinedMethods (ec) > 1) {
+                               lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
+                               lock_taken.Resolve (ec);
                        }
-                       
-                       return ok;
+
+                       return true;
                }
                
                protected override void EmitPreTryBody (EmitContext ec)
                {
-                       temp.EmitAssign (ec, expr);
-                       temp.Emit (ec);
-                       ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+                       expr_copy.EmitAssign (ec, expr);
+
+                       if (lock_taken != null) {
+                               //
+                               // Initialize ref variable
+                               //
+                               lock_taken.EmitAssign (ec, new BoolLiteral (false, loc));
+                       } else {
+                               //
+                               // Monitor.Enter (expr_copy)
+                               //
+                               expr_copy.Emit (ec);
+                               ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+                       }
                }
 
                protected override void EmitTryBody (EmitContext ec)
                {
+                       //
+                       // Monitor.Enter (expr_copy, ref lock_taken)
+                       //
+                       if (lock_taken != null) {
+                               expr_copy.Emit (ec);
+                               lock_taken.LocalInfo.CreateBuilder (ec);
+                               lock_taken.AddressOf (ec, AddressOp.Load);
+                               ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+                       }
+
                        Statement.Emit (ec);
                }
 
                protected override void EmitFinallyBody (EmitContext ec)
                {
-                       temp.Emit (ec);
+                       //
+                       // if (lock_taken) Monitor.Exit (expr_copy)
+                       //
+                       Label skip = ec.DefineLabel ();
+
+                       if (lock_taken != null) {
+                               lock_taken.Emit (ec);
+                               ec.Emit (OpCodes.Brfalse_S, skip);
+                       }
+
+                       expr_copy.Emit (ec);
                        ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
+                       ec.MarkLabel (skip);
+               }
+
+               int ResolvePredefinedMethods (ResolveContext rc)
+               {
+                       if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
+                               TypeSpec monitor_type = rc.Module.PredefinedTypes.Monitor.Resolve (loc);
+
+                               if (monitor_type == null)
+                                       return 0;
+
+                               // Try 4.0 Monitor.Enter (object, ref bool) overload first
+                               var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
+                                       new[] {
+                                                       new ParameterData (null, Parameter.Modifier.NONE),
+                                                       new ParameterData (null, Parameter.Modifier.REF)
+                                               },
+                                       new[] {
+                                                       TypeManager.object_type,
+                                                       TypeManager.bool_type
+                                               }, false), null);
+
+                               TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
+                               if (TypeManager.void_monitor_enter_object == null) {
+                                       TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
+                                               monitor_type, "Enter", loc, TypeManager.object_type);
+                               }
+
+                               TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
+                                       monitor_type, "Exit", loc, TypeManager.object_type);
+                       }
+
+                       return TypeManager.void_monitor_enter_object.Parameters.Count;
                }
 
                protected override void CloneTo (CloneContext clonectx, Statement t)
@@ -4286,8 +4403,11 @@ namespace Mono.CSharp {
                                pinned_string.Type = TypeManager.string_type;
 
                                if (TypeManager.int_get_offset_to_string_data == null) {
-                                       TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
-                                               TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
+                                       var helper = rc.Module.PredefinedTypes.RuntimeHelpers.Resolve (loc);
+                                       if (helper != null) {
+                                               TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (helper,
+                                                       "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
+                                       }
                                }
 
                                eclass = ExprClass.Variable;
@@ -4743,12 +4863,18 @@ namespace Mono.CSharp {
                        }
 
                        if (General != null) {
-                               if (CodeGen.Assembly.WrapNonExceptionThrows) {
-                                       foreach (Catch c in Specific){
-                                               if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
-                                                       ec.Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
-                                               }
-                                       }
+                               foreach (Catch c in Specific) {
+                                       if (c.CatchType != TypeManager.exception_type)
+                                               continue;
+
+                                       if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
+                                               continue;
+
+                                       if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
+                                               continue;
+
+                                       ec.Report.Warning (1058, 1, c.loc,
+                                               "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
                                }
 
                                ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
@@ -4847,36 +4973,36 @@ namespace Mono.CSharp {
                                return base.Resolve (bc);
                        }
 
-                       public void ResolveExpression (BlockContext bc)
+                       public Expression ResolveExpression (BlockContext bc)
                        {
                                var e = Initializer.Resolve (bc);
                                if (e == null)
-                                       return;
+                                       return null;
 
                                li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
                                Initializer = ResolveInitializer (bc, Variable, e);
+                               return e;
                        }
 
                        protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
                        {
-                               Assign assign;
                                if (li.Type == InternalType.Dynamic) {
                                        initializer = initializer.Resolve (bc);
                                        if (initializer == null)
                                                return null;
 
-                                       initializer = Convert.ImplicitConversionRequired (bc, initializer, TypeManager.idisposable_type, loc);
+                                       // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
+                                       Arguments args = new Arguments (1);
+                                       args.Add (new Argument (initializer));
+                                       initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
                                        if (initializer == null)
                                                return null;
 
                                        var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
-                                       assign = new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc);
-                                       assign.ResolveStatement (bc);
-
                                        dispose_call = CreateDisposeCall (bc, var);
                                        dispose_call.Resolve (bc);
 
-                                       return assign;
+                                       return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
                                }
 
                                if (li == Variable) {
@@ -4997,9 +5123,16 @@ namespace Mono.CSharp {
 
                public override bool Resolve (BlockContext ec)
                {
+                       VariableReference vr;
+                       bool vr_locked = false;
+
                        using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
                                if (decl.Variable == null) {
-                                       decl.ResolveExpression (ec);
+                                       vr = decl.ResolveExpression (ec) as VariableReference;
+                                       if (vr != null) {
+                                               vr_locked = vr.IsLockedByStatement;
+                                               vr.IsLockedByStatement = true;
+                                       }
                                } else {
                                        if (!decl.Resolve (ec))
                                                return false;
@@ -5007,18 +5140,23 @@ namespace Mono.CSharp {
                                        if (decl.Declarators != null) {
                                                stmt = decl.RewriteForDeclarators (ec, stmt);
                                        }
+
+                                       vr = null;
                                }
                        }
 
                        ec.StartFlowBranching (this);
 
-                       bool ok = stmt.Resolve (ec);
+                       stmt.Resolve (ec);
 
                        ec.EndFlowBranching ();
 
-                       ok &= base.Resolve (ec);
+                       if (vr != null)
+                               vr.IsLockedByStatement = vr_locked;
 
-                       return ok;
+                       base.Resolve (ec);
+
+                       return true;
                }
 
                protected override void CloneTo (CloneContext clonectx, Statement t)
@@ -5126,7 +5264,6 @@ namespace Mono.CSharp {
                                ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
                                ec.CurrentBranching.CreateSibling ();
 
-                               // TODO: dynamic
                                variable.local_info.Type = conv.Type;
                                variable.Resolve (ec);
 
@@ -5342,7 +5479,8 @@ namespace Mono.CSharp {
                                // Option 2: Try to match using IEnumerable interfaces with preference of generic version
                                //
                                TypeSpec iface_candidate = null;
-                               for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
+                               var t = expr.Type;
+                               do {
                                        var ifaces = t.Interfaces;
                                        if (ifaces != null) {
                                                foreach (var iface in ifaces) {
@@ -5365,7 +5503,13 @@ namespace Mono.CSharp {
                                                        }
                                                }
                                        }
-                               }
+
+                                       if (t.IsGenericParameter)
+                                               t = t.BaseType;
+                                       else
+                                               t = null;
+
+                               } while (t != null);
 
                                if (iface_candidate == null) {
                                        rc.Report.Error (1579, loc,
@@ -5417,10 +5561,11 @@ namespace Mono.CSharp {
                        public override bool Resolve (BlockContext ec)
                        {
                                bool is_dynamic = expr.Type == InternalType.Dynamic;
+
                                if (is_dynamic) {
                                        expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
                                } else if (TypeManager.IsNullableType (expr.Type)) {
-                                       expr = Nullable.Unwrap.Create (expr);
+                                       expr = new Nullable.UnwrapCall (expr).Resolve (ec);
                                }
 
                                var get_enumerator_mg = ResolveGetEnumerator (ec);