}
}
+ //
+ // Simple version of statement list not requiring a block
+ //
+ public class StatementList : Statement
+ {
+ List<Statement> statements;
+
+ public StatementList (Statement first, Statement second)
+ {
+ statements = new List<Statement> () { first, second };
+ }
+
+ #region Properties
+ public IList<Statement> 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<Statement> (statements.Count);
+ foreach (Statement s in statements)
+ t.statements.Add (s.Clone (clonectx));
+ }
+ }
+
// A 'return' or a 'yield break'
public abstract class ExitStatement : Statement
{
/// <summary>
/// Implements the return statement
/// </summary>
- 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) {
statements = new List<Statement> (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; }
}
}
+ #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)
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) {
}
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;
}
}
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);
"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);
}
{
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;
}
}
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);
}
}
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)
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);
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;
}
}
}
- GenericMethod generic;
protected ParametersCompiled parameters;
- ToplevelParameterInfo[] parameter_info;
+ protected ToplevelParameterInfo[] parameter_info;
LocalInfo this_variable;
bool resolved;
bool unreachable;
get { return compiler.Report; }
}
- public GenericMethod GenericMethod {
- get { return generic; }
- }
-
public ToplevelBlock Container {
get { return Parent == null ? null : Parent.Toplevel; }
}
{
}
- 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)
{
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)
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;
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;
+ }
+
// <summary>
// Returns the "this" instance variable of this block.
// See AddThisVariable() for more information.
unreachable = top_level.End ();
}
} catch (Exception e) {
+ if (e is CompletionResult || rc.Report.IsDisabled)
+ throw;
+
if (rc.CurrentBlock != null) {
rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
} else {
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;
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)
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)
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)
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);
}
// 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;
}
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)
UsingTemporary target = (UsingTemporary) t;
target.expr = expr.Clone (clonectx);
- target.Statement = Statement.Clone (clonectx);
+ target.statement = statement.Clone (clonectx);
}
}
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);
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;
}
}
- 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)
{
}
}
- 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));
+
+ 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);
+ }
- TemporaryVariable enumerator;
- Expression init;
- Statement loop;
- Statement wrapper;
+ base.EmitFinallyBody (ec);
- MethodGroupExpr get_enumerator;
- PropertyExpr get_current;
- MethodSpec move_next;
+ if (dispose != null) {
+ ec.MarkLabel (call_dispose);
+ dispose.Release (ec);
+ }
+ }
+ }
+
+ 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)
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 (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 (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;
- }
+ move_next_mg.InstanceExpression = enumerator;
- 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;
- }
-
- 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<MemberSpec> (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;
- }
+ if (init != null)
+ init.EmitStatement (ec);
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- throw new NotSupportedException ();
- }
-
- public override bool Resolve (BlockContext ec)
- {
- return parent.ResolveLoop (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;
- }
+ #region IErrorHandler Members
- 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);
-
- // 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;