X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fstatement.cs;h=871a798571ae873aa2f5c1cb7aea2160ea00bedf;hb=3fbd4b873c76d01f900a10a6e02dd6d38c376914;hp=d20f13fa411693e41d2149e41d3cb4f47588740d;hpb=c0a8ef12af1f473bfc837325581fe738f1b3178c;p=mono.git diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index d20f13fa411..871a798571a 100644 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -28,7 +28,7 @@ namespace Mono.CSharp { /// /// Resolves the statement, true means that all sub-statements /// did resolve ok. - // + /// public virtual bool Resolve (BlockContext bc) { return true; @@ -49,13 +49,22 @@ namespace Mono.CSharp { // in unreachable code, for instance. // - if (warn) + bool unreachable = false; + if (warn && !ec.UnreachableReported) { + ec.UnreachableReported = true; + unreachable = true; ec.Report.Warning (162, 2, loc, "Unreachable code detected"); + } ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc); + ec.CurrentBranching.CurrentUsageVector.Goto (); bool ok = Resolve (ec); ec.KillFlowBranching (); + if (unreachable) { + ec.UnreachableReported = false; + } + return ok; } @@ -403,8 +412,9 @@ namespace Mono.CSharp { return false; empty = true; return true; - } else - infinite = true; + } + + infinite = true; } ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc); @@ -539,8 +549,9 @@ namespace Mono.CSharp { return false; empty = true; return true; - } else - infinite = true; + } + + infinite = true; } } else infinite = true; @@ -653,7 +664,7 @@ namespace Mono.CSharp { public StatementExpression (ExpressionStatement expr) { this.expr = expr; - loc = expr.Location; + loc = expr.StartLocation; } public StatementExpression (ExpressionStatement expr, Location loc) @@ -693,11 +704,12 @@ namespace Mono.CSharp { public class StatementErrorExpression : Statement { - readonly Expression expr; + Expression expr; public StatementErrorExpression (Expression expr) { this.expr = expr; + this.loc = expr.StartLocation; } public Expression Expr { @@ -706,6 +718,12 @@ namespace Mono.CSharp { } } + public override bool Resolve (BlockContext bc) + { + expr.Error_InvalidExpressionStatement (bc); + return true; + } + protected override void DoEmit (EmitContext ec) { throw new NotSupportedException (); @@ -713,7 +731,9 @@ namespace Mono.CSharp { protected override void CloneTo (CloneContext clonectx, Statement target) { - throw new NotImplementedException (); + var t = (StatementErrorExpression) target; + + t.expr = expr.Clone (clonectx); } public override object Accept (StructuralVisitor visitor) @@ -731,7 +751,7 @@ namespace Mono.CSharp { public StatementList (Statement first, Statement second) { - statements = new List () { first, second }; + statements = new List { first, second }; } #region Properties @@ -845,7 +865,7 @@ namespace Mono.CSharp { if (ec.CurrentIterator != null) { Error_ReturnFromIterator (ec); - } else { + } else if (ec.ReturnType != InternalType.ErrorType) { ec.Report.Error (126, loc, "An object of a type convertible to `{0}' is required for the return statement", ec.ReturnType.GetSignatureForError ()); @@ -895,9 +915,18 @@ namespace Mono.CSharp { if (this is ContextualReturn) return true; - ec.Report.Error (1997, loc, - "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task' return type", - ec.GetSignatureForError ()); + // Same error code as .NET but better error message + if (async_block.DelegateType != null) { + ec.Report.Error (1997, loc, + "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task' return type", + async_block.DelegateType.GetSignatureForError ()); + } else { + ec.Report.Error (1997, loc, + "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task' return type", + ec.GetSignatureForError ()); + + } + return false; } @@ -923,9 +952,19 @@ namespace Mono.CSharp { } var l = am as AnonymousMethodBody; - if (l != null && l.ReturnTypeInference != null && expr != null) { - l.ReturnTypeInference.AddCommonTypeBound (expr.Type); - return true; + if (l != null && expr != null) { + if (l.ReturnTypeInference != null) { + l.ReturnTypeInference.AddCommonTypeBound (expr.Type); + return true; + } + + // + // Try to optimize simple lambda. Only when optimizations are enabled not to cause + // unexpected debugging experience + // + if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) { + l.DirectMethodGroupConversion = expr.CanReduceLambda (l); + } } } } @@ -1163,17 +1202,14 @@ namespace Mono.CSharp { return false; } - if (!ec.Switch.GotDefault) { - FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report); - return false; - } + ec.Switch.RegisterGotoCase (null, null); return true; } protected override void DoEmit (EmitContext ec) { - ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel); + ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec)); } public override object Accept (StructuralVisitor visitor) @@ -1187,7 +1223,6 @@ namespace Mono.CSharp { /// public class GotoCase : Statement { Expression expr; - SwitchLabel sl; public GotoCase (Expression e, Location l) { @@ -1200,6 +1235,8 @@ namespace Mono.CSharp { return this.expr; } } + + public SwitchLabel Label { get; set; } public override bool Resolve (BlockContext ec) { @@ -1210,13 +1247,8 @@ namespace Mono.CSharp { ec.CurrentBranching.CurrentUsageVector.Goto (); - expr = expr.Resolve (ec); - if (expr == null) - return false; - - Constant c = expr as Constant; + Constant c = expr.ResolveLabelConstant (ec); if (c == null) { - ec.Report.Error (150, expr.Location, "A constant value is expected"); return false; } @@ -1225,7 +1257,7 @@ namespace Mono.CSharp { res = c; } else { TypeSpec type = ec.Switch.SwitchType; - res = c.TryReduce (ec, type); + res = c.Reduce (ec, type); if (res == null) { c.Error_ValueCannotBeConverted (ec, type, true); return false; @@ -1234,17 +1266,17 @@ namespace Mono.CSharp { if (!Convert.ImplicitStandardConversionExists (c, type)) ec.Report.Warning (469, 2, loc, "The `goto case' value is not implicitly convertible to type `{0}'", - TypeManager.CSharpName (type)); + type.GetSignatureForError ()); } - sl = ec.Switch.ResolveGotoCase (ec, res); + ec.Switch.RegisterGotoCase (this, res); return true; } protected override void DoEmit (EmitContext ec) { - ec.Emit (OpCodes.Br, sl.GetILLabel (ec)); + ec.Emit (OpCodes.Br, Label.GetILLabel (ec)); } protected override void CloneTo (CloneContext clonectx, Statement t) @@ -1402,69 +1434,72 @@ namespace Mono.CSharp { Location Location { get; } } - public class BlockVariableDeclaration : Statement + public class BlockVariableDeclarator { - public class Declarator + LocalVariable li; + Expression initializer; + + public BlockVariableDeclarator (LocalVariable li, Expression initializer) { - LocalVariable li; - Expression initializer; + if (li.Type != null) + throw new ArgumentException ("Expected null variable type"); - public Declarator (LocalVariable li, Expression initializer) - { - if (li.Type != null) - throw new ArgumentException ("Expected null variable type"); + this.li = li; + this.initializer = initializer; + } - this.li = li; - this.initializer = initializer; - } + #region Properties - public Declarator (Declarator clone, Expression initializer) - { - this.li = clone.li; - this.initializer = initializer; + public LocalVariable Variable { + get { + return li; } + } - #region Properties - - public LocalVariable Variable { - get { - return li; - } + public Expression Initializer { + get { + return initializer; } - - public Expression Initializer { - get { - return initializer; - } - set { - initializer = value; - } + set { + initializer = value; } + } - #endregion + #endregion + + public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx) + { + var t = (BlockVariableDeclarator) MemberwiseClone (); + if (initializer != null) + t.initializer = initializer.Clone (cloneCtx); + + return t; } + } + public class BlockVariable : Statement + { Expression initializer; protected FullNamedExpression type_expr; protected LocalVariable li; - protected List declarators; + protected List declarators; TypeSpec type; - public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li) + public BlockVariable (FullNamedExpression type, LocalVariable li) { this.type_expr = type; this.li = li; this.loc = type_expr.Location; } - protected BlockVariableDeclaration (LocalVariable li) + protected BlockVariable (LocalVariable li) { this.li = li; } #region Properties - public List Declarators { + public List Declarators { get { return declarators; } @@ -1493,10 +1528,10 @@ namespace Mono.CSharp { #endregion - public void AddDeclarator (Declarator decl) + public void AddDeclarator (BlockVariableDeclarator decl) { if (declarators == null) - declarators = new List (); + declarators = new List (); declarators.Add (decl); } @@ -1589,7 +1624,7 @@ namespace Mono.CSharp { bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock; if (eval_global) { CreateEvaluatorVariable (bc, li); - } else { + } else if (type != InternalType.ErrorType) { li.PrepareForFlowAnalysis (bc); } @@ -1603,7 +1638,7 @@ namespace Mono.CSharp { d.Variable.Type = li.Type; if (eval_global) { CreateEvaluatorVariable (bc, d.Variable); - } else { + } else if (type != InternalType.ErrorType) { d.Variable.PrepareForFlowAnalysis (bc); } @@ -1643,7 +1678,7 @@ namespace Mono.CSharp { protected override void CloneTo (CloneContext clonectx, Statement target) { - BlockVariableDeclaration t = (BlockVariableDeclaration) target; + BlockVariable t = (BlockVariable) target; if (type_expr != null) t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx); @@ -1654,7 +1689,7 @@ namespace Mono.CSharp { if (declarators != null) { t.declarators = null; foreach (var d in declarators) - t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx))); + t.AddDeclarator (d.Clone (clonectx)); } } @@ -1664,9 +1699,9 @@ namespace Mono.CSharp { } } - public class BlockConstantDeclaration : BlockVariableDeclaration + public class BlockConstant : BlockVariable { - public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li) + public BlockConstant (FullNamedExpression type, LocalVariable li) : base (type, li) { } @@ -2041,7 +2076,8 @@ namespace Mono.CSharp { HasAsyncModifier = 1 << 10, Resolved = 1 << 11, YieldBlock = 1 << 12, - AwaitBlock = 1 << 13 + AwaitBlock = 1 << 13, + Iterator = 1 << 14 } public Block Parent; @@ -2073,9 +2109,7 @@ namespace Mono.CSharp { #endif // int assignable_slots; - bool unreachable_shown; - bool unreachable; - + public Block (Block parent, Location start, Location end) : this (parent, 0, start, end) { @@ -2140,14 +2174,6 @@ namespace Mono.CSharp { #endregion - public Block CreateSwitchBlock (Location start) - { - // FIXME: Only explicit block should be created - var new_block = new Block (this, start, start); - new_block.IsCompilerGenerated = true; - return new_block; - } - public void SetEndLocation (Location loc) { EndLocation = loc; @@ -2221,6 +2247,11 @@ namespace Mono.CSharp { scope_initializers.Add (s); } } + + public void InsertStatement (int index, Statement s) + { + statements.Insert (index, s); + } public void AddStatement (Statement s) { @@ -2247,6 +2278,8 @@ namespace Mono.CSharp { Block prev_block = ec.CurrentBlock; bool ok = true; + bool unreachable = ec.IsUnreachable; + bool prev_unreachable = unreachable; ec.CurrentBlock = this; ec.StartFlowBranching (this); @@ -2279,14 +2312,10 @@ namespace Mono.CSharp { if (s is EmptyStatement) continue; - if (!unreachable_shown && !(s is LabeledStatement)) { + if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) { ec.Report.Warning (162, 2, s.loc, "Unreachable code detected"); - unreachable_shown = true; + ec.UnreachableReported = true; } - - Block c_block = s as Block; - if (c_block != null) - c_block.unreachable = c_block.unreachable_shown = true; } // @@ -2299,19 +2328,25 @@ namespace Mono.CSharp { if (!s.Resolve (ec)) { ok = false; - if (ec.IsInProbingMode) - break; + if (!ec.IsInProbingMode) + statements [ix] = new EmptyStatement (s.loc); - statements [ix] = new EmptyStatement (s.loc); continue; } - if (unreachable && !(s is LabeledStatement) && !(s is Block)) + if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block)) statements [ix] = new EmptyStatement (s.loc); unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable; - if (unreachable && s is LabeledStatement) - throw new InternalErrorException ("should not happen"); + if (unreachable) { + ec.IsUnreachable = true; + } else if (ec.IsUnreachable) + ec.IsUnreachable = false; + } + + if (unreachable != prev_unreachable) { + ec.IsUnreachable = prev_unreachable; + ec.UnreachableReported = false; } while (ec.CurrentBranching is FlowBranchingLabeled) @@ -2335,17 +2370,21 @@ namespace Mono.CSharp { public override bool ResolveUnreachable (BlockContext ec, bool warn) { - unreachable_shown = true; - unreachable = true; - - if (warn) + bool unreachable = false; + if (warn && !ec.UnreachableReported) { + ec.UnreachableReported = true; + unreachable = true; ec.Report.Warning (162, 2, loc, "Unreachable code detected"); + } var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc); fb.CurrentUsageVector.IsUnreachable = true; bool ok = Resolve (ec); ec.KillFlowBranching (); + if (unreachable) + ec.UnreachableReported = false; + return ok; } @@ -2544,13 +2583,16 @@ namespace Mono.CSharp { } if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) { - storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis; - break; + if (storey.HoistedThis == null) + storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis; + + if (storey.HoistedThis != null) + break; } } - + // - // We are the first storey on path and this has to be hoisted + // We are the first storey on path and 'this' has to be hoisted // if (storey.HoistedThis == null) { foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) { @@ -2566,11 +2608,34 @@ namespace Mono.CSharp { if (block_on_path == null) continue; - if (storey.HoistedThis == null) - storey.AddCapturedThisField (ec); + if (storey.HoistedThis == null) { + storey.AddCapturedThisField (ec, null); + } for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) { + ParametersBlock pb; + if (b.AnonymousMethodStorey != null) { + // + // Don't add storey cross reference for `this' when the storey ends up not + // beeing attached to any parent + // + if (b.ParametersBlock.StateMachine == null) { + AnonymousMethodStorey s = null; + for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) { + s = ab.Explicit.AnonymousMethodStorey; + if (s != null) + break; + } + + // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost + if (s == null) { + var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey; + b.AnonymousMethodStorey.AddCapturedThisField (ec, parent); + break; + } + } + b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey); b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis; @@ -2583,14 +2648,32 @@ namespace Mono.CSharp { b = b.ParametersBlock; } - var pb = b as ParametersBlock; + pb = b as ParametersBlock; if (pb != null && pb.StateMachine != null) { if (pb.StateMachine == storey) break; + // + // If we are state machine with no parent. We can hook into parent without additional + // reference and capture this directly + // + ExplicitBlock parent_storey_block = pb; + while (parent_storey_block.Parent != null) { + parent_storey_block = parent_storey_block.Parent.Explicit; + if (parent_storey_block.AnonymousMethodStorey != null) { + break; + } + } + + if (parent_storey_block.AnonymousMethodStorey == null) { + pb.StateMachine.AddCapturedThisField (ec, null); + b.HasCapturedThis = true; + continue; + } + pb.StateMachine.AddParentStoreyReference (ec, storey); } - + b.HasCapturedVariable = true; } } @@ -2627,6 +2710,7 @@ namespace Mono.CSharp { } storey.Define (); + storey.PrepareEmit (); storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey); } @@ -2645,6 +2729,8 @@ namespace Mono.CSharp { public void RegisterIteratorYield () { + ParametersBlock.TopBlock.IsIterator = true; + var block = this; while ((block.flags & Flags.YieldBlock) == 0) { block.flags |= Flags.YieldBlock; @@ -2925,7 +3011,7 @@ namespace Mono.CSharp { public override Expression CreateExpressionTree (ResolveContext ec) { if (statements.Count == 1) { - Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec); + Expression expr = statements[0].CreateExpressionTree (ec); if (scope_initializers != null) expr = new BlockScopeExpression (expr, this); @@ -3016,7 +3102,7 @@ namespace Mono.CSharp { unreachable = top_level.End (); } } catch (Exception e) { - if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException) + if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException || rc.Report.Printer is NullReportPrinter) throw; if (rc.CurrentBlock != null) { @@ -3093,7 +3179,7 @@ namespace Mono.CSharp { return tlb; } - public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc) + public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc) { for (int i = 0; i < parameters.Count; i++) { Parameter p = parameters[i]; @@ -3125,6 +3211,7 @@ namespace Mono.CSharp { var block_type = host.Module.Compiler.BuiltinTypes.Void; var initializer = new AsyncInitializer (this, host, block_type); initializer.Type = block_type; + initializer.DelegateType = delegateType; var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType); @@ -3188,7 +3275,10 @@ namespace Mono.CSharp { public bool IsIterator { get { - return HasYield; + return (flags & Flags.Iterator) != 0; + } + set { + flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator; } } @@ -3241,18 +3331,19 @@ namespace Mono.CSharp { // // A collision checking between local names // + var variable_block = li.Block.Explicit; for (int i = 0; i < existing_list.Count; ++i) { existing = existing_list[i]; Block b = existing.Block.Explicit; // Collision at same level - if (li.Block.Explicit == b) { + if (variable_block == b) { li.Block.Error_AlreadyDeclared (name, li); break; } // Collision with parent - Block parent = li.Block.Explicit; + Block parent = variable_block; while ((parent = parent.Parent) != null) { if (parent == b) { li.Block.Error_AlreadyDeclared (name, li, "parent or current"); @@ -3261,10 +3352,10 @@ namespace Mono.CSharp { } } - if (!ignoreChildrenBlocks) { + if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) { // Collision with children while ((b = b.Parent) != null) { - if (li.Block.Explicit == b) { + if (variable_block == b) { li.Block.Error_AlreadyDeclared (name, li, "child"); i = existing_list.Count; break; @@ -3359,8 +3450,23 @@ namespace Mono.CSharp { int count = parameters.Count; Arguments args = new Arguments (count); for (int i = 0; i < count; ++i) { - var arg_expr = GetParameterReference (i, parameter_info[i].Location); - args.Add (new Argument (arg_expr)); + var pi = parameter_info[i]; + var arg_expr = GetParameterReference (i, pi.Location); + + Argument.AType atype_modifier; + switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) { + case Parameter.Modifier.REF: + atype_modifier = Argument.AType.Ref; + break; + case Parameter.Modifier.OUT: + atype_modifier = Argument.AType.Out; + break; + default: + atype_modifier = 0; + break; + } + + args.Add (new Argument (arg_expr, atype_modifier)); } return args; @@ -3441,20 +3547,12 @@ namespace Mono.CSharp { if (label != null) { 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.Original) - return label; } else { List list = (List) value; for (int i = 0; i < list.Count; ++i) { label = list[i]; 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.Original) - return label; } } @@ -3488,9 +3586,7 @@ namespace Mono.CSharp { if (Report.Errors > 0) return; -#if PRODUCTION try { -#endif if (IsCompilerGenerated) { using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { base.Emit (ec); @@ -3524,22 +3620,16 @@ namespace Mono.CSharp { ec.Emit (OpCodes.Ret); } -#if PRODUCTION - } catch (Exception e){ - Console.WriteLine ("Exception caught by the compiler while emitting:"); - Console.WriteLine (" Block that caused the problem begin at: " + block.loc); - - Console.WriteLine (e.GetType ().FullName + ": " + e.Message); - throw; + } catch (Exception e) { + throw new InternalErrorException (e, StartLocation); } -#endif } } - public class SwitchLabel { + public class SwitchLabel : Statement + { Expression label; Constant converted; - readonly Location loc; Label? il_label; @@ -3579,6 +3669,8 @@ namespace Mono.CSharp { } } + public bool SectionStart { get; set; } + public Label GetILLabel (EmitContext ec) { if (il_label == null){ @@ -3588,33 +3680,44 @@ namespace Mono.CSharp { return il_label.Value; } + protected override void DoEmit (EmitContext ec) + { + ec.MarkLabel (GetILLabel (ec)); + } + + public override bool Resolve (BlockContext bc) + { + if (ResolveAndReduce (bc)) + bc.Switch.RegisterLabel (bc, this); + + bc.CurrentBranching.CurrentUsageVector.ResetBarrier (); + + return base.Resolve (bc); + } + // // Resolves the expression, reduces it to a literal if possible // and then converts it to the requested type. // - public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable) - { - Expression e = label.Resolve (ec); + bool ResolveAndReduce (ResolveContext rc) + { + if (IsDefault) + return true; - if (e == null) + var c = label.ResolveLabelConstant (rc); + if (c == null) return false; - Constant c = e as Constant; - if (c == null){ - ec.Report.Error (150, loc, "A constant value is expected"); - return false; - } - - if (allow_nullable && c is NullLiteral) { + if (rc.Switch.IsNullable && c is NullLiteral) { converted = c; return true; } - converted = c.ImplicitConversionRequired (ec, required_type, loc); + converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType); return converted != null; } - public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with) + public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with) { string label; if (converted == null) @@ -3626,36 +3729,19 @@ namespace Mono.CSharp { ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label); } - public SwitchLabel Clone (CloneContext clonectx) - { - if (label == null) - return this; - - return new SwitchLabel (label.Clone (clonectx), loc); - } - } - - public class SwitchSection { - public readonly List Labels; - public readonly Block Block; - - public SwitchSection (List labels, Block block) + protected override void CloneTo (CloneContext clonectx, Statement target) { - Labels = labels; - Block = block; + var t = (SwitchLabel) target; + if (label != null) + t.label = label.Clone (clonectx); } - public SwitchSection Clone (CloneContext clonectx) + public override object Accept (StructuralVisitor visitor) { - var cloned_labels = new List (); - - foreach (SwitchLabel sl in Labels) - cloned_labels.Add (sl.Clone (clonectx)); - - return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block)); + return visitor.Visit (this); } } - + public class Switch : Statement { // structure used to hold blocks of keys while calculating table switch @@ -3708,33 +3794,26 @@ namespace Mono.CSharp { } } - sealed class LabelMarker : Statement + sealed class DispatchStatement : Statement { - readonly Switch s; - readonly List labels; + readonly Switch body; - public LabelMarker (Switch s, List labels) + public DispatchStatement (Switch body) { - this.s = s; - this.labels = labels; + this.body = body; } protected override void CloneTo (CloneContext clonectx, Statement target) { + throw new NotImplementedException (); } protected override void DoEmit (EmitContext ec) { - foreach (var l in labels) { - if (l.IsDefault) - ec.MarkLabel (s.DefaultLabel); - else - ec.MarkLabel (l.GetILLabel (ec)); - } + body.EmitDispatch (ec); } } - public List Sections; public Expression Expr; // @@ -3742,25 +3821,21 @@ namespace Mono.CSharp { // Dictionary labels; Dictionary string_labels; + List case_labels; + + List> goto_cases; /// /// The governing switch type /// public TypeSpec SwitchType; - // - // Computed - // - Label default_target; - Label null_target; Expression new_expr; - bool is_constant; - SwitchSection constant_section; - SwitchSection default_section; - SwitchLabel null_section; + SwitchLabel case_null; + SwitchLabel case_default; - Statement simple_stmt; + Label defaultLabel, nullLabel; VariableReference value; ExpressionStatement string_dictionary; FieldExpr switch_cache_field; @@ -3771,11 +3846,10 @@ namespace Mono.CSharp { // Nullable.Unwrap unwrap; - public Switch (Expression e, ExplicitBlock block, List sects, Location l) + public Switch (Expression e, ExplicitBlock block, Location l) { Expr = e; this.block = block; - Sections = sects; loc = l; } @@ -3785,15 +3859,9 @@ namespace Mono.CSharp { } } - public Label DefaultLabel { + public SwitchLabel DefaultLabel { get { - return default_target; - } - } - - public bool GotDefault { - get { - return default_section != null; + return case_default; } } @@ -3876,62 +3944,40 @@ namespace Mono.CSharp { }; } - // - // Performs the basic sanity checks on the switch statement - // (looks for duplicate keys and non-constant expressions). - // - // It also returns a hashtable with the keys that we will later - // use to compute the switch tables - // - bool CheckSwitch (ResolveContext ec) + public void RegisterLabel (ResolveContext rc, SwitchLabel sl) { - bool error = false; - if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) - string_labels = new Dictionary (Sections.Count + 1); - else - labels = new Dictionary (Sections.Count + 1); - - foreach (SwitchSection ss in Sections){ - foreach (SwitchLabel sl in ss.Labels){ - if (sl.IsDefault){ - if (default_section != null){ - sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]); - error = true; - } - default_section = ss; - continue; - } + case_labels.Add (sl); - if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) { - error = true; - continue; - } - - try { - if (string_labels != null) { - string s = sl.Converted.GetValue () as string; - if (s == null) - null_section = sl; - else - string_labels.Add (s, sl); - } else { - if (sl.Converted is NullLiteral) { - null_section = sl; - } else { - labels.Add (sl.Converted.GetValueAsLong (), sl); - } - } - } catch (ArgumentException) { - if (string_labels != null) - sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]); - else - sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]); + if (sl.IsDefault) { + if (case_default != null) { + sl.Error_AlreadyOccurs (rc, case_default); + } else { + case_default = sl; + } - error = true; + return; + } + + try { + if (string_labels != null) { + string string_value = sl.Converted.GetValue () as string; + if (string_value == null) + case_null = sl; + else + string_labels.Add (string_value, sl); + } else { + if (sl.Converted is NullLiteral) { + case_null = sl; + } else { + labels.Add (sl.Converted.GetValueAsLong (), sl); } } + } catch (ArgumentException) { + if (string_labels != null) + sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]); + else + sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]); } - return !error; } // @@ -3943,8 +3989,6 @@ namespace Mono.CSharp { // void EmitTableSwitch (EmitContext ec, Expression val) { - Label lbl_default = default_target; - if (labels != null && labels.Count > 0) { List ranges; if (string_labels != null) { @@ -3977,17 +4021,21 @@ namespace Mono.CSharp { ranges.Sort (); } + Label lbl_default = defaultLabel; TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType; for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) { LabelsRange kb = ranges[range_index]; - lbl_default = (range_index == 0) ? default_target : ec.DefineLabel (); + lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel (); // Optimize small ranges using simple equality check if (kb.Range <= 2) { foreach (var key in kb.label_values) { SwitchLabel sl = labels[key]; - if (sl.Converted.IsDefaultValue) { + if (sl == case_default || sl == case_null) + continue; + + if (sl.Converted.IsZeroInteger) { val.EmitBranchable (ec, sl.GetILLabel (ec), false); } else { val.Emit (ec); @@ -4058,35 +4106,8 @@ namespace Mono.CSharp { if (ranges.Count > 0) ec.Emit (OpCodes.Br, lbl_default); } - - // now emit the code for the sections - bool found_default = false; - - foreach (SwitchSection ss in Sections) { - foreach (SwitchLabel sl in ss.Labels) { - if (sl.IsDefault) { - ec.MarkLabel (lbl_default); - found_default = true; - if (null_section == null) - ec.MarkLabel (null_target); - } else if (sl.Converted.IsNull) { - ec.MarkLabel (null_target); - } - - ec.MarkLabel (sl.GetILLabel (ec)); - } - - ss.Block.Emit (ec); - } - - if (!found_default) { - ec.MarkLabel (lbl_default); - if (null_section == null) { - ec.MarkLabel (null_target); - } - } } - + SwitchLabel FindLabel (Constant value) { SwitchLabel sl = null; @@ -4094,16 +4115,16 @@ namespace Mono.CSharp { if (string_labels != null) { string s = value.GetValue () as string; if (s == null) { - if (null_section != null) - sl = null_section; - else if (default_section != null) - sl = default_section.Labels[0]; + if (case_null != null) + sl = case_null; + else if (case_default != null) + sl = case_default; } else { string_labels.TryGetValue (s, out sl); } } else { if (value is NullLiteral) { - sl = null_section; + sl = case_null; } else { labels.TryGetValue (value.GetValueAsLong (), out sl); } @@ -4112,18 +4133,6 @@ namespace Mono.CSharp { return sl; } - SwitchSection FindSection (SwitchLabel label) - { - foreach (SwitchSection ss in Sections){ - foreach (SwitchLabel sl in ss.Labels){ - if (label == sl) - return ss; - } - } - - return null; - } - public override bool Resolve (BlockContext ec) { Expr = Expr.Resolve (ec); @@ -4140,10 +4149,13 @@ namespace Mono.CSharp { new_expr = SwitchGoverningType (ec, unwrap); } - if (new_expr == null){ - ec.Report.Error (151, loc, - "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type", - TypeManager.CSharpName (Expr.Type)); + if (new_expr == null) { + if (Expr.Type != InternalType.ErrorType) { + ec.Report.Error (151, loc, + "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type", + Expr.Type.GetSignatureForError ()); + } + return false; } @@ -4155,127 +4167,121 @@ namespace Mono.CSharp { return false; } - if (!CheckSwitch (ec)) - return false; + if (block.Statements.Count == 0) + return true; - Switch old_switch = ec.Switch; - ec.Switch = this; - ec.Switch.SwitchType = SwitchType; + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) { + string_labels = new Dictionary (); + } else { + labels = new Dictionary (); + } - ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc); + case_labels = new List (); var constant = new_expr as Constant; - if (constant != null) { - is_constant = true; - SwitchLabel label = FindLabel (constant); - if (label != null) - constant_section = FindSection (label); - if (constant_section == null) - constant_section = default_section; - } else { + // + // Don't need extra variable for constant switch or switch with + // only default case + // + if (constant == null) { // - // Store switch expression for comparission purposes + // Store switch expression for comparison purposes // value = new_expr as VariableReference; - if (value == null) + if (value == null && !HasOnlyDefaultSection ()) { + var current_block = ec.CurrentBlock; + ec.CurrentBlock = Block; + // Create temporary variable inside switch scope value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc); + value.Resolve (ec); + ec.CurrentBlock = current_block; + } } - bool first = true; - bool ok = true; - foreach (SwitchSection ss in Sections){ - if (!first) - ec.CurrentBranching.CreateSibling ( - null, FlowBranching.SiblingType.SwitchSection); - else - first = false; + Switch old_switch = ec.Switch; + ec.Switch = this; + ec.Switch.SwitchType = SwitchType; - if (is_constant && (ss != constant_section)) { - // If we're a constant switch, we're only emitting - // one single section - mark all the others as - // unreachable. - ec.CurrentBranching.CurrentUsageVector.Goto (); - if (!ss.Block.ResolveUnreachable (ec, true)) { - ok = false; - } - } else { - if (!ss.Block.Resolve (ec)) - ok = false; - } - } + ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc); + + ec.CurrentBranching.CurrentUsageVector.Goto (); + + var ok = block.Resolve (ec); - if (default_section == null) + if (case_default == null) ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection); - ec.EndFlowBranching (); + if (ec.IsUnreachable) + ec.KillFlowBranching (); + else + ec.EndFlowBranching (); + ec.Switch = old_switch; - if (!ok) - return false; + // + // Check if all goto cases are valid. Needs to be done after switch + // is resolved becuase goto can jump forward in the scope. + // + if (goto_cases != null) { + foreach (var gc in goto_cases) { + if (gc.Item1 == null) { + if (DefaultLabel == null) { + FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report); + } - if (!is_constant) { - if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) { - if (string_labels.Count < 7) - ResolveSimpleSwitch (ec); - else - ResolveStringSwitchMap (ec); - } else if (labels.Count < 3 && !IsNullable) { - ResolveSimpleSwitch (ec); + continue; + } + + var sl = FindLabel (gc.Item2); + if (sl == null) { + FlowBranchingBlock.Error_UnknownLabel (loc, "case " + gc.Item2.GetValueAsLiteral (), ec.Report); + } else { + gc.Item1.Label = sl; + } } } - return true; - } + if (constant != null) { + ResolveUnreachableSections (ec, constant); + } - public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value) - { - var sl = FindLabel (value); + if (!ok) + return false; - if (sl == null) { - FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report); + if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) { + ResolveStringSwitchMap (ec); } - return sl; + // + // Anonymous storey initialization has to happen before + // any generated switch dispatch + // + block.InsertStatement (0, new DispatchStatement (this)); + + return true; } - // - // Prepares switch using simple if/else comparison for small label count (4 + optional default) - // - void ResolveSimpleSwitch (BlockContext bc) + bool HasOnlyDefaultSection () { - simple_stmt = default_section != null ? default_section.Block : null; - - for (int i = Sections.Count - 1; i >= 0; --i) { - var s = Sections[i]; + for (int i = 0; i < block.Statements.Count; ++i) { + var s = block.Statements[i] as SwitchLabel; - if (s == default_section) { - s.Block.AddScopeStatement (new LabelMarker (this, s.Labels)); + if (s == null || s.IsDefault) continue; - } - s.Block.AddScopeStatement (new LabelMarker (this, s.Labels)); - - Expression cond = null; - for (int ci = 0; ci < s.Labels.Count; ++ci) { - var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted); + return false; + } - if (ci > 0) { - cond = new Binary (Binary.Operator.LogicalOr, cond, e); - } else { - cond = e; - } - } + return true; + } - // - // Compiler generated, hide from symbol file - // - simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null); - } + public void RegisterGotoCase (GotoCase gotoCase, Constant value) + { + if (goto_cases == null) + goto_cases = new List> (); - // It's null for empty switch - if (simple_stmt != null) - simple_stmt.Resolve (bc); + goto_cases.Add (Tuple.Create (gotoCase, value)); } // @@ -4305,37 +4311,28 @@ namespace Mono.CSharp { ctype.AddField (field); var init = new List (); - int counter = 0; + int counter = -1; labels = new Dictionary (string_labels.Count); string value = null; - foreach (SwitchSection section in Sections) { - bool contains_label = false; - foreach (SwitchLabel sl in section.Labels) { - if (sl.IsDefault || sl.Converted.IsNull) - continue; - if (!contains_label) { - labels.Add (counter, sl); - contains_label = true; - } + foreach (SwitchLabel sl in case_labels) { - value = (string) sl.Converted.GetValue (); - var init_args = new List (2); - init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location)); + if (sl.SectionStart) + labels.Add (++counter, sl); - sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc); - init_args.Add (sl.Converted); + if (sl == case_default || sl == case_null) + continue; - init.Add (new CollectionElementInitializer (init_args, loc)); - } + value = (string) sl.Converted.GetValue (); + var init_args = new List (2); + init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location)); - // - // Don't add empty sections - // - if (contains_label) - ++counter; - } + sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc); + init_args.Add (sl.Converted); + init.Add (new CollectionElementInitializer (init_args, loc)); + } + Arguments args = new Arguments (1); args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc))); Expression initializer = new NewInitialize (string_dictionary_type, args, @@ -4345,6 +4342,40 @@ namespace Mono.CSharp { string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec)); } + void ResolveUnreachableSections (BlockContext bc, Constant value) + { + var constant_label = FindLabel (value) ?? case_default; + + bool found = false; + bool unreachable_reported = false; + for (int i = 0; i < block.Statements.Count; ++i) { + var s = block.Statements[i]; + + if (s is SwitchLabel) { + if (unreachable_reported) { + found = unreachable_reported = false; + } + + found |= s == constant_label; + continue; + } + + if (found) { + unreachable_reported = true; + continue; + } + + if (!unreachable_reported) { + unreachable_reported = true; + if (!bc.IsUnreachable) { + bc.Report.Warning (162, 2, s.loc, "Unreachable code detected"); + } + } + + block.Statements[i] = new EmptyStatement (s.loc); + } + } + void DoEmitStringSwitch (EmitContext ec) { Label l_initialized = ec.DefineLabel (); @@ -4352,7 +4383,7 @@ namespace Mono.CSharp { // // Skip initialization when value is null // - value.EmitBranchable (ec, null_target, false); + value.EmitBranchable (ec, nullLabel, false); // // Check if string dictionary is initialized and initialize @@ -4378,7 +4409,7 @@ namespace Mono.CSharp { // // A value was not found, go to default case // - get_item.EmitBranchable (ec, default_target, false); + get_item.EmitBranchable (ec, defaultLabel, false); } else { Arguments get_value_args = new Arguments (1); get_value_args.Add (new Argument (value)); @@ -4389,7 +4420,7 @@ namespace Mono.CSharp { LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object); get_item_object.EmitAssign (ec, get_item, true, false); - ec.Emit (OpCodes.Brfalse, default_target); + ec.Emit (OpCodes.Brfalse, defaultLabel); ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable, new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc); @@ -4402,49 +4433,106 @@ namespace Mono.CSharp { string_switch_variable.Release (ec); } - protected override void DoEmit (EmitContext ec) + // + // Emits switch using simple if/else comparison for small label count (4 + optional default) + // + void EmitShortSwitch (EmitContext ec) { - // Workaround broken flow-analysis - block.HasUnreachableClosingBrace = true; + MethodSpec equal_method = null; + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) { + equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc); + } - // - // Needed to emit anonymous storey initialization - // Otherwise it does not contain any statements for now - // - block.Emit (ec); + if (equal_method != null) { + value.EmitBranchable (ec, nullLabel, false); + } + + for (int i = 0; i < case_labels.Count; ++i) { + var label = case_labels [i]; + if (label == case_default || label == case_null) + continue; - default_target = ec.DefineLabel (); - null_target = ec.DefineLabel (); + var constant = label.Converted; - if (IsNullable) { - unwrap.EmitCheck (ec); - ec.Emit (OpCodes.Brfalse, null_target); - value.EmitAssign (ec, new_expr, false, false); - } else if (new_expr != value && !is_constant) { - value.EmitAssign (ec, new_expr, false, false); + if (equal_method != null) { + value.Emit (ec); + constant.Emit (ec); + + var call = new CallEmitter (); + call.EmitPredefined (ec, equal_method, new Arguments (0)); + ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec)); + continue; + } + + if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) { + value.EmitBranchable (ec, label.GetILLabel (ec), false); + continue; + } + + value.Emit (ec); + constant.Emit (ec); + ec.Emit (OpCodes.Beq, label.GetILLabel (ec)); } + ec.Emit (OpCodes.Br, defaultLabel); + } + + void EmitDispatch (EmitContext ec) + { + if (value == null) { + // + // Constant switch, we already done the work + // + return; + } + + if (string_dictionary != null) { + DoEmitStringSwitch (ec); + } else if (case_labels.Count < 4 || string_labels != null) { + EmitShortSwitch (ec); + } else { + EmitTableSwitch (ec, value); + } + } + + protected override void DoEmit (EmitContext ec) + { + // Workaround broken flow-analysis + block.HasUnreachableClosingBrace = true; + // // Setup the codegen context // Label old_end = ec.LoopEnd; Switch old_switch = ec.Switch; - + ec.LoopEnd = ec.DefineLabel (); ec.Switch = this; - // Emit Code. - if (is_constant) { - if (constant_section != null) - constant_section.Block.Emit (ec); - } else if (string_dictionary != null) { - DoEmitStringSwitch (ec); - } else if (simple_stmt != null) { - simple_stmt.Emit (ec); - } else { - EmitTableSwitch (ec, value); + defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec); + nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec); + + if (value != null) { + ec.Mark (loc); + if (IsNullable) { + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, nullLabel); + value.EmitAssign (ec, new_expr, false, false); + } else if (new_expr != value) { + value.EmitAssign (ec, new_expr, false, false); + } + + + // + // Next statement is compiler generated we don't need extra + // nop when we can use the statement for sequence point + // + ec.Mark (block.StartLocation); + block.IsCompilerGenerated = true; } + block.Emit (ec); + // Restore context state. ec.MarkLabel (ec.LoopEnd); @@ -4460,10 +4548,7 @@ namespace Mono.CSharp { Switch target = (Switch) t; target.Expr = Expr.Clone (clonectx); - target.Sections = new List (); - foreach (SwitchSection ss in Sections){ - target.Sections.Add (ss.Clone (clonectx)); - } + target.block = (ExplicitBlock) block.Clone (clonectx); } public override object Accept (StructuralVisitor visitor) @@ -4552,6 +4637,7 @@ namespace Mono.CSharp { if (finally_host != null) { finally_host.Define (); + finally_host.PrepareEmit (); finally_host.Emit (); // Now it's safe to add, to close it properly and emit sequence points @@ -5042,7 +5128,7 @@ namespace Mono.CSharp { { LocalVariable pinned_string; - public StringEmitter (Expression expr, LocalVariable li, Location loc) + public StringEmitter (Expression expr, LocalVariable li) : base (expr, li) { } @@ -5089,7 +5175,7 @@ namespace Mono.CSharp { } } - public class VariableDeclaration : BlockVariableDeclaration + public class VariableDeclaration : BlockVariable { public VariableDeclaration (FullNamedExpression type, LocalVariable li) : base (type, li) @@ -5165,7 +5251,7 @@ namespace Mono.CSharp { // Case 2: string // if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) { - return new StringEmitter (initializer, li, loc).Resolve (bc); + return new StringEmitter (initializer, li).Resolve (bc); } // Case 3: fixed buffer @@ -5214,7 +5300,7 @@ namespace Mono.CSharp { } } - public BlockVariableDeclaration Variables { + public BlockVariable Variables { get { return decl; } @@ -5457,7 +5543,7 @@ namespace Mono.CSharp { { TryFinally target = (TryFinally) t; - target.stmt = (Statement) stmt.Clone (clonectx); + target.stmt = stmt.Clone (clonectx); if (fini != null) target.fini = clonectx.LookupBlock (fini); } @@ -5588,7 +5674,7 @@ namespace Mono.CSharp { public class Using : TryFinallyBlock { - public class VariableDeclaration : BlockVariableDeclaration + public class VariableDeclaration : BlockVariable { Statement dispose_call; @@ -5765,7 +5851,7 @@ namespace Mono.CSharp { } } - public BlockVariableDeclaration Variables { + public BlockVariable Variables { get { return decl; } @@ -6102,7 +6188,7 @@ namespace Mono.CSharp { if (mg != null) { mg.InstanceExpression = expr; Arguments args = new Arguments (0); - mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None); + mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly); // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply if (ambiguous_getenumerator_name) @@ -6123,39 +6209,31 @@ namespace Mono.CSharp { if (!gen_ienumerable.Define ()) gen_ienumerable = null; - do { - var ifaces = t.Interfaces; - if (ifaces != null) { - foreach (var iface in ifaces) { - if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) { - if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) { - rc.Report.SymbolRelatedToPreviousError (expr.Type); - rc.Report.Error (1640, loc, - "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation", - expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ()); - - return null; - } + var ifaces = t.Interfaces; + if (ifaces != null) { + foreach (var iface in ifaces) { + if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) { + if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) { + rc.Report.SymbolRelatedToPreviousError (expr.Type); + rc.Report.Error (1640, loc, + "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation", + expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ()); + + return null; + } - // TODO: Cache this somehow - iface_candidate = new PredefinedMember (rc.Module, iface, - MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null)); + // TODO: Cache this somehow + iface_candidate = new PredefinedMember (rc.Module, iface, + MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null)); - continue; - } + continue; + } - if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) { - iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator; - } + if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) { + iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator; } } - - if (t.IsGenericParameter) - t = t.BaseType; - else - t = null; - - } while (t != null); + } if (iface_candidate == null) { if (expr.Type != InternalType.ErrorType) { @@ -6416,14 +6494,20 @@ namespace Mono.CSharp { protected override void DoEmit (EmitContext ec) { - variable.CreateBuilder (ec); - Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd; ec.LoopBegin = ec.DefineLabel (); ec.LoopEnd = ec.DefineLabel (); + if (!(statement is Block)) + ec.BeginCompilerScope (); + + variable.CreateBuilder (ec); + statement.Emit (ec); + if (!(statement is Block)) + ec.EndScope (); + ec.LoopBegin = old_begin; ec.LoopEnd = old_end; }