[csharp] repl using statement fix + support for --fatal
[mono.git] / mcs / mcs / statement.cs
index dfae5d990ef4281b34cb853755043d2a8c283fcb..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 {
@@ -1203,7 +1211,7 @@ namespace Mono.CSharp {
 
                Expression initializer;
                protected FullNamedExpression type_expr;
-               LocalVariable li;
+               protected LocalVariable li;
                protected List<Declarator> declarators;
 
                public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
@@ -1274,64 +1282,66 @@ namespace Mono.CSharp {
 
                public override bool Resolve (BlockContext bc)
                {
-                       TypeSpec type = null;
-                       if (type_expr is VarExpr) {
-                               //
-                               // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
-                               // same name exists or as a keyword when no type was found
-                               // 
-                               var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
-                               if (texpr == null) {
-                                       if (RootContext.Version < LanguageVersion.V_3)
-                                               bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
-
-                                       if (li.IsFixed) {
-                                               bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
-                                               return false;
-                                       }
+                       if (li.Type == null) {
+                               TypeSpec type = null;
+                               if (type_expr is VarExpr) {
+                                       //
+                                       // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
+                                       // same name exists or as a keyword when no type was found
+                                       // 
+                                       var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
+                                       if (texpr == null) {
+                                               if (RootContext.Version < LanguageVersion.V_3)
+                                                       bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
+
+                                               if (li.IsFixed) {
+                                                       bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
+                                                       return false;
+                                               }
 
-                                       if (li.IsConstant) {
-                                               bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
-                                               return false;
-                                       }
+                                               if (li.IsConstant) {
+                                                       bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
+                                                       return false;
+                                               }
 
-                                       if (Initializer == null) {
-                                               bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
-                                               return false;
-                                       }
+                                               if (Initializer == null) {
+                                                       bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
+                                                       return false;
+                                               }
 
-                                       if (declarators != null) {
-                                               bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
-                                               declarators = null;
-                                       }
+                                               if (declarators != null) {
+                                                       bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
+                                                       declarators = null;
+                                               }
 
-                                       Initializer = Initializer.Resolve (bc);
-                                       if (Initializer != null) {
-                                               ((VarExpr) type_expr).InferType (bc, Initializer);
-                                               type = type_expr.Type;
+                                               Initializer = Initializer.Resolve (bc);
+                                               if (Initializer != null) {
+                                                       ((VarExpr) type_expr).InferType (bc, Initializer);
+                                                       type = type_expr.Type;
+                                               }
                                        }
                                }
-                       }
 
-                       if (type == null) {
-                               var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
-                               if (texpr == null)
-                                       return false;
+                               if (type == null) {
+                                       var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
+                                       if (texpr == null)
+                                               return false;
 
-                               type = texpr.Type;
+                                       type = texpr.Type;
 
-                               if (li.IsConstant && !type.IsConstantCompatible) {
-                                       Const.Error_InvalidConstantType (type, loc, bc.Report);
+                                       if (li.IsConstant && !type.IsConstantCompatible) {
+                                               Const.Error_InvalidConstantType (type, loc, bc.Report);
+                                       }
                                }
-                       }
 
-                       if (type.IsStatic)
-                               FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
+                               if (type.IsStatic)
+                                       FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
 
-                       if (type.IsPointer && !bc.IsUnsafe)
-                               Expression.UnsafeError (bc, loc);
+                               if (type.IsPointer && !bc.IsUnsafe)
+                                       Expression.UnsafeError (bc, loc);
 
-                       li.Type = type;
+                               li.Type = type;
+                       }
 
                        bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
                        if (eval_global) {
@@ -1347,7 +1357,7 @@ namespace Mono.CSharp {
 
                        if (declarators != null) {
                                foreach (var d in declarators) {
-                                       d.Variable.Type = type;
+                                       d.Variable.Type = li.Type;
                                        if (eval_global) {
                                                CreateEvaluatorVariable (bc, d.Variable);
                                        } else {
@@ -1393,14 +1403,16 @@ namespace Mono.CSharp {
                {
                        BlockVariableDeclaration t = (BlockVariableDeclaration) target;
 
-                       t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
+                       if (type_expr != null)
+                               t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
+
                        if (initializer != null)
                                t.initializer = initializer.Clone (clonectx);
 
                        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)));
                        }
                }
        }
@@ -1456,6 +1468,7 @@ namespace Mono.CSharp {
                        FixedVariable = 1 << 6,
                        UsingVariable = 1 << 7,
 //                     DefinitelyAssigned = 1 << 8,
+                       IsLocked = 1 << 9,
 
                        ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
                }
@@ -1495,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;
@@ -1534,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;
@@ -1625,6 +1652,10 @@ namespace Mono.CSharp {
 
                public void Emit (EmitContext ec)
                {
+                       // TODO: Need something better for temporary variables
+                       if ((flags & Flags.CompilerGenerated) != 0)
+                               CreateBuilder (ec);
+
                        ec.Emit (OpCodes.Ldloc, builder);
                }
 
@@ -1642,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)
@@ -1681,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>
@@ -2171,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);
@@ -2260,6 +2286,7 @@ namespace Mono.CSharp {
                        readonly ParametersBlock block;
                        readonly int index;
                        public VariableInfo VariableInfo;
+                       bool is_locked;
 
                        public ParameterInfo (ParametersBlock block, int index)
                        {
@@ -2281,6 +2308,15 @@ namespace Mono.CSharp {
                                }
                        }
 
+                       public bool IsLocked {
+                               get {
+                                       return is_locked;
+                               }
+                               set {
+                                       is_locked = value;
+                               }
+                       }
+
                        public Location Location {
                                get {
                                        return Parameter.Location;
@@ -2774,7 +2810,7 @@ namespace Mono.CSharp {
                        Block b = block;
                        if (variable != null) {
                                do {
-                                       if (variable.Block == b)
+                                       if (variable.Block == b.Original)
                                                return true;
 
                                        b = b.Parent;
@@ -2792,7 +2828,7 @@ namespace Mono.CSharp {
                                for (int i = 0; i < list.Count; ++i) {
                                        variable = list[i];
                                        do {
-                                               if (variable.Block == b)
+                                               if (variable.Block == b.Original)
                                                        return true;
 
                                                b = b.Parent;
@@ -2827,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;
                                }
                        }
@@ -3054,6 +3090,9 @@ namespace Mono.CSharp {
 
                public SwitchLabel Clone (CloneContext clonectx)
                {
+                       if (label == null)
+                               return this;
+
                        return new SwitchLabel (label.Clone (clonectx), loc);
                }
        }
@@ -3072,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));
@@ -4055,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)
@@ -4071,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)
@@ -4278,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;
@@ -4735,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);
@@ -4796,167 +4930,144 @@ namespace Mono.CSharp {
                }
        }
 
-       // FIXME: Why is it almost exact copy of Using ??
-       public class UsingTemporary : ExceptionStatement
+       public class Using : ExceptionStatement
        {
-               protected TemporaryVariableReference local_copy;
-               Expression expr;
-               protected Statement dispose_call;
-
-               public UsingTemporary (Expression expr, Statement stmt, Location loc)
-                       : base (stmt, loc)
+               public class VariableDeclaration : BlockVariableDeclaration
                {
-                       this.expr = expr;
-               }
+                       Statement dispose_call;
 
-               #region Properties
-               public Expression Expression {
-                       get {
-                               return expr;
+                       public VariableDeclaration (FullNamedExpression type, LocalVariable li)
+                               : base (type, li)
+                       {
                        }
-               }
 
-               #endregion
-
-               protected virtual bool DoResolve (BlockContext ec)
-               {
-                       expr = expr.Resolve (ec);
-                       if (expr == null)
-                               return false;
-
-                       if (expr.Type != TypeManager.idisposable_type && !expr.Type.ImplementsInterface (TypeManager.idisposable_type, false)) {
-                               if (TypeManager.IsNullableType (expr.Type)) {
-                                       // it's handled it a custom code bellow
-                               } else if (expr.Type == InternalType.Dynamic) {
-                                       expr = Convert.ImplicitConversionStandard (ec, expr, TypeManager.idisposable_type, loc);
-                               } else {
-                                       Using.Error_IsNotConvertibleToIDisposable (ec, expr.Type, expr.Location);
-                                       return false;
-                               }
+                       public VariableDeclaration (LocalVariable li, Location loc)
+                               : base (li)
+                       {
+                               this.loc = loc;
                        }
 
-                       var expr_type = expr.Type;
-
-                       local_copy = TemporaryVariableReference.Create (expr_type, ec.CurrentBlock.Parent, loc);
-                       local_copy.Resolve (ec);
-
-                       if (TypeManager.void_dispose_void == null) {
-                               TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
-                                       TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
+                       public VariableDeclaration (Expression expr)
+                               : base (null)
+                       {
+                               loc = expr.Location;
+                               Initializer = expr;
                        }
 
-                       var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
-                       dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
-                               new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
-                               local_copy;
-
-                       dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
-
-                       // Add conditional call when disposing possible null variable
-                       if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
-                               dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
-
-                       return dispose_call.Resolve (ec);
-               }
-
-               public override bool Resolve (BlockContext ec)
-               {
-                       bool ok = DoResolve (ec);
-
-                       ec.StartFlowBranching (this);
-
-                       ok &= stmt.Resolve (ec);
-
-                       ec.EndFlowBranching ();
-
-                       ok &= base.Resolve (ec);
-
-                       return ok;
-               }
-
-               protected override void EmitPreTryBody (EmitContext ec)
-               {
-                       //local_copy.Variable.
-                       local_copy.EmitAssign (ec, expr);
-               }
-
-               protected override void EmitTryBody (EmitContext ec)
-               {
-                       stmt.Emit (ec);
-               }
-
-               protected override void EmitFinallyBody (EmitContext ec)
-               {
-                       dispose_call.Emit (ec);
-               }
-
-               protected override void CloneTo (CloneContext clonectx, Statement t)
-               {
-                       UsingTemporary target = (UsingTemporary) t;
+                       #region Properties
 
-                       target.expr = expr.Clone (clonectx);
-                       target.stmt = stmt.Clone (clonectx);
-               }
-       }
+                       public bool IsNested { get; private set; }
 
-       public class Using : ExceptionStatement
-       {
-               public class VariableDeclaration : BlockVariableDeclaration
-               {
-                       public VariableDeclaration (FullNamedExpression type, LocalVariable li)
-                               : base (type, li)
-                       {
-                       }
+                       #endregion
 
-                       private VariableDeclaration (LocalVariable li, Location loc)
-                               : base (li)
+                       public void EmitDispose (EmitContext ec)
                        {
-                               this.loc = loc;
+                               dispose_call.Emit (ec);
                        }
 
                        public override bool Resolve (BlockContext bc)
                        {
-                               if (type_expr == null)
+                               if (IsNested)
                                        return true;
 
                                return base.Resolve (bc);
                        }
 
-                       protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
+                       public Expression ResolveExpression (BlockContext bc)
                        {
-                               Assign assign;
+                               var e = Initializer.Resolve (bc);
+                               if (e == null)
+                                       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)
+                       {
                                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 = TemporaryVariableReference.Create (TypeManager.idisposable_type, bc.CurrentBlock, loc);
-                                       assign = new SimpleAssign (var, initializer, loc);
-                                       assign.ResolveStatement (bc);
-                                       return assign;
+                                       var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
+                                       dispose_call = CreateDisposeCall (bc, var);
+                                       dispose_call.Resolve (bc);
+
+                                       return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
                                }
 
                                if (li == Variable) {
-                                       if (li.Type != TypeManager.idisposable_type && !li.Type.ImplementsInterface (TypeManager.idisposable_type, false)) {
-                                               Using.Error_IsNotConvertibleToIDisposable (bc, li.Type, type_expr.Location);
-                                               return null;
-                                       }
+                                       CheckIDiposableConversion (bc, li, initializer);
+                                       dispose_call = CreateDisposeCall (bc, li);
+                                       dispose_call.Resolve (bc);
                                }
 
                                return base.ResolveInitializer (bc, li, initializer);
                        }
 
+                       protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
+                       {
+                               var type = li.Type;
+
+                               if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
+                                       if (TypeManager.IsNullableType (type)) {
+                                               // it's handled in CreateDisposeCall
+                                               return;
+                                       }
+
+                                       bc.Report.SymbolRelatedToPreviousError (type);
+                                       var loc = type_expr == null ? initializer.Location : type_expr.Location;
+                                       bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
+                                               type.GetSignatureForError ());
+
+                                       return;
+                               }
+                       }
+
+                       protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
+                       {
+                               var lvr = lv.CreateReferenceExpression (bc, lv.Location);
+                               var type = lv.Type;
+                               var loc = lv.Location;
+
+                               if (TypeManager.void_dispose_void == null) {
+                                       TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
+                                               TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
+                               }
+
+                               var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
+                               dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
+                                       new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
+                                       lvr;
+
+                               Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
+
+                               // Add conditional call when disposing possible null variable
+                               if (!type.IsStruct || TypeManager.IsNullableType (type))
+                                       dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
+
+                               return dispose;
+                       }
+
                        public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
                        {
                                for (int i = declarators.Count - 1; i >= 0; --i) {
                                        var d = declarators [i];
                                        var vd = new VariableDeclaration (d.Variable, type_expr.Location);
                                        vd.Initializer = d.Initializer;
+                                       vd.IsNested = true;
+                                       vd.dispose_call = CreateDisposeCall (bc, d.Variable);
+                                       vd.dispose_call.Resolve (bc);
+
                                        stmt = new Using (vd, stmt, d.Variable.Location);
                                }
 
@@ -4973,8 +5084,20 @@ namespace Mono.CSharp {
                        this.decl = decl;
                }
 
+               public Using (Expression expr, Statement stmt, Location loc)
+                       : base (stmt, loc)
+               {
+                       this.decl = new VariableDeclaration (expr);
+               }
+
                #region Properties
 
+               public Expression Expression {
+                       get {
+                               return decl.Variable == null ? decl.Initializer : null;
+                       }
+               }
+
                public BlockVariableDeclaration Variables {
                        get {
                                return decl;
@@ -4983,13 +5106,6 @@ namespace Mono.CSharp {
 
                #endregion
 
-               static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, TypeSpec type, Location loc)
-               {
-                       ec.Report.SymbolRelatedToPreviousError (type);
-                       ec.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
-                               type.GetSignatureForError ());
-               }
-
                protected override void EmitPreTryBody (EmitContext ec)
                {
                        decl.Emit (ec);
@@ -5002,46 +5118,45 @@ namespace Mono.CSharp {
 
                protected override void EmitFinallyBody (EmitContext ec)
                {
-                       Label skip = ec.DefineLabel ();
-
-                       var var = ((Assign) decl.Initializer).Target;
-                       bool emit_null_check = !TypeManager.IsValueType (var.Type);
-                       if (emit_null_check) {
-                               var.Emit (ec);
-                               ec.Emit (OpCodes.Brfalse, skip);
-                       }
-
-                       Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
-
-                       if (emit_null_check)
-                               ec.MarkLabel (skip);
+                       decl.EmitDispose (ec);
                }
 
                public override bool Resolve (BlockContext ec)
                {
+                       VariableReference vr;
+                       bool vr_locked = false;
+
                        using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
-                               if (!decl.Resolve (ec))
-                                       return false;
+                               if (decl.Variable == null) {
+                                       vr = decl.ResolveExpression (ec) as VariableReference;
+                                       if (vr != null) {
+                                               vr_locked = vr.IsLockedByStatement;
+                                               vr.IsLockedByStatement = true;
+                                       }
+                               } else {
+                                       if (!decl.Resolve (ec))
+                                               return false;
+
+                                       if (decl.Declarators != null) {
+                                               stmt = decl.RewriteForDeclarators (ec, stmt);
+                                       }
 
-                               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;
 
-                       if (TypeManager.void_dispose_void == null) {
-                               TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
-                                       TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
-                       }
+                       base.Resolve (ec);
 
-                       return ok;
+                       return true;
                }
 
                protected override void CloneTo (CloneContext clonectx, Statement t)
@@ -5149,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);
 
@@ -5268,52 +5382,43 @@ namespace Mono.CSharp {
                                }
                        }
 
-                       class Dispose : UsingTemporary
+                       class RuntimeDispose : Using.VariableDeclaration
                        {
-                               LocalTemporary dispose;
+                               public RuntimeDispose (LocalVariable lv, Location loc)
+                                       : base (lv, loc)
+                               {
+                               }
 
-                               public Dispose (TemporaryVariableReference variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
-                                       : base (expr, statement, loc)
+                               protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
                                {
-                                       base.local_copy = variable;
-                                       this.dispose = dispose;
+                                       // Defered to runtime check
                                }
 
-                               protected override bool DoResolve (BlockContext ec)
+                               protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
                                {
                                        if (TypeManager.void_dispose_void == null) {
                                                TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
                                                        TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
                                        }
 
-                                       Expression dispose_var = (Expression) dispose ?? local_copy;
-
-                                       var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
-                                       dispose_mg.InstanceExpression = dispose_var;
-
-                                       dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
-
-                                       if (!dispose_var.Type.IsStruct)
-                                               dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
+                                       //
+                                       // Fabricates code like
+                                       //
+                                       // if ((temp = vr as IDisposable) != null) temp.Dispose ();
+                                       //
 
-                                       return dispose_call.Resolve (ec);
-                               }
+                                       var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
 
-                               protected override void EmitFinallyBody (EmitContext ec)
-                               {
-                                       Label call_dispose = ec.DefineLabel ();
-                                       if (dispose != null) {
-                                               local_copy.Emit (ec, false);
-                                               ec.Emit (OpCodes.Isinst, dispose.Type);
-                                               dispose.Store (ec);
-                                       }
+                                       var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
+                                               dispose_variable.CreateReferenceExpression (bc, loc),
+                                               new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
+                                               loc), new NullLiteral (loc), loc);
 
-                                       base.EmitFinallyBody (ec);
+                                       var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
+                                       dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
 
-                                       if (dispose != null) {
-                                               ec.MarkLabel (call_dispose);
-                                               dispose.Release (ec);
-                                       }
+                                       Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
+                                       return new If (idisaposable_test, dispose, loc);
                                }
                        }
 
@@ -5374,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) {
@@ -5397,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,
@@ -5449,8 +5561,12 @@ namespace Mono.CSharp {
                        public override bool Resolve (BlockContext ec)
                        {
                                bool is_dynamic = expr.Type == InternalType.Dynamic;
-                               if (is_dynamic)
+
+                               if (is_dynamic) {
                                        expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
+                               } else if (TypeManager.IsNullableType (expr.Type)) {
+                                       expr = new Nullable.UnwrapCall (expr).Resolve (ec);
+                               }
 
                                var get_enumerator_mg = ResolveGetEnumerator (ec);
                                if (get_enumerator_mg == null) {
@@ -5500,7 +5616,6 @@ namespace Mono.CSharp {
                                variable.Type = var_type.Type;
 
                                var init = new Invocation (get_enumerator_mg, null);
-                               init.Resolve (ec);
 
                                statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
                                        new Body (var_type.Type, variable, current_pe, statement, loc), loc);
@@ -5515,8 +5630,9 @@ namespace Mono.CSharp {
                                                //
                                                // Runtime Dispose check
                                                //
-                                               var tv = new LocalTemporary (TypeManager.idisposable_type);
-                                               statement = new Dispose (enumerator_variable, tv, init, statement, loc);
+                                               var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
+                                               vd.Initializer = init;
+                                               statement = new Using (vd, statement, loc);
                                        } else {
                                                //
                                                // No Dispose call needed
@@ -5528,7 +5644,9 @@ namespace Mono.CSharp {
                                        //
                                        // Static Dispose check
                                        //
-                                       statement = new Dispose (enumerator_variable, null, init, statement, loc);
+                                       var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
+                                       vd.Initializer = init;
+                                       statement = new Using (vd, statement, loc);
                                }
 
                                return statement.Resolve (ec);