X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fstatement.cs;h=51a975d549f59797a6eeff5bfbe67ab96fdd1cc1;hb=21dd2b0200d091c43fce9e1941e8ab5e111f3651;hp=38a7529f89242fb61d8e95671044b6ca6bc12fc9;hpb=2d23bfcbce7a3f7e54dcd5911adb88b244baca35;p=mono.git diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index 38a7529f892..51a975d549f 100644 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -632,6 +632,55 @@ namespace Mono.CSharp { } } + // + // Simple version of statement list not requiring a block + // + public class StatementList : Statement + { + List statements; + + public StatementList (Statement first, Statement second) + { + statements = new List () { first, second }; + } + + #region Properties + public IList Statements { + get { + return statements; + } + } + #endregion + + public void Add (Statement statement) + { + statements.Add (statement); + } + + public override bool Resolve (BlockContext ec) + { + foreach (var s in statements) + s.Resolve (ec); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + foreach (var s in statements) + s.Emit (ec); + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + StatementList t = (StatementList) target; + + t.statements = new List (statements.Count); + foreach (Statement s in statements) + t.statements.Add (s.Clone (clonectx)); + } + } + // A 'return' or a 'yield break' public abstract class ExitStatement : Statement { @@ -659,14 +708,23 @@ namespace Mono.CSharp { /// /// Implements the return statement /// - public class Return : ExitStatement { + public class Return : ExitStatement + { protected Expression Expr; public Return (Expression expr, Location l) { Expr = expr; loc = l; } - + + #region Properties + public Expression Expression { + get { + return Expr; + } + } + #endregion + protected override bool DoResolve (BlockContext ec) { if (Expr == null) { @@ -1392,6 +1450,8 @@ namespace Mono.CSharp { List anonymous_children; + int? resolving_init_idx; + protected static int id; int this_id; @@ -1445,13 +1505,7 @@ namespace Mono.CSharp { statements = new List (4); } - public Block CreateSwitchBlock (Location start) - { - // FIXME: should this be implicit? - Block new_block = new ExplicitBlock (this, start, start); - new_block.switch_block = this; - return new_block; - } + #region Properties public int ID { get { return this_id; } @@ -1465,6 +1519,16 @@ namespace Mono.CSharp { } } + #endregion + + public Block CreateSwitchBlock (Location start) + { + // FIXME: should this be implicit? + Block new_block = new ExplicitBlock (this, start, start); + new_block.switch_block = this; + return new_block; + } + void AddChild (Block b) { if (children == null) @@ -1638,7 +1702,7 @@ namespace Mono.CSharp { return false; } - protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l) + protected bool CheckParentConflictName (ToplevelBlock block, string name, Location l) { LocalInfo vi = GetLocalInfo (name); if (vi != null) { @@ -1653,13 +1717,13 @@ namespace Mono.CSharp { } if (block != null) { - Expression e = block.GetParameterReference (name, Location.Null); - if (e != null) { - ParameterReference pr = e as ParameterReference; - if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess)) + var tblock = block.CheckParameterNameConflict (name); + if (tblock != null) { + if (block == tblock && block is Linq.QueryBlock) Error_AlreadyDeclared (loc, name); else Error_AlreadyDeclared (loc, name, "parent or current"); + return false; } } @@ -1672,16 +1736,6 @@ namespace Mono.CSharp { if (!CheckParentConflictName (Toplevel, name, l)) return null; - if (Toplevel.GenericMethod != null) { - foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) { - if (tp.Name == name) { - Toplevel.Report.SymbolRelatedToPreviousError (tp); - Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable"); - return null; - } - } - } - IKnownVariable kvi = Explicit.GetKnownVariable (name); if (kvi != null) { Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name); @@ -1723,9 +1777,9 @@ namespace Mono.CSharp { "A local variable named `{0}' is already defined in this scope", name); } - public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict) + public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict) { - r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'", + Toplevel.Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'", name, conflict); } @@ -1766,9 +1820,8 @@ namespace Mono.CSharp { { LocalInfo ret; for (Block b = this; b != null; b = b.Parent) { - if (b.variables != null) { - if (b.variables.TryGetValue (name, out ret)) - return ret; + if (b.variables != null && b.variables.TryGetValue (name, out ret)) { + return ret; } } @@ -1802,7 +1855,18 @@ namespace Mono.CSharp { if (scope_initializers == null) scope_initializers = new List (); - scope_initializers.Add (s); + // + // Simple recursive helper, when resolve scope initializer another + // new scope initializer can be added, this ensures it's initialized + // before existing one. For now this can happen with expression trees + // in base ctor initializer only + // + if (resolving_init_idx.HasValue) { + scope_initializers.Insert (resolving_init_idx.Value, s); + ++resolving_init_idx; + } else { + scope_initializers.Add (s); + } } public void AddStatement (Statement s) @@ -2024,8 +2088,11 @@ namespace Mono.CSharp { // Compiler generated scope statements // if (scope_initializers != null) { - foreach (Statement s in scope_initializers) - s.Resolve (ec); + for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) { + scope_initializers[resolving_init_idx.Value].Resolve (ec); + } + + resolving_init_idx = null; } // @@ -2132,8 +2199,7 @@ namespace Mono.CSharp { protected override void DoEmit (EmitContext ec) { for (int ix = 0; ix < statements.Count; ix++){ - Statement s = (Statement) statements [ix]; - s.Emit (ec); + statements [ix].Emit (ec); } } @@ -2172,7 +2238,7 @@ namespace Mono.CSharp { public override string ToString () { - return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation); + return String.Format ("{0} ({1}:{2})", GetType (), this_id, StartLocation); } protected override void CloneTo (CloneContext clonectx, Statement t) @@ -2181,7 +2247,7 @@ namespace Mono.CSharp { clonectx.AddBlockMap (this, target); - //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel); + target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel); target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit); if (Parent != null) target.Parent = clonectx.RemapBlockCopy (Parent); @@ -2354,15 +2420,13 @@ namespace Mono.CSharp { base.EmitMeta (ec); } - internal IKnownVariable GetKnownVariable (string name) + public IKnownVariable GetKnownVariable (string name) { if (known_variables == null) return null; IKnownVariable kw; - if (!known_variables.TryGetValue (name, out kw)) - return null; - + known_variables.TryGetValue (name, out kw); return kw; } @@ -2464,9 +2528,8 @@ namespace Mono.CSharp { } } - GenericMethod generic; protected ParametersCompiled parameters; - ToplevelParameterInfo[] parameter_info; + protected ToplevelParameterInfo[] parameter_info; LocalInfo this_variable; bool resolved; bool unreachable; @@ -2491,10 +2554,6 @@ namespace Mono.CSharp { get { return compiler.Report; } } - public GenericMethod GenericMethod { - get { return generic; } - } - public ToplevelBlock Container { get { return Parent == null ? null : Parent.Toplevel; } } @@ -2504,12 +2563,6 @@ namespace Mono.CSharp { { } - public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) : - this (ctx, parent, parameters, start) - { - this.generic = generic; - } - public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) : this (ctx, null, (Flags) 0, parameters, start) { @@ -2547,10 +2600,11 @@ namespace Mono.CSharp { ToplevelBlock target = (ToplevelBlock) t; base.CloneTo (clonectx, t); - if (parameters.Count != 0) - target.parameter_info = new ToplevelParameterInfo [parameters.Count]; - for (int i = 0; i < parameters.Count; ++i) - target.parameter_info [i] = new ToplevelParameterInfo (target, i); + if (parameters.Count != 0) { + target.parameter_info = new ToplevelParameterInfo[parameters.Count]; + for (int i = 0; i < parameters.Count; ++i) + target.parameter_info[i] = new ToplevelParameterInfo (target, i); + } } public bool CheckError158 (string name, Location loc) @@ -2643,6 +2697,9 @@ namespace Mono.CSharp { public Expression GetParameterReference (string name, Location loc) { for (ToplevelBlock t = this; t != null; t = t.Container) { + if (t.parameters.IsEmpty) + continue; + Expression expr = t.GetParameterReferenceExpression (name, loc); if (expr != null) return expr; @@ -2658,6 +2715,21 @@ namespace Mono.CSharp { null : new ParameterReference (parameter_info [idx], loc); } + public ToplevelBlock CheckParameterNameConflict (string name) + { + for (ToplevelBlock t = this; t != null; t = t.Container) { + if (t.HasParameterWithName (name)) + return t; + } + + return null; + } + + protected virtual bool HasParameterWithName (string name) + { + return parameters.GetParameterIndexByName (name) >= 0; + } + // // Returns the "this" instance variable of this block. // See AddThisVariable() for more information. @@ -2725,15 +2797,18 @@ namespace Mono.CSharp { unreachable = top_level.End (); } - } catch (Exception) { -#if PRODUCTION + } catch (Exception e) { + if (e is CompletionResult || rc.Report.IsDisabled) + throw; + if (rc.CurrentBlock != null) { - ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve"); + rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message); } else { - ec.Report.Error (587, "Internal compiler error: Phase Resolve"); + rc.Report.Error (587, "Internal compiler error: {0}", e.Message); } -#endif - throw; + + if (Report.DebugFlags > 0) + throw; } if (rc.ReturnType != TypeManager.void_type && !unreachable) { @@ -3689,7 +3764,7 @@ namespace Mono.CSharp { Arguments get_value_args = new Arguments (1); get_value_args.Add (new Argument (value)); - Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc); + Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args, loc), loc).Resolve (rc); if (get_item == null) return; @@ -4034,10 +4109,11 @@ namespace Mono.CSharp { public class Unchecked : Statement { public Block Block; - public Unchecked (Block b) + public Unchecked (Block b, Location loc) { Block = b; b.Unchecked = true; + this.loc = loc; } public override bool Resolve (BlockContext ec) @@ -4063,10 +4139,11 @@ namespace Mono.CSharp { public class Checked : Statement { public Block Block; - public Checked (Block b) + public Checked (Block b, Location loc) { Block = b; b.Unchecked = false; + this.loc = loc; } public override bool Resolve (BlockContext ec) @@ -4092,11 +4169,11 @@ namespace Mono.CSharp { public class Unsafe : Statement { public Block Block; - public Unsafe (Block b) + public Unsafe (Block b, Location loc) { Block = b; Block.Unsafe = true; - loc = b.StartLocation; + this.loc = loc; } public override bool Resolve (BlockContext ec) @@ -4124,7 +4201,7 @@ namespace Mono.CSharp { // // Fixed statement // - class Fixed : Statement { + public class Fixed : Statement { Expression type; List> declarators; Statement statement; @@ -4203,7 +4280,7 @@ namespace Mono.CSharp { pinned_string.Emit (ec); ec.Emit (OpCodes.Conv_I); - PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location); + PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location); //pe.InstanceExpression = pinned_string; pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec); @@ -4315,7 +4392,7 @@ namespace Mono.CSharp { new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc), new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)), new NullPointer (loc), - converted); + converted, loc); converted = converted.Resolve (ec); @@ -4687,54 +4764,87 @@ namespace Mono.CSharp { } // FIXME: Why is it almost exact copy of Using ?? - public class UsingTemporary : ExceptionStatement { - TemporaryVariable local_copy; - public Statement Statement; + public class UsingTemporary : ExceptionStatement + { + protected TemporaryVariable local_copy; + Statement statement; Expression expr; - TypeSpec expr_type; + protected Statement dispose_call; public UsingTemporary (Expression expr, Statement stmt, Location l) { this.expr = expr; - Statement = stmt; + statement = stmt; loc = l; } - public override bool Resolve (BlockContext ec) + #region Properties + public Expression Expression { + get { + return expr; + } + } + + public Statement Statement { + get { + return statement; + } + } + + #endregion + + protected virtual bool DoResolve (BlockContext ec) { expr = expr.Resolve (ec); if (expr == null) return false; - expr_type = expr.Type; - - if (!expr_type.ImplementsInterface (TypeManager.idisposable_type) && + if (!expr.Type.ImplementsInterface (TypeManager.idisposable_type) && Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) { - if (expr_type != InternalType.Dynamic) { + if (expr.Type != InternalType.Dynamic) { Using.Error_IsNotConvertibleToIDisposable (ec, expr); return false; } expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc); - expr_type = expr.Type; } + var expr_type = expr.Type; + local_copy = new TemporaryVariable (expr_type, loc); local_copy.Resolve (ec); + 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 (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); - bool ok = Statement.Resolve (ec); + ok &= statement.Resolve (ec); ec.EndFlowBranching (); ok &= base.Resolve (ec); - if (TypeManager.void_dispose_void == null) { - TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod ( - TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes); - } - return ok; } @@ -4745,34 +4855,12 @@ namespace Mono.CSharp { protected override void EmitTryBody (EmitContext ec) { - Statement.Emit (ec); + statement.Emit (ec); } protected override void EmitFinallyBody (EmitContext ec) { - if (!TypeManager.IsStruct (expr_type)) { - Label skip = ec.DefineLabel (); - local_copy.Emit (ec); - ec.Emit (OpCodes.Brfalse, skip); - local_copy.Emit (ec); - ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); - ec.MarkLabel (skip); - return; - } - - MethodSpec ms = MemberCache.FindMember (expr_type, - MemberFilter.Method ("Dispose", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.void_type), - BindingRestriction.InstanceOnly) as MethodSpec; - - if (ms == null) { - local_copy.Emit (ec); - ec.Emit (OpCodes.Box, expr_type); - ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); - return; - } - - local_copy.AddressOf (ec, AddressOp.Load); - ec.Emit (OpCodes.Call, ms); + dispose_call.Emit (ec); } protected override void CloneTo (CloneContext clonectx, Statement t) @@ -4780,7 +4868,7 @@ namespace Mono.CSharp { UsingTemporary target = (UsingTemporary) t; target.expr = expr.Clone (clonectx); - target.Statement = Statement.Clone (clonectx); + target.statement = statement.Clone (clonectx); } } @@ -4830,7 +4918,7 @@ namespace Mono.CSharp { ec.Emit (OpCodes.Brfalse, skip); } - Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc); + Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc); if (emit_null_check) ec.MarkLabel (skip); @@ -4980,7 +5068,7 @@ namespace Mono.CSharp { list.Add (new Argument (counter [i])); } - access = new ElementAccess (copy, list).Resolve (ec); + access = new ElementAccess (copy, list, loc).Resolve (ec); if (access == null) return false; @@ -5069,16 +5157,16 @@ namespace Mono.CSharp { } } - sealed class CollectionForeach : Statement + sealed class CollectionForeach : Statement, MethodGroupExpr.IErrorHandler { - class CollectionForeachStatement : Statement + class Body : Statement { TypeSpec type; Expression variable, current, conv; Statement statement; Assign assign; - public CollectionForeachStatement (TypeSpec type, Expression variable, + public Body (TypeSpec type, Expression variable, Expression current, Statement statement, Location loc) { @@ -5121,20 +5209,59 @@ namespace Mono.CSharp { } } - Expression variable, expr; - Statement statement; + class Dispose : UsingTemporary + { + LocalTemporary dispose; + + public Dispose (TemporaryVariable variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc) + : base (expr, statement, loc) + { + base.local_copy = variable; + this.dispose = dispose; + } + + protected override bool DoResolve (BlockContext ec) + { + 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)); - TemporaryVariable enumerator; - Expression init; - Statement loop; - Statement wrapper; + if (!dispose_var.Type.IsStruct) + dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc); + + return dispose_call.Resolve (ec); + } + + 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); + } + + base.EmitFinallyBody (ec); + + if (dispose != null) { + ec.MarkLabel (call_dispose); + dispose.Release (ec); + } + } + } - MethodGroupExpr get_enumerator; - PropertyExpr get_current; - MethodSpec move_next; + Expression variable, expr; + Statement statement; Expression var_type; - TypeSpec enumerator_type; - bool enumerator_found; + ExpressionStatement init; public CollectionForeach (Expression var_type, Expression var, Expression expr, Statement stmt, Location l) @@ -5151,405 +5278,215 @@ namespace Mono.CSharp { throw new NotImplementedException (); } - bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi) + void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator) { - TypeSpec return_type = mi.ReturnType; + rc.Report.SymbolRelatedToPreviousError (enumerator); + rc.Report.Error (202, loc, + "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property", + enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ()); + } + MethodGroupExpr ResolveGetEnumerator (ResolveContext rc) + { // - // Ok, we can access it, now make sure that we can do something - // with this `GetEnumerator' + // Option 1: Try to match by name GetEnumerator first // - - if (return_type == TypeManager.ienumerator_type || - return_type.ImplementsInterface (TypeManager.ienumerator_type)) { - // - // If it is not an interface, lets try to find the methods ourselves. - // For example, if we have: - // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}} - // We can avoid the iface call. This is a runtime perf boost. - // even bigger if we have a ValueType, because we avoid the cost - // of boxing. - // - // We have to make sure that both methods exist for us to take - // this path. If one of the methods does not exist, we will just - // use the interface. Sadly, this complex if statement is the only - // way I could do this without a goto - // - - if (TypeManager.bool_movenext_void == null) { - TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod ( - TypeManager.ienumerator_type, "MoveNext", loc, TypeSpec.EmptyTypes); + var mexpr = Expression.MemberLookup (rc.Compiler, rc.CurrentType, null, expr.Type, "GetEnumerator", -1, + MemberKind.All, BindingRestriction.DefaultMemberLookup | BindingRestriction.AccessibleOnly, loc); + + var mg = mexpr as MethodGroupExpr; + if (mg != null) { + mg.InstanceExpression = expr; + mg.CustomErrorHandler = this; + Arguments args = new Arguments (0); + mg = mg.OverloadResolve (rc, ref args, false, loc); + + if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) { + return mg; } + } - if (TypeManager.ienumerator_getcurrent == null) { - TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty ( - TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type); - } + // + // 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 ifaces = t.Interfaces; + if (ifaces != null) { + foreach (var iface in ifaces) { + if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) { + if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) { + 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 (), TypeManager.generic_ienumerable_type.GetSignatureForError ()); + + return null; + } + + iface_candidate = iface; + continue; + } - // - // Prefer a generic enumerator over a non-generic one. - // - if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) { - enumerator_type = return_type; - if (!FetchGetCurrent (ec, return_type)) - get_current = new PropertyExpr ( - ec.CurrentType, TypeManager.ienumerator_getcurrent, loc); - if (!FetchMoveNext (return_type)) - move_next = TypeManager.bool_movenext_void; - return true; + if (iface == TypeManager.ienumerable_type && iface_candidate == null) { + iface_candidate = iface; + } + } } + } - if (return_type.IsInterface || - !FetchMoveNext (return_type) || - !FetchGetCurrent (ec, return_type)) { - enumerator_type = return_type; - move_next = TypeManager.bool_movenext_void; - get_current = new PropertyExpr ( - ec.CurrentType, TypeManager.ienumerator_getcurrent, loc); - return true; - } - } else { - // - // Ok, so they dont return an IEnumerable, we will have to - // find if they support the GetEnumerator pattern. - // + if (iface_candidate == null) { + rc.Report.Error (1579, loc, + "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is not accessible", + expr.Type.GetSignatureForError (), "GetEnumerator"); - if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) { - ec.Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property", - TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi)); - return false; - } + return null; } - enumerator_type = return_type; + var method = TypeManager.GetPredefinedMethod (iface_candidate, + MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc); - return true; + if (method == null) + return null; + + mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc); + mg.InstanceExpression = expr; + return mg; } - // - // Retrieves a `public bool MoveNext ()' method from the Type `t' - // - bool FetchMoveNext (TypeSpec t) + MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator) { - move_next = MemberCache.FindMember (t, + var ms = MemberCache.FindMember (enumerator.ReturnType, MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type), BindingRestriction.InstanceOnly) as MethodSpec; - return move_next != null && (move_next.Modifiers & Modifiers.PUBLIC) != 0; - } - - // - // Retrieves a `public T get_Current ()' method from the Type `t' - // - bool FetchGetCurrent (ResolveContext ec, TypeSpec t) - { - PropertyExpr pe = Expression.MemberLookup (ec.Compiler, - ec.CurrentType, t, "Current", 0, MemberKind.Property, - BindingRestriction.AccessibleOnly, loc) as PropertyExpr; - if (pe == null) - return false; + if (ms == null || !ms.IsPublic) { + Error_WrongEnumerator (rc, enumerator); + return null; + } - get_current = pe; - return true; + return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc); } - void Error_Enumerator (BlockContext ec) + PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator) { - if (enumerator_found) { - return; + var ps = MemberCache.FindMember (enumerator.ReturnType, + MemberFilter.Property ("Current", null), + BindingRestriction.InstanceOnly) as PropertySpec; + + if (ps == null || !ps.IsPublic) { + Error_WrongEnumerator (rc, enumerator); + return null; } - ec.Report.Error (1579, loc, - "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible", - TypeManager.CSharpName (expr.Type)); + return ps; } - bool TryType (ResolveContext ec, TypeSpec t) + public override bool Resolve (BlockContext ec) { - var mg = Expression.MemberLookup (ec.Compiler, ec.CurrentType, null, t, "GetEnumerator", 0, - MemberKind.Method, BindingRestriction.NoOverrides | BindingRestriction.InstanceOnly, loc) as MethodGroupExpr; + bool is_dynamic = expr.Type == InternalType.Dynamic; + if (is_dynamic) + expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc); - if (mg == null) + var get_enumerator_mg = ResolveGetEnumerator (ec); + if (get_enumerator_mg == null) { return false; - - MethodSpec result = null; - MethodSpec tmp_move_next = null; - PropertyExpr tmp_get_cur = null; - TypeSpec tmp_enumerator_type = enumerator_type; - foreach (MethodSpec mi in mg.Methods) { - if (!mi.Parameters.IsEmpty) - continue; - - // Check whether GetEnumerator is public - if ((mi.Modifiers & Modifiers.AccessibilityMask) != Modifiers.PUBLIC) - continue; - - enumerator_found = true; - - if (!GetEnumeratorFilter (ec, mi)) - continue; - - if (result != null) { - if (TypeManager.IsGenericType (result.ReturnType)) { - if (!TypeManager.IsGenericType (mi.ReturnType)) - continue; - - ec.Report.SymbolRelatedToPreviousError (t); - ec.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", - TypeManager.CSharpName (t), TypeManager.generic_ienumerable_type.GetSignatureForError ()); - return false; - } - - // Always prefer generics enumerators - if (!TypeManager.IsGenericType (mi.ReturnType)) { - if (mi.DeclaringType.ImplementsInterface (result.DeclaringType) || - result.DeclaringType.ImplementsInterface (mi.DeclaringType)) - continue; - - ec.Report.SymbolRelatedToPreviousError (result); - ec.Report.SymbolRelatedToPreviousError (mi); - ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'", - TypeManager.CSharpName (t), "enumerable", result.GetSignatureForError (), mi.GetSignatureForError ()); - return false; - } - } - result = mi; - tmp_move_next = move_next; - tmp_get_cur = get_current; - tmp_enumerator_type = enumerator_type; - if (mi.DeclaringType == t) - break; } - if (result != null) { - move_next = tmp_move_next; - get_current = tmp_get_cur; - enumerator_type = tmp_enumerator_type; - get_enumerator = new MethodGroupExpr (result, enumerator_type, loc); - - if (t != expr.Type) { - expr = Convert.ExplicitConversion ( - ec, expr, t, loc); - if (expr == null) - throw new InternalErrorException (); - } - - get_enumerator.InstanceExpression = expr; - get_enumerator.IsBase = t != expr.Type; + var get_enumerator = get_enumerator_mg.BestCandidate; + var enumerator = new TemporaryVariable (get_enumerator.ReturnType, loc); + enumerator.Resolve (ec); - return true; + // Prepare bool MoveNext () + var move_next_mg = ResolveMoveNext (ec, get_enumerator); + if (move_next_mg == null) { + return false; } - return false; - } - - bool ProbeCollectionType (ResolveContext ec, TypeSpec t) - { - int errors = ec.Report.Errors; - for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type;){ - if (TryType (ec, tt)) - return true; - tt = tt.BaseType; - } + move_next_mg.InstanceExpression = enumerator; - if (ec.Report.Errors > errors) + // Prepare ~T~ Current { get; } + var current_prop = ResolveCurrent (ec, get_enumerator); + if (current_prop == null) { return false; - - // - // Now try to find the method in the interfaces - // - for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type; ) { - if (tt.Interfaces != null) { - foreach (TypeSpec i in tt.Interfaces) { - if (TryType (ec, i)) - return true; - } - } - tt = tt.BaseType; } - return false; - } - - public override bool Resolve (BlockContext ec) - { - enumerator_type = TypeManager.ienumerator_type; - - bool is_dynamic = expr.Type == InternalType.Dynamic; - if (is_dynamic) - expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc); - - if (!ProbeCollectionType (ec, expr.Type)) { - Error_Enumerator (ec); + var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator }.Resolve (ec); + if (current_pe == null) return false; - } VarExpr ve = var_type as VarExpr; if (ve != null) { // Infer implicitly typed local variable from foreach enumerable type - var_type = new TypeExpression ( - is_dynamic ? InternalType.Dynamic : get_current.Type, - var_type.Location); + var_type = new TypeExpression (current_pe.Type, var_type.Location); } var_type = var_type.ResolveAsTypeTerminal (ec, false); if (var_type == null) return false; - - enumerator = new TemporaryVariable (enumerator_type, loc); - enumerator.Resolve (ec); - - init = new Invocation (get_enumerator, null); - init = init.Resolve (ec); - if (init == null) - return false; - - Expression move_next_expr; - { - var mi = new List (1) { move_next }; - MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc); - mg.InstanceExpression = enumerator; - - move_next_expr = new Invocation (mg, null); - } - get_current.InstanceExpression = enumerator; + var init = new Invocation (get_enumerator_mg, null); + init.Resolve (ec); - Statement block = new CollectionForeachStatement ( - var_type.Type, variable, get_current, statement, loc); + statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)), + new Body (var_type.Type, variable, current_pe, statement, loc), loc); - loop = new While (new BooleanExpression (move_next_expr), block, loc); + var enum_type = enumerator.Type; - - bool implements_idisposable = enumerator_type.ImplementsInterface (TypeManager.idisposable_type); - if (implements_idisposable || !enumerator_type.IsSealed) { - wrapper = new DisposableWrapper (this, implements_idisposable); + // + // Add Dispose method call when enumerator can be IDisposable + // + if (!enumerator.Type.ImplementsInterface (TypeManager.idisposable_type)) { + if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) { + // + // Runtime Dispose check + // + var tv = new LocalTemporary (TypeManager.idisposable_type); + statement = new Dispose (enumerator, tv, init, statement, loc); + } else { + // + // No Dispose call needed + // + this.init = new SimpleAssign (enumerator, init); + this.init.Resolve (ec); + } } else { - wrapper = new NonDisposableWrapper (this); + // + // Static Dispose check + // + statement = new Dispose (enumerator, null, init, statement, loc); } - return wrapper.Resolve (ec); + return statement.Resolve (ec); } protected override void DoEmit (EmitContext ec) { - wrapper.Emit (ec); - } - - class NonDisposableWrapper : Statement { - CollectionForeach parent; - - internal NonDisposableWrapper (CollectionForeach parent) - { - this.parent = parent; - } - - protected override void CloneTo (CloneContext clonectx, Statement target) - { - throw new NotSupportedException (); - } - - public override bool Resolve (BlockContext ec) - { - return parent.ResolveLoop (ec); - } + if (init != null) + init.EmitStatement (ec); - protected override void DoEmit (EmitContext ec) - { - parent.EmitLoopInit (ec); - parent.EmitLoopBody (ec); - } + statement.Emit (ec); } - sealed class DisposableWrapper : ExceptionStatement - { - CollectionForeach parent; - bool implements_idisposable; - - internal DisposableWrapper (CollectionForeach parent, bool implements) - { - this.parent = parent; - this.implements_idisposable = implements; - } - - protected override void CloneTo (CloneContext clonectx, Statement target) - { - throw new NotSupportedException (); - } - - public override bool Resolve (BlockContext ec) - { - bool ok = true; - - ec.StartFlowBranching (this); - - if (!parent.ResolveLoop (ec)) - ok = false; - - ec.EndFlowBranching (); - - ok &= base.Resolve (ec); - - if (TypeManager.void_dispose_void == null) { - TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod ( - TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes); - } - return ok; - } - - protected override void EmitPreTryBody (EmitContext ec) - { - parent.EmitLoopInit (ec); - } - - protected override void EmitTryBody (EmitContext ec) - { - parent.EmitLoopBody (ec); - } - - protected override void EmitFinallyBody (EmitContext ec) - { - Expression instance = parent.enumerator; - if (!TypeManager.IsValueType (parent.enumerator_type)) { - - parent.enumerator.Emit (ec); - - Label call_dispose = ec.DefineLabel (); - - if (!implements_idisposable) { - ec.Emit (OpCodes.Isinst, TypeManager.idisposable_type); - LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type); - temp.Store (ec); - temp.Emit (ec); - instance = temp; - } - - ec.Emit (OpCodes.Brtrue_S, call_dispose); + #region IErrorHandler Members - // using 'endfinally' to empty the evaluation stack - ec.Emit (OpCodes.Endfinally); - ec.MarkLabel (call_dispose); - } - - Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc); - } - } - - bool ResolveLoop (BlockContext ec) + bool MethodGroupExpr.IErrorHandler.AmbiguousCall (ResolveContext ec, MethodGroupExpr mg, MethodSpec ambiguous) { - return loop.Resolve (ec); - } + ec.Report.SymbolRelatedToPreviousError (mg.BestCandidate); + ec.Report.Warning (278, 2, loc, + "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'", + mg.DeclaringType.GetSignatureForError (), "enumerable", + mg.BestCandidate.GetSignatureForError (), ambiguous.GetSignatureForError ()); - void EmitLoopInit (EmitContext ec) - { - enumerator.EmitAssign (ec, init); + return true; } - void EmitLoopBody (EmitContext ec) + bool MethodGroupExpr.IErrorHandler.NoExactMatch (ResolveContext ec, MethodSpec method) { - loop.Emit (ec); + return false; } + + #endregion } Expression type;