using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
+using System.Collections;
+using System.Collections.Specialized;
namespace Mono.CSharp {
-
- using System.Collections;
public abstract class Statement {
public Location loc;
// in unreachable code, for instance.
//
- if (warn && (RootContext.WarningLevel >= 2))
- Report.Warning (162, loc, "Unreachable code detected");
+ if (warn)
+ Report.Warning (162, 2, loc, "Unreachable code detected");
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
return ok;
}
-
- protected void CheckObsolete (Type type)
- {
- ObsoleteAttribute obsolete_attr = AttributeTester.GetObsoleteAttribute (type);
- if (obsolete_attr == null)
- return;
-
- AttributeTester.Report_ObsoleteMessage (obsolete_attr, type.FullName, loc);
- }
-
+
/// <summary>
/// Return value indicates whether all code paths emitted return.
/// </summary>
public void Error (int error, string s)
{
- if (!Location.IsNull (loc))
+ if (!loc.IsNull)
Report.Error (error, loc, s);
else
Report.Error (error, s);
Expression Test;
readonly Statement InitStatement;
readonly Statement Increment;
- readonly Statement Statement;
+ public readonly Statement Statement;
bool infinite, empty;
public For (Statement initStatement,
public class StatementExpression : Statement {
ExpressionStatement expr;
- public StatementExpression (ExpressionStatement expr, Location l)
+ public StatementExpression (ExpressionStatement expr)
{
this.expr = expr;
- loc = l;
+ loc = expr.Location;
}
public override bool Resolve (EmitContext ec)
if (expr == null)
return false;
- if (!(expr is Constant)){
+ Constant c = expr as Constant;
+ if (c == null) {
Error (150, "A constant value is expected");
return false;
}
- object val = Expression.ConvertIntLiteral (
- (Constant) expr, ec.Switch.SwitchType, loc);
+ c = c.ToType (ec.Switch.SwitchType, loc);
+ if (c == null)
+ return false;
+ object val = c.GetValue ();
if (val == null)
- return false;
+ val = SwitchLabel.NullStringCase;
sl = (SwitchLabel) ec.Switch.Elements [val];
if (sl == null){
- Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", val);
+ Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", c.GetValue () == null ? "null" : val.ToString ());
return false;
}
public override bool Resolve (EmitContext ec)
{
- if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
+ if (!ec.CurrentBranching.InLoop ()){
Error (139, "No enclosing loop out of which to break or continue");
return false;
} else if (ec.InFinally) {
public readonly ToplevelBlock Toplevel;
[Flags]
- public enum Flags {
+ public enum Flags : ushort {
Implicit = 1,
Unchecked = 2,
BlockUsed = 4,
HasRet = 16,
IsDestructor = 32,
IsToplevel = 64,
- Unsafe = 128
+ Unsafe = 128,
+ HasVarargs = 256 // Used in ToplevelBlock
}
- Flags flags;
+ protected Flags flags;
public bool Implicit {
get { return (flags & Flags.Implicit) != 0; }
//
// Keeps track of (name, type) pairs
//
- Hashtable variables;
+ IDictionary variables;
//
// Keeps track of constants
get { return this_id; }
}
- protected Hashtable Variables {
+ protected IDictionary Variables {
get {
if (variables == null)
- variables = new Hashtable ();
+ variables = new ListDictionary ();
return variables;
}
}
public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
{
- LocalInfo kvi = GetKnownVariableInfo (name);
- if (kvi == null || kvi.Block == this)
+ Block b = this;
+ LocalInfo kvi = b.GetKnownVariableInfo (name);
+ while (kvi == null) {
+ while (b.Implicit)
+ b = b.Parent;
+ b = b.Parent;
+ if (b == null)
+ return true;
+ kvi = b.GetKnownVariableInfo (name);
+ }
+
+ if (kvi.Block == b)
return true;
- if (known_variables != kvi.Block.known_variables) {
- Report.SymbolRelatedToPreviousError (kvi.Location, name);
- Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
- return false;
+ // Is kvi.Block nested inside 'b'
+ if (b.known_variables != kvi.Block.known_variables) {
+ //
+ // If a variable by the same name it defined in a nested block of this
+ // block, we violate the invariant meaning in a block.
+ //
+ if (b == this) {
+ Report.SymbolRelatedToPreviousError (kvi.Location, name);
+ Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
+ return false;
+ }
+
+ //
+ // It's ok if the definition is in a nested subblock of b, but not
+ // nested inside this block -- a definition in a sibling block
+ // should not affect us.
+ //
+ return true;
}
//
- // this block and kvi.Block are the same textual block.
+ // Block 'b' and kvi.Block are the same textual block.
// However, different variables are extant.
//
// Check if the variable is in scope in both blocks. We use
// an indirect check that depends on AddVariable doing its
// part in maintaining the invariant-meaning-in-block property.
//
- if (e is LocalVariableReference || (e is Constant && GetLocalInfo (name) != null))
+ if (e is LocalVariableReference || (e is Constant && b.GetLocalInfo (name) != null))
return true;
- Report.SymbolRelatedToPreviousError (kvi.Location, name);
- Error_AlreadyDeclared (loc, name, "parent or current");
+ //
+ // Even though we detected the error when the name is used, we
+ // treat it as if the variable declaration was in error.
+ //
+ Report.SymbolRelatedToPreviousError (loc, name);
+ Error_AlreadyDeclared (kvi.Location, name, "parent or current");
return false;
}
constants = new Hashtable ();
constants.Add (name, value);
+
+ // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
+ Use ();
return true;
}
return null;
}
- /// <summary>
- /// True if the variable named @name is a constant
- /// </summary>
- public bool IsConstant (string name)
- {
- Expression e = GetConstantExpression (name);
- return e != null;
- }
-
public void AddStatement (Statement s)
{
statements.Add (s);
/// tc: is our typecontainer (to resolve type references)
/// ig: is the code generator:
/// </remarks>
- public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip)
+ public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, Parameters ip)
{
bool old_unsafe = ec.InUnsafe;
if (cv == null)
continue;
+ // Don't let 'const int Foo = Foo;' succeed.
+ // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
+ // which in turn causes the 'must be constant' error to be triggered.
+ constants.Remove (name);
+
ec.CurrentBlock = this;
Expression e = cv.Resolve (ec);
+ if (e == null)
+ continue;
Constant ce = e as Constant;
if (ce == null){
- Report.Error (133, vi.Location,
- "The expression being assigned to `{0}' must be constant", name);
+ Const.Error_ExpressionMustBeConstant (vi.Location, name);
continue;
}
- if (e.Type != variable_type){
- e = Const.ChangeType (vi.Location, ce, variable_type);
- if (e == null)
- continue;
- }
+ e = ce.ToType (variable_type, vi.Location);
+ if (e == null)
+ continue;
- constants.Remove (name);
constants.Add (name, e);
}
}
name = (string) de.Key;
if (vector.IsAssigned (vi.VariableInfo)){
- Report.Warning (219, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
+ Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
} else {
- Report.Warning (168, vi.Location, "The variable `{0}' is declared but never used", name);
+ Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
}
}
}
bool unreachable_shown;
bool unreachable;
+ private void CheckPossibleMistakenEmptyStatement (Statement s)
+ {
+ Statement body;
+
+ // Some statements are wrapped by a Block. Since
+ // others' internal could be changed, here I treat
+ // them as possibly wrapped by Block equally.
+ Block b = s as Block;
+ if (b != null && b.statements.Count == 1)
+ s = (Statement) b.statements [0];
+
+ if (s is Lock)
+ body = ((Lock) s).Statement;
+ else if (s is For)
+ body = ((For) s).Statement;
+ else if (s is Foreach)
+ body = ((Foreach) s).Statement;
+ else if (s is While)
+ body = ((While) s).Statement;
+ else if (s is Using)
+ body = ((Using) s).Statement;
+ else if (s is Fixed)
+ body = ((Fixed) s).Statement;
+ else
+ return;
+
+ if (body == null || body is EmptyStatement)
+ Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
+ }
+
public override bool Resolve (EmitContext ec)
{
Block prev_block = ec.CurrentBlock;
int statement_count = statements.Count;
for (int ix = 0; ix < statement_count; ix++){
Statement s = (Statement) statements [ix];
+ // Check possible empty statement (CS0642)
+ if (RootContext.WarningLevel >= 3 &&
+ ix + 1 < statement_count &&
+ statements [ix + 1] is Block)
+ CheckPossibleMistakenEmptyStatement (s);
//
// Warn if we detect unreachable code.
//
if (unreachable) {
+ if (s is EmptyStatement)
+ continue;
+
if (s is Block)
((Block) s).unreachable = true;
- if (!unreachable_shown && (RootContext.WarningLevel >= 2)) {
- Report.Warning (
- 162, loc, "Unreachable code detected");
+ if (!unreachable_shown) {
+ Report.Warning (162, 2, s.loc, "Unreachable code detected");
unreachable_shown = true;
}
}
if ((labels != null) && (RootContext.WarningLevel >= 2)) {
foreach (LabeledStatement label in labels.Values)
if (!label.HasBeenReferenced)
- Report.Warning (164, label.loc,
+ Report.Warning (164, 2, label.loc,
"This label has not been referenced");
}
unreachable_shown = true;
unreachable = true;
- if (warn && (RootContext.WarningLevel >= 2))
- Report.Warning (162, loc, "Unreachable code detected");
+ if (warn)
+ Report.Warning (162, 2, loc, "Unreachable code detected");
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
Hashtable capture_contexts;
ArrayList children;
- public bool HasVarargs = false;
+ public bool HasVarargs {
+ get { return (flags & Flags.HasVarargs) != 0; }
+ set { flags |= Flags.HasVarargs; }
+ }
//
// The parameters for the block.
Parameters pars = t.Parameters;
par = pars.GetParameterByName (name, out idx);
if (par != null)
- return new ParameterReference (pars, this, idx, name, loc);
+ return new ParameterReference (par, this, idx, loc);
}
return null;
}
return this_variable == null || this_variable.IsThisAssigned (ec, loc);
}
- public bool ResolveMeta (EmitContext ec, InternalParameters ip)
+ public bool ResolveMeta (EmitContext ec, Parameters ip)
{
int errors = Report.Errors;
public class SwitchLabel {
Expression label;
object converted;
- public Location loc;
+ Location loc;
Label il_label;
bool il_label_set;
Label il_label_code;
bool il_label_code_set;
+ public static readonly object NullStringCase = new object ();
+
//
// if expr == null, then it is the default case.
//
// and then converts it to the requested type.
//
public bool ResolveAndReduce (EmitContext ec, Type required_type)
- {
- if (label == null)
- return true;
-
+ {
Expression e = label.Resolve (ec);
if (e == null)
return false;
- if (!(e is Constant)){
- Report.Error (150, loc, "A constant value is expected, got: " + e);
+ Constant c = e as Constant;
+ if (c == null){
+ Report.Error (150, loc, "A constant value is expected");
return false;
}
- if (e is StringConstant || e is NullLiteral){
- if (required_type == TypeManager.string_type){
- converted = e;
- return true;
- }
+ if (required_type == TypeManager.string_type && c.GetValue () == null) {
+ converted = NullStringCase;
+ return true;
}
- converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
- if (converted == null)
+ c = c.ToType (required_type, loc);
+ if (c == null)
return false;
+ converted = c.GetValue ();
return true;
}
+
+ public void Erorr_AlreadyOccurs ()
+ {
+ string label;
+ if (converted == null)
+ label = "default";
+ else if (converted == NullStringCase)
+ label = "null";
+ else
+ label = converted.ToString ();
+
+ Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
+ }
}
public class SwitchSection {
/// <summary>
/// Maps constants whose type type SwitchType to their SwitchLabels.
/// </summary>
- public Hashtable Elements;
+ public IDictionary Elements;
/// <summary>
/// The governing switch type
return converted;
}
- static string Error152 {
- get {
- return "The label `{0}:' already occurs in this switch statement";
- }
- }
-
//
// Performs the basic sanity checks on the switch statement
// (looks for duplicate keys and non-constant expressions).
//
bool CheckSwitch (EmitContext ec)
{
- Type compare_type;
bool error = false;
- Elements = new Hashtable ();
+ Elements = Sections.Count > 10 ?
+ (IDictionary)new Hashtable () :
+ (IDictionary)new ListDictionary ();
- if (TypeManager.IsEnumType (SwitchType)){
- compare_type = TypeManager.EnumToUnderlying (SwitchType);
- } else
- compare_type = SwitchType;
-
foreach (SwitchSection ss in Sections){
foreach (SwitchLabel sl in ss.Labels){
- if (!sl.ResolveAndReduce (ec, SwitchType)){
- error = true;
- continue;
- }
-
if (sl.Label == null){
if (default_section != null){
- Report.Error (152, sl.loc, Error152, "default");
+ sl.Erorr_AlreadyOccurs ();
error = true;
}
default_section = ss;
continue;
}
-
- object key = sl.Converted;
-
- if (key is Constant)
- key = ((Constant) key).GetValue ();
-
- if (key == null)
- key = NullLiteral.Null;
-
- string lname = null;
- if (compare_type == TypeManager.uint64_type){
- ulong v = (ulong) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.int64_type){
- long v = (long) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.uint32_type){
- uint v = (uint) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.char_type){
- char v = (char) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.byte_type){
- byte v = (byte) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.sbyte_type){
- sbyte v = (sbyte) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.short_type){
- short v = (short) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.ushort_type){
- ushort v = (ushort) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.string_type){
- if (key is NullLiteral){
- if (Elements.Contains (NullLiteral.Null))
- lname = "null";
- else
- Elements.Add (NullLiteral.Null, null);
- } else {
- string s = (string) key;
-
- if (Elements.Contains (s))
- lname = s;
- else
- Elements.Add (s, sl);
- }
- } else if (compare_type == TypeManager.int32_type) {
- int v = (int) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- } else if (compare_type == TypeManager.bool_type) {
- bool v = (bool) key;
-
- if (Elements.Contains (v))
- lname = v.ToString ();
- else
- Elements.Add (v, sl);
- }
- else
- {
- throw new Exception ("Unknown switch type!" +
- SwitchType + " " + compare_type);
- }
- if (lname != null) {
- Report.Error (152, sl.loc, Error152, "case " + lname);
+ if (!sl.ResolveAndReduce (ec, SwitchType)){
error = true;
+ continue;
}
+
+ object key = sl.Converted;
+ try {
+ Elements.Add (key, sl);
+ }
+ catch (ArgumentException) {
+ sl.Erorr_AlreadyOccurs ();
+ error = true;
+ }
}
}
- if (error)
- return false;
-
- return true;
+ return !error;
}
void EmitObjectInteger (ILGenerator ig, object k)
ig.Emit (OpCodes.Ldloc, val);
- if (Elements.Contains (NullLiteral.Null)){
+ if (Elements.Contains (SwitchLabel.NullStringCase)){
ig.Emit (OpCodes.Brfalse, null_target);
} else
ig.Emit (OpCodes.Brfalse, default_target);
if (sl.Label != null){
object lit = sl.Converted;
- if (lit is NullLiteral){
+ if (lit == SwitchLabel.NullStringCase){
null_found = true;
if (label_count == 1)
ig.Emit (OpCodes.Br, next_test);
continue;
-
}
- StringConstant str = (StringConstant) lit;
ig.Emit (OpCodes.Ldloc, val);
- ig.Emit (OpCodes.Ldstr, str.Value);
+ ig.Emit (OpCodes.Ldstr, (string)lit);
if (label_count == 1)
ig.Emit (OpCodes.Bne_Un, next_test);
else {
public class Lock : ExceptionStatement {
Expression expr;
- Statement Statement;
- LocalBuilder temp;
+ public Statement Statement;
+ TemporaryVariable temp;
public Lock (Expression expr, Statement stmt, Location l)
{
if (expr.Type.IsValueType){
Report.Error (185, loc,
- "`{0}' is not a reference type as required by the lock statement",
- TypeManager.CSharpName (expr.Type));
+ "`{0}' is not a reference type as required by the lock statement",
+ TypeManager.CSharpName (expr.Type));
return false;
}
ec.NeedReturnLabel ();
}
+ temp = new TemporaryVariable (expr.Type, loc);
+ temp.Resolve (ec);
+
return true;
}
protected override void DoEmit (EmitContext ec)
{
- Type type = expr.Type;
-
ILGenerator ig = ec.ig;
- temp = ig.DeclareLocal (type);
-
- expr.Emit (ec);
- ig.Emit (OpCodes.Dup);
- ig.Emit (OpCodes.Stloc, temp);
+
+ temp.Store (ec, expr);
+ temp.Emit (ec);
ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
// try
public override void EmitFinally (EmitContext ec)
{
- ILGenerator ig = ec.ig;
- ig.Emit (OpCodes.Ldloc, temp);
- ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
+ temp.Emit (ec);
+ ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
}
}
public abstract void EmitExit (ILGenerator ig);
}
- class ExpressionEmitter: Emitter {
+ class ExpressionEmitter : Emitter {
public ExpressionEmitter (Expression converted, LocalInfo li) :
base (converted, li)
{
}
}
- class StringEmitter: Emitter {
+ class StringEmitter : Emitter {
LocalBuilder pinned_string;
Location loc;
loc = l;
}
+ public Statement Statement {
+ get { return statement; }
+ }
+
public override bool Resolve (EmitContext ec)
{
if (!ec.InUnsafe){
expr_type = texpr.ResolveType (ec);
- CheckObsolete (expr_type);
-
data = new Emitter [declarators.Count];
if (!expr_type.IsPointer){
if (!TypeManager.VerifyUnManaged (child.Type, loc))
return false;
+ if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
+ e.Error_ValueCannotBeConverted (e.Location, expr_type, false);
+ return false;
+ }
+
data [i] = new ExpressionEmitter (e, vi);
i++;
}
}
- public class Catch: Statement {
+ public class Catch : Statement {
public readonly string Name;
public readonly Block Block;
type = te.ResolveType (ec);
- CheckObsolete (type);
-
if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
Error (155, "The type caught or thrown must be derived from System.Exception");
return false;
Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
if (General != null){
+ if (CodeGen.Assembly.WrapNonExceptionThrows) {
+ foreach (Catch c in Specific){
+ if (c.CatchType == TypeManager.exception_type) {
+ Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
+ }
+ }
+ }
+
ec.CurrentBranching.CreateSibling (
General.Block, FlowBranching.SiblingType.Catch);
if (vi == null)
throw new Exception ("Variable does not exist in this block");
- ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
+ if (vi.IsCaptured){
+ LocalBuilder e = ig.DeclareLocal (vi.VariableType);
+ ig.Emit (OpCodes.Stloc, e);
+
+ ec.EmitCapturedVariableInstance (vi);
+ ig.Emit (OpCodes.Ldloc, e);
+ ig.Emit (OpCodes.Stfld, vi.FieldBuilder);
+ } else
+ ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
} else
ig.Emit (OpCodes.Pop);
public class Using : ExceptionStatement {
object expression_or_block;
- Statement Statement;
+ public Statement Statement;
ArrayList var_list;
Expression expr;
Type expr_type;
- Expression conv;
Expression [] resolved_vars;
Expression [] converted_vars;
ExpressionStatement [] assign;
continue;
}
- converted_vars [i] = Convert.ImplicitConversionRequired (
+ converted_vars [i] = Convert.ImplicitConversion (
ec, var, TypeManager.idisposable_type, loc);
- if (converted_vars [i] == null)
+ if (converted_vars [i] == null) {
+ Error_IsNotConvertibleToIDisposable ();
return false;
+ }
i++;
}
return true;
}
+ void Error_IsNotConvertibleToIDisposable ()
+ {
+ Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
+ TypeManager.CSharpName (expr_type));
+ }
+
bool ResolveExpression (EmitContext ec)
{
if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
- Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to 'System.IDisposable'",
- TypeManager.CSharpName (expr_type));
+ Error_IsNotConvertibleToIDisposable ();
return false;
}
}
MethodInfo mi = null;
foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
- if (TypeManager.GetArgumentTypes (mk).Length == 0) {
+ if (TypeManager.GetParameterData (mk).Count == 0) {
mi = mk;
break;
}
//
ILGenerator ig = ec.ig;
local_copy = ig.DeclareLocal (expr_type);
- if (conv != null)
- conv.Emit (ec);
- else
- expr.Emit (ec);
+
+ expr.Emit (ec);
ig.Emit (OpCodes.Stloc, local_copy);
if (emit_finally)
MethodInfo mi = null;
foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
- if (TypeManager.GetArgumentTypes (mk).Length == 0) {
+ if (TypeManager.GetParameterData (mk).Count == 0) {
mi = mk;
break;
}
statement = stmt;
loc = l;
}
-
+
+ public Statement Statement {
+ get { return statement; }
+ }
+
public override bool Resolve (EmitContext ec)
{
expr = expr.Resolve (ec);
if (expr == null)
return false;
- if (expr is NullLiteral) {
+ Constant c = expr as Constant;
+ if (c != null && c.GetValue () == null) {
Report.Error (186, loc, "Use of null is not valid in this context");
return false;
}
Type var_type = texpr.Type;
+ if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethod) {
+ Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
+ expr.ExprClassName);
+ return false;
+ }
+
//
// We need an instance variable. Not sure this is the best
// way of doing this.
//
if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
- collection.error1579 ();
+ collection.Error_Enumerator ();
return false;
}
ec.LoopEnd = old_end;
}
- protected class TemporaryVariable : Expression, IMemoryLocation
- {
- LocalInfo li;
-
- public TemporaryVariable (Type type, Location loc)
- {
- this.type = type;
- this.loc = loc;
- eclass = ExprClass.Value;
- }
-
- public override Expression DoResolve (EmitContext ec)
- {
- if (li != null)
- return this;
-
- TypeExpr te = new TypeExpression (type, loc);
- li = ec.CurrentBlock.AddTemporaryVariable (te, loc);
- if (!li.Resolve (ec))
- return null;
-
- AnonymousContainer am = ec.CurrentAnonymousMethod;
- if ((am != null) && am.IsIterator)
- ec.CaptureVariable (li);
-
- return this;
- }
-
- public override void Emit (EmitContext ec)
- {
- ILGenerator ig = ec.ig;
-
- if (li.FieldBuilder != null) {
- ig.Emit (OpCodes.Ldarg_0);
- ig.Emit (OpCodes.Ldfld, li.FieldBuilder);
- } else {
- ig.Emit (OpCodes.Ldloc, li.LocalBuilder);
- }
- }
-
- public void EmitLoadAddress (EmitContext ec)
- {
- ILGenerator ig = ec.ig;
-
- if (li.FieldBuilder != null) {
- ig.Emit (OpCodes.Ldarg_0);
- ig.Emit (OpCodes.Ldflda, li.FieldBuilder);
- } else {
- ig.Emit (OpCodes.Ldloca, li.LocalBuilder);
- }
- }
-
- public void Store (EmitContext ec, Expression right_side)
- {
- if (li.FieldBuilder != null)
- ec.ig.Emit (OpCodes.Ldarg_0);
-
- right_side.Emit (ec);
- if (li.FieldBuilder != null) {
- ec.ig.Emit (OpCodes.Stfld, li.FieldBuilder);
- } else {
- ec.ig.Emit (OpCodes.Stloc, li.LocalBuilder);
- }
- }
-
- public void EmitThis (EmitContext ec)
- {
- if (li.FieldBuilder != null) {
- ec.ig.Emit (OpCodes.Ldarg_0);
- }
- }
-
- public void EmitStore (ILGenerator ig)
- {
- if (li.FieldBuilder != null)
- ig.Emit (OpCodes.Stfld, li.FieldBuilder);
- else
- ig.Emit (OpCodes.Stloc, li.LocalBuilder);
- }
-
- public void AddressOf (EmitContext ec, AddressOp mode)
- {
- EmitLoadAddress (ec);
- }
- }
-
protected class ArrayCounter : TemporaryVariable
{
public ArrayCounter (Location loc)
{
Expression variable, expr, conv;
Statement statement;
- Type array_type, element_type;
+ Type array_type;
Type var_type;
TemporaryVariable[] lengths;
ArrayCounter[] counter;
public override bool Resolve (EmitContext ec)
{
array_type = expr.Type;
- element_type = TypeManager.GetElementType (array_type);
rank = array_type.GetArrayRank ();
copy = new TemporaryVariable (array_type, loc);
list.Add (counter [i]);
}
- access = new ElementAccess (copy, list, loc).Resolve (ec);
+ access = new ElementAccess (copy, list).Resolve (ec);
if (access == null)
return false;
MethodInfo move_next;
Type var_type, enumerator_type;
bool is_disposable;
+ bool enumerator_found;
public CollectionForeach (Type var_type, Expression var,
Expression expr, Statement stmt, Location l)
bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
{
- Type [] args = TypeManager.GetArgumentTypes (mi);
- if (args != null){
- if (args.Length != 0)
- return false;
- }
-
- if (TypeManager.IsOverride (mi))
- return false;
-
- // Check whether GetEnumerator is public
- if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
- return false;
+ Type return_type = mi.ReturnType;
- if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
+ if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
//
// Apply the same optimization as MS: skip the GetEnumerator
// returning an IEnumerator, and use the one returning a
// with this `GetEnumerator'
//
- Type return_type = mi.ReturnType;
- if (mi.ReturnType == TypeManager.ienumerator_type ||
+ if (return_type == TypeManager.ienumerator_type ||
TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
(!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
//
// find if they support the GetEnumerator pattern.
//
- if (!FetchMoveNext (ec, return_type))
- return false;
-
- if (!FetchGetCurrent (ec, return_type))
+ if (TypeManager.HasElementType (return_type) || !FetchMoveNext (ec, return_type) || !FetchGetCurrent (ec, return_type)) {
+ 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;
+ }
}
enumerator_type = return_type;
foreach (MemberInfo m in move_next_list){
MethodInfo mi = (MethodInfo) m;
- Type [] args;
- args = TypeManager.GetArgumentTypes (mi);
- if ((args != null) && (args.Length == 0) &&
+ if ((TypeManager.GetParameterData (mi).Count == 0) &&
TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
move_next = mi;
return true;
foreach (MemberInfo m in dispose_list){
MethodInfo mi = (MethodInfo) m;
- Type [] args;
- args = TypeManager.GetArgumentTypes (mi);
- if (args != null && args.Length == 0){
+ if (TypeManager.GetParameterData (mi).Count == 0){
if (mi.ReturnType == TypeManager.void_type)
return mi;
}
return null;
}
- public void error1579 ()
+ public void Error_Enumerator ()
{
+ if (enumerator_found) {
+ return;
+ }
+
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));
+ "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));
}
bool TryType (EmitContext ec, Type t)
return false;
foreach (MethodBase mb in mg.Methods) {
+ if (TypeManager.GetParameterData (mb).Count != 0)
+ continue;
+
+ // Check whether GetEnumerator is public
+ if ((mb.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
+ continue;
+
+ if (TypeManager.IsOverride (mb))
+ continue;
+
+ enumerator_found = true;
+
if (!GetEnumeratorFilter (ec, (MethodInfo) mb))
continue;
is_disposable = true;
if (!ProbeCollectionType (ec, expr.Type)) {
- error1579 ();
+ Error_Enumerator ();
return false;
}
enumerator = new TemporaryVariable (enumerator_type, loc);
enumerator.Resolve (ec);
- init = new Invocation (get_enumerator, new ArrayList (), loc);
+ init = new Invocation (get_enumerator, new ArrayList ());
init = init.Resolve (ec);
if (init == null)
return false;
MethodGroupExpr mg = new MethodGroupExpr (mi, loc);
mg.InstanceExpression = enumerator;
- move_next_expr = new Invocation (mg, new ArrayList (), loc);
+ move_next_expr = new Invocation (mg, new ArrayList ());
}
get_current.InstanceExpression = enumerator;