X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fstatement.cs;h=d717fba709339e2335e66f816595c62d231f2b4e;hb=29f95a7d2392761ca8aa5a0d45f598241b40f947;hp=072594233f2a525887c4b3f9013d31a32e96adce;hpb=e0069cd6e8bf8744303ffe12557c1702d9bbe36b;p=mono.git
diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs
index 072594233f2..d717fba7093 100644
--- a/mcs/mcs/statement.cs
+++ b/mcs/mcs/statement.cs
@@ -14,10 +14,10 @@ using System.Text;
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;
@@ -80,7 +80,7 @@ namespace Mono.CSharp {
public void Error (int error, string s)
{
- if (!Location.IsNull (loc))
+ if (!loc.IsNull)
Report.Error (error, loc, s);
else
Report.Error (error, s);
@@ -349,6 +349,8 @@ namespace Mono.CSharp {
}
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
+ if (!infinite)
+ ec.CurrentBranching.CreateSibling ();
if (!Statement.Resolve (ec))
ok = false;
@@ -408,7 +410,7 @@ namespace Mono.CSharp {
Expression Test;
readonly Statement InitStatement;
readonly Statement Increment;
- readonly Statement Statement;
+ public readonly Statement Statement;
bool infinite, empty;
public For (Statement initStatement,
@@ -525,10 +527,10 @@ namespace Mono.CSharp {
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)
@@ -565,30 +567,31 @@ namespace Mono.CSharp {
public override bool Resolve (EmitContext ec)
{
+ AnonymousContainer am = ec.CurrentAnonymousMethod;
+ if ((am != null) && am.IsIterator && ec.InIterator) {
+ Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
+ "statement to return a value, or yield break to end the iteration");
+ return false;
+ }
+
if (ec.ReturnType == null){
if (Expr != null){
if (ec.CurrentAnonymousMethod != null){
- Report.Error (1662, loc, String.Format (
- "Anonymous method could not be converted to delegate " +
- "since the return value does not match the delegate value"));
+ Report.Error (1662, loc,
+ "Cannot convert anonymous method block to delegate type `{0}' because some of the return types in the block are not implicitly convertible to the delegate return type",
+ ec.CurrentAnonymousMethod.GetSignatureForError ());
}
- Error (127, "Return with a value not allowed here");
+ Error (127, "A return keyword must not be followed by any expression when method returns void");
return false;
}
} else {
if (Expr == null){
- Error (126, "An object of type `{0}' is expected " +
+ Error (126, "An object of a type convertible to `{0}' is required " +
"for the return statement",
TypeManager.CSharpName (ec.ReturnType));
return false;
}
- if (ec.InIterator) {
- Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
- "statement to return a value, or yield break to end the iteration");
- return false;
- }
-
Expr = Expr.Resolve (ec);
if (Expr == null)
return false;
@@ -601,18 +604,13 @@ namespace Mono.CSharp {
}
}
- if (ec.InIterator){
- Error (-206, "Return statement not allowed inside iterators");
- return false;
- }
-
FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
if (ec.CurrentBranching.InTryOrCatch (true)) {
ec.CurrentBranching.AddFinallyVector (vector);
in_exc = true;
} else if (ec.InFinally) {
- Error (157, "Control can not leave the body of the finally block");
+ Error (157, "Control cannot leave the body of a finally clause");
return false;
} else
vector.CheckOutParameters (ec.CurrentBranching);
@@ -680,7 +678,6 @@ namespace Mono.CSharp {
}
public class LabeledStatement : Statement {
- public readonly Location Location;
bool defined;
bool referenced;
Label label;
@@ -690,7 +687,7 @@ namespace Mono.CSharp {
public LabeledStatement (string label_name, Location l)
{
- this.Location = l;
+ this.loc = l;
}
public Label LabelTarget (EmitContext ec)
@@ -733,7 +730,8 @@ namespace Mono.CSharp {
protected override void DoEmit (EmitContext ec)
{
if (ig != null && ig != ec.ig) {
- Report.Error (1632, "Control cannot leave body of anonymous method");
+ // TODO: location is wrong
+ Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
return;
}
LabelTarget (ec);
@@ -766,12 +764,12 @@ namespace Mono.CSharp {
protected override void DoEmit (EmitContext ec)
{
if (ec.Switch == null){
- Report.Error (153, loc, "goto default is only valid in a switch statement");
+ Report.Error (153, loc, "A goto case is only valid inside a switch statement");
return;
}
if (!ec.Switch.GotDefault){
- Report.Error (159, loc, "No default target on switch statement");
+ Report.Error (159, loc, "No such label `default:' within the scope of the goto statement");
return;
}
ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
@@ -794,7 +792,7 @@ namespace Mono.CSharp {
public override bool Resolve (EmitContext ec)
{
if (ec.Switch == null){
- Report.Error (153, loc, "goto case is only valid in a switch statement");
+ Report.Error (153, loc, "A goto case is only valid inside a switch statement");
return false;
}
@@ -802,23 +800,24 @@ namespace Mono.CSharp {
if (expr == null)
return false;
- if (!(expr is Constant)){
- Report.Error (159, loc, "Target expression for goto case is not 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 " + val + "': for the goto case");
+ Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", c.GetValue () == null ? "null" : val);
return false;
}
@@ -854,7 +853,7 @@ namespace Mono.CSharp {
if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
- expr.Error_UnexpectedKind ("value, variable, property or indexer access ", loc);
+ expr.Error_UnexpectedKind (ec, "value, variable, property or indexer access ", loc);
return false;
}
@@ -877,7 +876,7 @@ namespace Mono.CSharp {
}
if (ec.InFinally) {
- Error (724, "A throw statement with no argument is only allowed in a catch clause nested inside of the innermost catch clause");
+ Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
return false;
}
return true;
@@ -907,15 +906,15 @@ namespace Mono.CSharp {
public override bool Resolve (EmitContext ec)
{
if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
- Error (139, "No enclosing loop or switch to continue to");
+ Error (139, "No enclosing loop out of which to break or continue");
return false;
} else if (ec.InFinally && ec.CurrentBranching.BreakCrossesTryCatchBoundary()) {
- Error (157, "Control can not leave the body of the finally block");
+ Error (157, "Control cannot leave the body of a finally clause");
return false;
} else if (ec.CurrentBranching.InTryOrCatch (false))
ec.CurrentBranching.AddFinallyVector (
ec.CurrentBranching.CurrentUsageVector);
- else if (ec.CurrentBranching.InLoop ())
+ else if (ec.CurrentBranching.InLoop () || ec.CurrentBranching.InSwitch ())
ec.CurrentBranching.AddBreakVector (
ec.CurrentBranching.CurrentUsageVector);
@@ -951,11 +950,11 @@ namespace Mono.CSharp {
public override bool Resolve (EmitContext ec)
{
- if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
- Error (139, "No enclosing loop to continue to");
+ if (!ec.CurrentBranching.InLoop ()){
+ Error (139, "No enclosing loop out of which to break or continue");
return false;
} else if (ec.InFinally) {
- Error (157, "Control can not leave the body of the finally block");
+ Error (157, "Control cannot leave the body of a finally clause");
return false;
} else if (ec.CurrentBranching.InTryOrCatch (false))
ec.CurrentBranching.AddFinallyVector (ec.CurrentBranching.CurrentUsageVector);
@@ -1007,10 +1006,18 @@ namespace Mono.CSharp {
Pinned = 4,
IsThis = 8,
Captured = 16,
- AddressTaken = 32
+ AddressTaken = 32,
+ CompilerGenerated = 64
+ }
+
+ public enum ReadOnlyContext: byte {
+ Using,
+ Foreach,
+ Fixed
}
Flags flags;
+ ReadOnlyContext ro_context;
public LocalInfo (Expression type, string name, Block block, Location l)
{
@@ -1063,26 +1070,14 @@ namespace Mono.CSharp {
}
if (VariableType.IsAbstract && VariableType.IsSealed) {
- Report.Error (723, Location, "Cannot declare variable of static type '{0}'", TypeManager.CSharpName (VariableType));
+ Report.Error (723, Location, "Cannot declare variable of static type `{0}'", TypeManager.CSharpName (VariableType));
return false;
}
-// TODO: breaks the build
-// if (VariableType.IsPointer && !ec.InUnsafe)
-// Expression.UnsafeError (Location);
- return true;
- }
-
- //
- // Whether the variable is Fixed (because its Pinned or its a value type)
- //
- public bool IsFixed {
- get {
- if (((flags & Flags.Pinned) != 0) || TypeManager.IsValueType (VariableType))
- return true;
+ if (VariableType.IsPointer && !ec.InUnsafe)
+ Expression.UnsafeError (Location);
- return false;
- }
+ return true;
}
public bool IsCaptured {
@@ -1105,6 +1100,16 @@ namespace Mono.CSharp {
}
}
+ public bool CompilerGenerated {
+ get {
+ return (flags & Flags.CompilerGenerated) != 0;
+ }
+
+ set {
+ flags |= Flags.CompilerGenerated;
+ }
+ }
+
public override string ToString ()
{
return String.Format ("LocalInfo ({0},{1},{2},{3})",
@@ -1124,9 +1129,28 @@ namespace Mono.CSharp {
get {
return (flags & Flags.ReadOnly) != 0;
}
- set {
- flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly));
+ }
+
+ public void SetReadOnlyContext (ReadOnlyContext context)
+ {
+ flags |= Flags.ReadOnly;
+ ro_context = context;
+ }
+
+ public string GetReadOnlyContext ()
+ {
+ if (!ReadOnly)
+ throw new InternalErrorException ("Variable is not readonly");
+
+ switch (ro_context) {
+ case ReadOnlyContext.Fixed:
+ return "fixed variable";
+ case ReadOnlyContext.Foreach:
+ return "foreach iteration variable";
+ case ReadOnlyContext.Using:
+ return "using variable";
}
+ throw new NotImplementedException ();
}
//
@@ -1174,53 +1198,31 @@ namespace Mono.CSharp {
public readonly ToplevelBlock Toplevel;
[Flags]
- public enum Flags {
+ public enum Flags : ushort {
Implicit = 1,
Unchecked = 2,
BlockUsed = 4,
VariablesInitialized = 8,
HasRet = 16,
IsDestructor = 32,
- HasVarargs = 64,
- IsToplevel = 128,
- Unsafe = 256
+ IsToplevel = 64,
+ Unsafe = 128,
+ HasVarargs = 256 // Used in ToplevelBlock
}
- Flags flags;
+ protected Flags flags;
public bool Implicit {
- get {
- return (flags & Flags.Implicit) != 0;
- }
+ get { return (flags & Flags.Implicit) != 0; }
}
public bool Unchecked {
- get {
- return (flags & Flags.Unchecked) != 0;
- }
- set {
- flags |= Flags.Unchecked;
- }
+ get { return (flags & Flags.Unchecked) != 0; }
+ set { flags |= Flags.Unchecked; }
}
public bool Unsafe {
- get {
- return (flags & Flags.Unsafe) != 0;
- }
- set {
- flags |= Flags.Unsafe;
- }
- }
-
- public bool HasVarargs {
- get {
- if (Parent != null)
- return Parent.HasVarargs;
- else
- return (flags & Flags.HasVarargs) != 0;
- }
- set {
- flags |= Flags.HasVarargs;
- }
+ get { return (flags & Flags.Unsafe) != 0; }
+ set { flags |= Flags.Unsafe; }
}
//
@@ -1237,7 +1239,7 @@ namespace Mono.CSharp {
// statements.
//
ArrayList children;
-
+
//
// Labels. (label, block) pairs.
//
@@ -1251,6 +1253,11 @@ namespace Mono.CSharp {
//
// Keeps track of constants
Hashtable constants;
+
+ //
+ // Temporary variables.
+ //
+ ArrayList temporary_variables;
//
// If this is a switch section, the enclosing switch block.
@@ -1308,8 +1315,14 @@ namespace Mono.CSharp {
}
public int ID {
+ get { return this_id; }
+ }
+
+ protected Hashtable Variables {
get {
- return this_id;
+ if (variables == null)
+ variables = new Hashtable ();
+ return variables;
}
}
@@ -1344,7 +1357,7 @@ namespace Mono.CSharp {
while (cur != null) {
if (cur.DoLookupLabel (name) != null) {
Report.Error (
- 140, loc, "The label '{0}' is a duplicate",
+ 140, loc, "The label `{0}' is a duplicate",
name);
return false;
}
@@ -1359,8 +1372,8 @@ namespace Mono.CSharp {
if (cur.DoLookupLabel (name) != null) {
Report.Error (
158, loc,
- "The label '{0}' shadows another label " +
- "by the same name in a containing scope.",
+ "The label `{0}' shadows another label " +
+ "by the same name in a contained scope.",
name);
return false;
}
@@ -1372,10 +1385,10 @@ namespace Mono.CSharp {
continue;
Report.Error (
- 158, s.Location,
- "The label '{0}' shadows another " +
+ 158, s.loc,
+ "The label `{0}' shadows another " +
"label by the same name in a " +
- "containing scope.",
+ "contained scope.",
name);
return false;
}
@@ -1425,23 +1438,6 @@ namespace Mono.CSharp {
return null;
}
- LocalInfo this_variable = null;
-
- //
- // Returns the "this" instance variable of this block.
- // See AddThisVariable() for more information.
- //
- public LocalInfo ThisVariable {
- get {
- for (Block b = this; b != null; b = b.Parent) {
- if (b.this_variable != null)
- return b.this_variable;
- }
-
- return null;
- }
- }
-
Hashtable known_variables;
//
@@ -1466,94 +1462,91 @@ namespace Mono.CSharp {
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}' has a different meaning 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);
- Report.Error (136, loc, "'{0}' has a different meaning later in the block", name);
+ //
+ // 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;
}
- //
- // This is used by non-static `struct' constructors which do not have an
- // initializer - in this case, the constructor must initialize all of the
- // struct's fields. To do this, we add a "this" variable and use the flow
- // analysis code to ensure that it's been fully initialized before control
- // leaves the constructor.
- //
- public LocalInfo AddThisVariable (TypeContainer tc, Location l)
- {
- if (this_variable != null)
- return this_variable;
-
- if (variables == null)
- variables = new Hashtable ();
-
- this_variable = new LocalInfo (tc, this, l);
- this_variable.Used = true;
- this_variable.IsThis = true;
-
- variables.Add ("this", this_variable);
-
- return this_variable;
- }
-
public LocalInfo AddVariable (Expression type, string name, Location l)
{
- if (variables == null)
- variables = new Hashtable ();
-
LocalInfo vi = GetLocalInfo (name);
if (vi != null) {
Report.SymbolRelatedToPreviousError (vi.Location, name);
if (known_variables == vi.Block.known_variables)
Report.Error (128, l,
- "A local variable '{0}' is already declared in this scope", name);
+ "A local variable named `{0}' is already defined in this scope", name);
else
- Report.Error (136, l,
- "'{0}' hides the declaration of local variable '{0}' in a parent scope", name);
+ Error_AlreadyDeclared (l, name, "parent");
return null;
}
vi = GetKnownVariableInfo (name);
if (vi != null) {
Report.SymbolRelatedToPreviousError (vi.Location, name);
- Report.Error (136, l,
- "A child block already has a declaration of local variable '{0}':" +
- " allowing this declaration would violate 'invariant meaning in a block'",
- name);
+ Error_AlreadyDeclared (l, name, "child");
return null;
}
int idx;
Parameter p = Toplevel.Parameters.GetParameterByName (name, out idx);
if (p != null) {
- Report.SymbolRelatedToPreviousError (Toplevel.Parameters.Location, name);
- Report.Error (136, l, "'{0}' hides a method parameter", name);
+ Report.SymbolRelatedToPreviousError (p.Location, name);
+ Error_AlreadyDeclared (l, name, "method argument");
return null;
}
vi = new LocalInfo (type, name, this, l);
- variables.Add (name, vi);
+ Variables.Add (name, vi);
for (Block b = this; b != null; b = b.Parent)
b.AddKnownVariable (name, vi);
@@ -1561,10 +1554,15 @@ namespace Mono.CSharp {
if ((flags & Flags.VariablesInitialized) != 0)
throw new Exception ();
- // Console.WriteLine ("Adding {0} to {1}", name, ID);
return vi;
}
+ void Error_AlreadyDeclared (Location loc, string var, string reason)
+ {
+ Report.Error (136, loc, "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning to `{0}', " +
+ "which is already used in a `{1}' scope", var, reason);
+ }
+
public bool AddConstant (Expression type, string name, Expression value, Location l)
{
if (AddVariable (type, name, l) == null)
@@ -1577,10 +1575,20 @@ namespace Mono.CSharp {
return true;
}
- public Hashtable Variables {
- get {
- return variables;
- }
+ static int next_temp_id = 0;
+
+ public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
+ {
+ if (temporary_variables == null)
+ temporary_variables = new ArrayList ();
+
+ int id = ++next_temp_id;
+ string name = "$s_" + id.ToString ();
+
+ LocalInfo li = new LocalInfo (te, name, this, loc);
+ li.CompilerGenerated = true;
+ temporary_variables.Add (li);
+ return li;
}
public LocalInfo GetLocalInfo (string name)
@@ -1598,11 +1606,7 @@ namespace Mono.CSharp {
public Expression GetVariableType (string name)
{
LocalInfo vi = GetLocalInfo (name);
-
- if (vi != null)
- return vi.Type;
-
- return null;
+ return vi == null ? null : vi.Type;
}
public Expression GetConstantExpression (string name)
@@ -1622,65 +1626,10 @@ namespace Mono.CSharp {
///
public bool IsConstant (string name)
{
- Expression e = null;
-
- e = GetConstantExpression (name);
-
+ Expression e = GetConstantExpression (name);
return e != null;
}
- //
- // Returns a `ParameterReference' for the given name, or null if there
- // is no such parameter
- //
- public ParameterReference GetParameterReference (string name, Location loc)
- {
- Parameter par;
- int idx;
-
- for (Block b = this; b != null; b = b.Toplevel.Parent) {
- Parameters pars = b.Toplevel.Parameters;
- par = pars.GetParameterByName (name, out idx);
- if (par != null)
- return new ParameterReference (pars, this, idx, name, loc);
- }
- return null;
- }
-
- //
- // Whether the parameter named `name' is local to this block,
- // or false, if the parameter belongs to an encompassing block.
- //
- public bool IsLocalParameter (string name)
- {
- return Toplevel.Parameters.GetParameterByName (name) != null;
- }
-
- //
- // Whether the `name' is a parameter reference
- //
- public bool IsParameterReference (string name)
- {
- Parameter par;
- int idx;
-
- for (Block b = this; b != null; b = b.Toplevel.Parent) {
- par = b.Toplevel.Parameters.GetParameterByName (name, out idx);
- if (par != null)
- return true;
- }
- return false;
- }
-
- ///
- /// A list of labels that were not used within this block
- ///
- public string [] GetUnreferenced ()
- {
- // FIXME: Implement me
- return null;
- }
-
public void AddStatement (Statement s)
{
statements.Add (s);
@@ -1688,9 +1637,7 @@ namespace Mono.CSharp {
}
public bool Used {
- get {
- return (flags & Flags.BlockUsed) != 0;
- }
+ get { return (flags & Flags.BlockUsed) != 0; }
}
public void Use ()
@@ -1699,15 +1646,11 @@ namespace Mono.CSharp {
}
public bool HasRet {
- get {
- return (flags & Flags.HasRet) != 0;
- }
+ get { return (flags & Flags.HasRet) != 0; }
}
public bool IsDestructor {
- get {
- return (flags & Flags.IsDestructor) != 0;
- }
+ get { return (flags & Flags.IsDestructor) != 0; }
}
public void SetDestructor ()
@@ -1802,19 +1745,6 @@ namespace Mono.CSharp {
continue;
}
-#if false
- if (remap_locals)
- vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
- else if (vi.Pinned)
- //
- // This is needed to compile on both .NET 1.x and .NET 2.x
- // the later introduced `DeclareLocal (Type t, bool pinned)'
- //
- vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
- else if (!vi.IsThis)
- vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
-#endif
-
if (constants == null)
continue;
@@ -1824,22 +1754,16 @@ namespace Mono.CSharp {
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 `" +
- name + "' must be constant (" + e + ")");
+ 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);
@@ -1866,7 +1790,6 @@ namespace Mono.CSharp {
if (variables != null){
bool have_captured_vars = ec.HaveCapturedVariables ();
- bool remap_locals = ec.RemapToProxy;
foreach (DictionaryEntry de in variables){
LocalInfo vi = (LocalInfo) de.Value;
@@ -1874,18 +1797,32 @@ namespace Mono.CSharp {
if (have_captured_vars && ec.IsCaptured (vi))
continue;
- if (remap_locals){
- vi.FieldBuilder = ec.MapVariable (vi.Name, vi.VariableType);
- } else {
- if (vi.Pinned)
- //
- // This is needed to compile on both .NET 1.x and .NET 2.x
- // the later introduced `DeclareLocal (Type t, bool pinned)'
- //
- vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
- else if (!vi.IsThis)
- vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
- }
+ if (vi.Pinned)
+ //
+ // This is needed to compile on both .NET 1.x and .NET 2.x
+ // the later introduced `DeclareLocal (Type t, bool pinned)'
+ //
+ vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
+ else if (!vi.IsThis)
+ vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
+ }
+ }
+
+ if (temporary_variables != null) {
+ AnonymousContainer am = ec.CurrentAnonymousMethod;
+ TypeBuilder scope = null;
+ if ((am != null) && am.IsIterator) {
+ scope = am.Scope.ScopeTypeBuilder;
+ if (scope == null)
+ throw new InternalErrorException ();
+ }
+ foreach (LocalInfo vi in temporary_variables) {
+ if (scope != null) {
+ if (vi.FieldBuilder == null)
+ vi.FieldBuilder = scope.DefineField (
+ vi.Name, vi.VariableType, FieldAttributes.Assembly);
+ } else
+ vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
}
}
@@ -1909,15 +1846,46 @@ namespace Mono.CSharp {
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, 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, 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)
{
@@ -1931,44 +1899,47 @@ namespace Mono.CSharp {
Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
- //
- // This flag is used to notate nested statements as unreachable from the beginning of this block.
- // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
- // from the beginning of the function. The outer Resolve() that detected the unreachability is
- // responsible for handling the situation.
- //
- bool unreachable = false;
-
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);
- if (unreachable && !(s is LabeledStatement)) {
- if (s == EmptyStatement.Value)
- s.loc = EndLocation;
-
- if (!s.ResolveUnreachable (ec, !unreachable_shown))
- ok = false;
+ //
+ // Warn if we detect unreachable code.
+ //
+ if (unreachable) {
+ if (s is Block)
+ ((Block) s).unreachable = true;
- if (s != EmptyStatement.Value)
+ if (!unreachable_shown && (RootContext.WarningLevel >= 2)) {
+ Report.Warning (
+ 162, s.loc, "Unreachable code detected");
unreachable_shown = true;
- else
- s.loc = Location.Null;
-
- if (ok && !(s is Block)) {
- statements [ix] = EmptyStatement.Value;
- continue;
}
}
- if (s.Resolve (ec) == false) {
- ok = false;
+ //
+ // Note that we're not using ResolveUnreachable() for unreachable
+ // statements here. ResolveUnreachable() creates a temporary
+ // flow branching and kills it afterwards. This leads to problems
+ // if you have two unreachable statements where the first one
+ // assigns a variable and the second one tries to access it.
+ //
+
+ if (!s.Resolve (ec)) {
+ ok = false;
statements [ix] = EmptyStatement.Value;
continue;
}
- num_statements = ix + 1;
+ if (unreachable && !(s is LabeledStatement) && !(s is Block))
+ statements [ix] = EmptyStatement.Value;
+ num_statements = ix + 1;
if (s is LabeledStatement)
unreachable = false;
else
@@ -1978,22 +1949,21 @@ namespace Mono.CSharp {
Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
ec.CurrentBranching, statement_count, num_statements);
-
FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
ec.CurrentBlock = prev_block;
// If we're a non-static `struct' constructor which doesn't have an
// initializer, then we must initialize all of the struct's fields.
- if ((this_variable != null) &&
- (vector.Reachability.Throws != FlowBranching.FlowReturns.Always) &&
- !this_variable.IsThisAssigned (ec, loc))
+ if ((flags & Flags.IsToplevel) != 0 &&
+ !Toplevel.IsThisAssigned (ec) &&
+ vector.Reachability.Throws != FlowBranching.FlowReturns.Always)
ok = false;
if ((labels != null) && (RootContext.WarningLevel >= 2)) {
foreach (LabeledStatement label in labels.Values)
if (!label.HasBeenReferenced)
- Report.Warning (164, label.Location,
+ Report.Warning (164, label.loc,
"This label has not been referenced");
}
@@ -2015,13 +1985,11 @@ namespace Mono.CSharp {
public override bool ResolveUnreachable (EmitContext ec, bool warn)
{
unreachable_shown = true;
+ unreachable = true;
if (warn && (RootContext.WarningLevel >= 2))
Report.Warning (162, loc, "Unreachable code detected");
- if (Implicit)
- return Resolve (ec);
-
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
ec.KillFlowBranching ();
@@ -2096,6 +2064,11 @@ namespace Mono.CSharp {
} while (current != null);
return false;
}
+
+ public override string ToString ()
+ {
+ return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
+ }
}
//
@@ -2111,11 +2084,17 @@ namespace Mono.CSharp {
// Pointer to the host of this anonymous method, or null
// if we are the topmost block
//
- public ToplevelBlock Container;
+ ToplevelBlock container;
CaptureContext capture_context;
FlowBranching top_level_branching;
Hashtable capture_contexts;
+ ArrayList children;
+
+ public bool HasVarargs {
+ get { return (flags & Flags.HasVarargs) != 0; }
+ set { flags |= Flags.HasVarargs; }
+ }
//
// The parameters for the block.
@@ -2138,13 +2117,23 @@ namespace Mono.CSharp {
cc.AdjustScopes ();
}
}
-
+
public CaptureContext ToplevelBlockCaptureContext {
- get {
- return capture_context;
- }
+ get { return capture_context; }
}
-
+
+ public ToplevelBlock Container {
+ get { return container; }
+ }
+
+ protected void AddChild (ToplevelBlock block)
+ {
+ if (children == null)
+ children = new ArrayList ();
+
+ children.Add (block);
+ }
+
//
// Parent is only used by anonymous blocks to link back to their
// parents
@@ -2168,29 +2157,123 @@ namespace Mono.CSharp {
base (null, flags | Flags.IsToplevel, start, Location.Null)
{
Parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
- Container = container;
+ this.container = container;
+
+ if (container != null)
+ container.AddChild (this);
}
public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
{
}
- public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host)
+ public void SetHaveAnonymousMethods (Location loc, AnonymousContainer host)
{
if (capture_context == null)
capture_context = new CaptureContext (this, loc, host);
}
public CaptureContext CaptureContext {
- get {
- return capture_context;
- }
+ get { return capture_context; }
}
public FlowBranching TopLevelBranching {
- get {
- return top_level_branching;
+ get { return top_level_branching; }
+ }
+
+ //
+ // This is used if anonymous methods are used inside an iterator
+ // (see 2test-22.cs for an example).
+ //
+ // The AnonymousMethod is created while parsing - at a time when we don't
+ // know yet that we're inside an iterator, so it's `Container' is initially
+ // null. Later on, when resolving the iterator, we need to move the
+ // anonymous method into that iterator.
+ //
+ public void ReParent (ToplevelBlock new_parent, AnonymousContainer new_host)
+ {
+ foreach (ToplevelBlock block in children) {
+ if (block.CaptureContext == null)
+ continue;
+
+ block.container = new_parent;
+ block.CaptureContext.ReParent (new_parent, new_host);
+ }
+ }
+
+ //
+ // Returns a `ParameterReference' for the given name, or null if there
+ // is no such parameter
+ //
+ public ParameterReference GetParameterReference (string name, Location loc)
+ {
+ Parameter par;
+ int idx;
+
+ for (ToplevelBlock t = this; t != null; t = t.Container) {
+ Parameters pars = t.Parameters;
+ par = pars.GetParameterByName (name, out idx);
+ if (par != null)
+ return new ParameterReference (pars, this, idx, name, loc);
+ }
+ return null;
+ }
+
+ //
+ // Whether the parameter named `name' is local to this block,
+ // or false, if the parameter belongs to an encompassing block.
+ //
+ public bool IsLocalParameter (string name)
+ {
+ return Parameters.GetParameterByName (name) != null;
+ }
+
+ //
+ // Whether the `name' is a parameter reference
+ //
+ public bool IsParameterReference (string name)
+ {
+ for (ToplevelBlock t = this; t != null; t = t.Container) {
+ if (t.IsLocalParameter (name))
+ return true;
+ }
+ return false;
+ }
+
+ LocalInfo this_variable = null;
+
+ //
+ // Returns the "this" instance variable of this block.
+ // See AddThisVariable() for more information.
+ //
+ public LocalInfo ThisVariable {
+ get { return this_variable; }
+ }
+
+
+ //
+ // This is used by non-static `struct' constructors which do not have an
+ // initializer - in this case, the constructor must initialize all of the
+ // struct's fields. To do this, we add a "this" variable and use the flow
+ // analysis code to ensure that it's been fully initialized before control
+ // leaves the constructor.
+ //
+ public LocalInfo AddThisVariable (TypeContainer tc, Location l)
+ {
+ if (this_variable == null) {
+ this_variable = new LocalInfo (tc, this, l);
+ this_variable.Used = true;
+ this_variable.IsThis = true;
+
+ Variables.Add ("this", this_variable);
}
+
+ return this_variable;
+ }
+
+ public bool IsThisAssigned (EmitContext ec)
+ {
+ return this_variable == null || this_variable.IsThisAssigned (ec, loc);
}
public bool ResolveMeta (EmitContext ec, InternalParameters ip)
@@ -2211,13 +2294,15 @@ namespace Mono.CSharp {
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.
//
@@ -2262,33 +2347,43 @@ namespace Mono.CSharp {
// 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 && e is NullLiteral) {
+ 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 is NullLiteral)
+ label = "null";
+ else
+ label = converted.ToString ();
+
+ Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
+ }
}
public class SwitchSection {
@@ -2310,7 +2405,7 @@ namespace Mono.CSharp {
///
/// Maps constants whose type type SwitchType to their SwitchLabels.
///
- public Hashtable Elements;
+ public IDictionary Elements;
///
/// The governing switch type
@@ -2320,11 +2415,11 @@ namespace Mono.CSharp {
//
// Computed
//
- bool got_default;
Label default_target;
Expression new_expr;
bool is_constant;
SwitchSection constant_section;
+ SwitchSection default_section;
//
// The types allowed to be implicitly cast from
@@ -2341,7 +2436,7 @@ namespace Mono.CSharp {
public bool GotDefault {
get {
- return got_default;
+ return default_section != null;
}
}
@@ -2359,33 +2454,33 @@ namespace Mono.CSharp {
//
Expression SwitchGoverningType (EmitContext ec, Type t)
{
- if (t == TypeManager.int32_type ||
- t == TypeManager.uint32_type ||
- t == TypeManager.char_type ||
- t == TypeManager.byte_type ||
+ if (t == TypeManager.byte_type ||
t == TypeManager.sbyte_type ||
t == TypeManager.ushort_type ||
t == TypeManager.short_type ||
+ t == TypeManager.uint32_type ||
+ t == TypeManager.int32_type ||
t == TypeManager.uint64_type ||
t == TypeManager.int64_type ||
+ t == TypeManager.char_type ||
t == TypeManager.string_type ||
- t == TypeManager.bool_type ||
- t.IsSubclassOf (TypeManager.enum_type))
+ t == TypeManager.bool_type ||
+ t.IsSubclassOf (TypeManager.enum_type))
return Expr;
if (allowed_types == null){
allowed_types = new Type [] {
- TypeManager.int32_type,
- TypeManager.uint32_type,
TypeManager.sbyte_type,
TypeManager.byte_type,
TypeManager.short_type,
TypeManager.ushort_type,
+ TypeManager.int32_type,
+ TypeManager.uint32_type,
TypeManager.int64_type,
TypeManager.uint64_type,
TypeManager.char_type,
- TypeManager.bool_type,
- TypeManager.string_type
+ TypeManager.string_type,
+ TypeManager.bool_type
};
}
@@ -2407,32 +2502,22 @@ namespace Mono.CSharp {
// Ignore over-worked ImplicitUserConversions that do
// an implicit conversion in addition to the user conversion.
//
- if (e is UserCast){
- UserCast ue = e as UserCast;
+ if (!(e is UserCast))
+ continue;
- if (ue.Source != Expr)
- e = null;
- }
-
if (converted != null){
Report.ExtraInformation (
loc,
String.Format ("reason: more than one conversion to an integral type exist for type {0}",
TypeManager.CSharpName (Expr.Type)));
return null;
- } else {
- converted = e;
}
+
+ converted = e;
}
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).
@@ -2442,143 +2527,38 @@ namespace Mono.CSharp {
//
bool CheckSwitch (EmitContext ec)
{
- Type compare_type;
bool error = false;
- Elements = new Hashtable ();
+ Elements = Sections.Count > 10 ?
+ (IDictionary)new Hashtable () :
+ (IDictionary)new ListDictionary ();
- got_default = false;
-
- 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 (got_default){
- Report.Error (152, sl.loc, Error152, "default");
+ if (default_section != null){
+ sl.Erorr_AlreadyOccurs ();
error = true;
}
- got_default = 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)
@@ -2871,15 +2851,14 @@ namespace Mono.CSharp {
Label end_of_switch = ig.DefineLabel ();
Label next_test = ig.DefineLabel ();
Label null_target = ig.DefineLabel ();
- bool default_found = false;
bool first_test = true;
bool pending_goto_end = false;
+ bool null_marked = false;
bool null_found;
- bool default_at_end = false;
-
+
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);
@@ -2891,15 +2870,18 @@ namespace Mono.CSharp {
int section_count = Sections.Count;
for (int section = 0; section < section_count; section++){
SwitchSection ss = (SwitchSection) Sections [section];
+
+ if (ss == default_section)
+ continue;
+
Label sec_begin = ig.DefineLabel ();
- default_at_end = false;
+ ig.Emit (OpCodes.Nop);
if (pending_goto_end)
ig.Emit (OpCodes.Br, end_of_switch);
int label_count = ss.Labels.Count;
- bool mark_default = false;
null_found = false;
for (int label = 0; label < label_count; label++){
SwitchLabel sl = (SwitchLabel) ss.Labels [label];
@@ -2912,25 +2894,18 @@ namespace Mono.CSharp {
//
// If we are the default target
//
- if (sl.Label == null){
- if (label+1 == label_count)
- default_at_end = true;
- mark_default = true;
- default_found = true;
- } else {
+ 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 {
@@ -2941,24 +2916,24 @@ namespace Mono.CSharp {
}
}
}
- if (null_found)
+ if (null_found) {
ig.MarkLabel (null_target);
+ null_marked = true;
+ }
ig.MarkLabel (sec_begin);
foreach (SwitchLabel sl in ss.Labels)
ig.MarkLabel (sl.GetILLabelCode (ec));
- if (mark_default)
- ig.MarkLabel (default_target);
ss.Block.Emit (ec);
pending_goto_end = !ss.Block.HasRet;
first_test = false;
}
ig.MarkLabel (next_test);
- if (default_found){
- if (!default_at_end)
- ig.Emit (OpCodes.Br, default_target);
- } else
- ig.MarkLabel (default_target);
+ ig.MarkLabel (default_target);
+ if (!null_marked)
+ ig.MarkLabel (null_target);
+ if (default_section != null)
+ default_section.Block.Emit (ec);
ig.MarkLabel (end_of_switch);
}
@@ -2982,7 +2957,7 @@ namespace Mono.CSharp {
new_expr = SwitchGoverningType (ec, Expr.Type);
if (new_expr == null){
- Report.Error (151, loc, "An integer type or string was expected for switch");
+ Report.Error (151, loc, "A value of an integral type or string expected for switch");
return false;
}
@@ -3005,6 +2980,8 @@ namespace Mono.CSharp {
SwitchLabel label = (SwitchLabel) Elements [key];
constant_section = FindSection (label);
+ if (constant_section == null)
+ constant_section = default_section;
}
bool first = true;
@@ -3028,7 +3005,7 @@ namespace Mono.CSharp {
}
}
- if (!got_default)
+ if (default_section == null)
ec.CurrentBranching.CreateSibling (
null, FlowBranching.SiblingType.SwitchSection);
@@ -3096,7 +3073,7 @@ namespace Mono.CSharp {
{
if (emit_finally)
ec.ig.BeginFinallyBlock ();
- else
+ else if (ec.InIterator)
ec.CurrentIterator.MarkFinally (ec, parent_vectors);
EmitFinally (ec);
}
@@ -3111,7 +3088,7 @@ namespace Mono.CSharp {
public class Lock : ExceptionStatement {
Expression expr;
- Statement Statement;
+ public Statement Statement;
LocalBuilder temp;
public Lock (Expression expr, Statement stmt, Location l)
@@ -3128,9 +3105,9 @@ namespace Mono.CSharp {
return false;
if (expr.Type.IsValueType){
- Error (185, "lock statement requires the expression to be " +
- " a reference type (type is: `{0}'",
- TypeManager.CSharpName (expr.Type));
+ Report.Error (185, loc,
+ "`{0}' is not a reference type as required by the lock statement",
+ TypeManager.CSharpName (expr.Type));
return false;
}
@@ -3382,6 +3359,10 @@ namespace Mono.CSharp {
loc = l;
}
+ public Statement Statement {
+ get { return statement; }
+ }
+
public override bool Resolve (EmitContext ec)
{
if (!ec.InUnsafe){
@@ -3397,15 +3378,10 @@ namespace Mono.CSharp {
CheckObsolete (expr_type);
- if (ec.RemapToProxy){
- Report.Error (-210, loc, "Fixed statement not allowed in iterators");
- return false;
- }
-
data = new Emitter [declarators.Count];
if (!expr_type.IsPointer){
- Report.Error (209, loc, "Variables in a fixed statement must be pointers");
+ Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
return false;
}
@@ -3415,7 +3391,7 @@ namespace Mono.CSharp {
Expression e = (Expression) p.Second;
vi.VariableInfo.SetAssigned (ec);
- vi.ReadOnly = true;
+ vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
//
// The rules for the possible declarators are pretty wise,
@@ -3429,7 +3405,7 @@ namespace Mono.CSharp {
//
if (e is Cast){
- Report.Error (254, loc, "Cast expression not allowed as right hand expression in fixed statement");
+ Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
return false;
}
@@ -3697,7 +3673,7 @@ namespace Mono.CSharp {
Type resolvedType = c.CatchType;
for (int ii = 0; ii < last_index; ++ii) {
if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
- Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type '{0}'", prevCatches [ii].FullName);
+ Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prevCatches [ii].FullName);
return false;
}
}
@@ -3733,10 +3709,16 @@ namespace Mono.CSharp {
if (!Fini.Resolve (ec))
ok = false;
ec.InFinally = was_finally;
+
+ if (!ec.InIterator)
+ need_exc_block = true;
}
- ResolveFinally (branching);
- need_exc_block |= emit_finally;
+ if (ec.InIterator) {
+ ResolveFinally (branching);
+ need_exc_block |= emit_finally;
+ } else
+ emit_finally = Fini != null;
FlowBranching.Reachability reachability = ec.EndFlowBranching ();
@@ -3775,6 +3757,11 @@ namespace Mono.CSharp {
throw new Exception ("Variable does not exist in this block");
ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
+ if (vi.IsCaptured){
+ ec.EmitCapturedVariableInstance (vi);
+ ig.Emit (OpCodes.Ldloc, vi.LocalBuilder);
+ ig.Emit (OpCodes.Stfld, vi.FieldBuilder);
+ }
} else
ig.Emit (OpCodes.Pop);
@@ -3794,9 +3781,8 @@ namespace Mono.CSharp {
public override void EmitFinally (EmitContext ec)
{
- if (Fini != null){
+ if (Fini != null)
Fini.Emit (ec);
- }
}
public bool HasCatch
@@ -3809,7 +3795,7 @@ namespace Mono.CSharp {
public class Using : ExceptionStatement {
object expression_or_block;
- Statement Statement;
+ public Statement Statement;
ArrayList var_list;
Expression expr;
Type expr_type;
@@ -3853,7 +3839,7 @@ namespace Mono.CSharp {
foreach (DictionaryEntry e in var_list){
Expression var = (Expression) e.Key;
- var = var.ResolveLValue (ec, new EmptyExpression ());
+ var = var.ResolveLValue (ec, new EmptyExpression (), loc);
if (var == null)
return false;
@@ -3897,7 +3883,7 @@ namespace Mono.CSharp {
{
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'",
+ Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
TypeManager.CSharpName (expr_type));
return false;
}
@@ -4105,16 +4091,13 @@ namespace Mono.CSharp {
///
/// Implementation of the foreach C# statement
///
- public class Foreach : ExceptionStatement {
+ public class Foreach : Statement {
Expression type;
Expression variable;
Expression expr;
Statement statement;
- ForeachHelperMethods hm;
- Expression empty, conv;
- Type array_type, element_type;
- Type var_type;
- VariableStorage enumerator;
+ ArrayForeach array;
+ CollectionForeach collection;
public Foreach (Expression type, LocalVariableReference var, Expression expr,
Statement stmt, Location l)
@@ -4125,7 +4108,11 @@ namespace Mono.CSharp {
statement = stmt;
loc = l;
}
-
+
+ public Statement Statement {
+ get { return statement; }
+ }
+
public override bool Resolve (EmitContext ec)
{
expr = expr.Resolve (ec);
@@ -4133,7 +4120,7 @@ namespace Mono.CSharp {
return false;
if (expr is NullLiteral) {
- Report.Error (186, expr.Location, "Use of null is not valid in this context");
+ Report.Error (186, loc, "Use of null is not valid in this context");
return false;
}
@@ -4141,8 +4128,8 @@ namespace Mono.CSharp {
if (texpr == null)
return false;
- var_type = texpr.ResolveType (ec);
-
+ Type var_type = texpr.Type;
+
//
// We need an instance variable. Not sure this is the best
// way of doing this.
@@ -4152,642 +4139,665 @@ namespace Mono.CSharp {
//
if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
- error1579 (expr.Type);
+ collection.error1579 ();
return false;
}
if (expr.Type.IsArray) {
- array_type = expr.Type;
- element_type = TypeManager.GetElementType (array_type);
-
- empty = new EmptyExpression (element_type);
+ array = new ArrayForeach (var_type, variable, expr, statement, loc);
+ return array.Resolve (ec);
} else {
- hm = ProbeCollectionType (ec, expr.Type);
- if (hm == null){
- error1579 (expr.Type);
- return false;
- }
-
- // When ProbeCollection reported error
- if (hm.move_next == null)
- return false;
-
- array_type = expr.Type;
- element_type = hm.element_type;
-
- empty = new EmptyExpression (hm.element_type);
+ collection = new CollectionForeach (
+ var_type, variable, expr, statement, loc);
+ return collection.Resolve (ec);
}
+ }
- bool ok = true;
+ protected override void DoEmit (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
+ ec.LoopBegin = ig.DefineLabel ();
+ ec.LoopEnd = ig.DefineLabel ();
- ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
- ec.CurrentBranching.CreateSibling ();
+ if (collection != null)
+ collection.Emit (ec);
+ else
+ array.Emit (ec);
+
+ ec.LoopBegin = old_begin;
+ ec.LoopEnd = old_end;
+ }
- //
- //
- // FIXME: maybe we can apply the same trick we do in the
- // array handling to avoid creating empty and conv in some cases.
- //
- // Although it is not as important in this case, as the type
- // will not likely be object (what the enumerator will return).
- //
- conv = Convert.ExplicitConversion (ec, empty, var_type, loc);
- if (conv == null)
- ok = false;
+ protected class TemporaryVariable : Expression, IMemoryLocation
+ {
+ LocalInfo li;
- variable = variable.ResolveLValue (ec, empty);
- if (variable == null)
- ok = false;
+ public TemporaryVariable (Type type, Location loc)
+ {
+ this.type = type;
+ this.loc = loc;
+ eclass = ExprClass.Value;
+ }
- bool disposable = (hm != null) && hm.is_disposable;
- FlowBranchingException branching = null;
- if (disposable)
- branching = ec.StartFlowBranching (this);
+ public override Expression DoResolve (EmitContext ec)
+ {
+ if (li != null)
+ return this;
- if (!statement.Resolve (ec))
- ok = false;
+ TypeExpr te = new TypeExpression (type, loc);
+ li = ec.CurrentBlock.AddTemporaryVariable (te, loc);
+ if (!li.Resolve (ec))
+ return null;
- if (disposable) {
- ResolveFinally (branching);
- ec.EndFlowBranching ();
- } else
- emit_finally = true;
+ AnonymousContainer am = ec.CurrentAnonymousMethod;
+ if ((am != null) && am.IsIterator)
+ ec.CaptureVariable (li);
- ec.EndFlowBranching ();
+ return this;
+ }
- return ok;
- }
-
- //
- // Retrieves a `public bool MoveNext ()' method from the Type `t'
- //
- static MethodInfo FetchMethodMoveNext (Type t)
- {
- MemberList move_next_list;
-
- move_next_list = TypeContainer.FindMembers (
- t, MemberTypes.Method,
- BindingFlags.Public | BindingFlags.Instance,
- Type.FilterName, "MoveNext");
- if (move_next_list.Count == 0)
- return null;
+ public override void Emit (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
- foreach (MemberInfo m in move_next_list){
- MethodInfo mi = (MethodInfo) m;
- Type [] args;
-
- args = TypeManager.GetArgumentTypes (mi);
- if (args != null && args.Length == 0){
- if (mi.ReturnType == TypeManager.bool_type)
- return mi;
+ if (li.FieldBuilder != null) {
+ ig.Emit (OpCodes.Ldarg_0);
+ ig.Emit (OpCodes.Ldfld, li.FieldBuilder);
+ } else {
+ ig.Emit (OpCodes.Ldloc, li.LocalBuilder);
}
}
- return null;
- }
-
- //
- // Retrieves a `public T get_Current ()' method from the Type `t'
- //
- static MethodInfo FetchMethodGetCurrent (Type t)
- {
- MemberList get_current_list;
-
- get_current_list = TypeContainer.FindMembers (
- t, MemberTypes.Method,
- BindingFlags.Public | BindingFlags.Instance,
- Type.FilterName, "get_Current");
- if (get_current_list.Count == 0)
- return null;
- foreach (MemberInfo m in get_current_list){
- MethodInfo mi = (MethodInfo) m;
- Type [] args;
+ public void EmitLoadAddress (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
- args = TypeManager.GetArgumentTypes (mi);
- if (args != null && args.Length == 0)
- return mi;
+ if (li.FieldBuilder != null) {
+ ig.Emit (OpCodes.Ldarg_0);
+ ig.Emit (OpCodes.Ldflda, li.FieldBuilder);
+ } else {
+ ig.Emit (OpCodes.Ldloca, li.LocalBuilder);
+ }
}
- return null;
- }
- //
- // Retrieves a `public void Dispose ()' method from the Type `t'
- //
- static MethodInfo FetchMethodDispose (Type t)
- {
- MemberList dispose_list;
-
- dispose_list = TypeContainer.FindMembers (
- t, MemberTypes.Method,
- BindingFlags.Public | BindingFlags.Instance,
- Type.FilterName, "Dispose");
- if (dispose_list.Count == 0)
- return null;
+ public void Store (EmitContext ec, Expression right_side)
+ {
+ if (li.FieldBuilder != null)
+ ec.ig.Emit (OpCodes.Ldarg_0);
- foreach (MemberInfo m in dispose_list){
- MethodInfo mi = (MethodInfo) m;
- Type [] args;
-
- args = TypeManager.GetArgumentTypes (mi);
- if (args != null && args.Length == 0){
- if (mi.ReturnType == TypeManager.void_type)
- return mi;
+ right_side.Emit (ec);
+ if (li.FieldBuilder != null) {
+ ec.ig.Emit (OpCodes.Stfld, li.FieldBuilder);
+ } else {
+ ec.ig.Emit (OpCodes.Stloc, li.LocalBuilder);
}
}
- return null;
+
+ 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);
+ }
}
- //
- // This struct records the helper methods used by the Foreach construct
- //
- class ForeachHelperMethods {
- public EmitContext ec;
- public MethodInfo get_enumerator;
- public MethodInfo move_next;
- public MethodInfo get_current;
- public Type element_type;
- public Type enumerator_type;
- public bool is_disposable;
- public readonly Location Location;
-
- public ForeachHelperMethods (EmitContext ec, Location loc)
+ protected class ArrayCounter : TemporaryVariable
+ {
+ public ArrayCounter (Location loc)
+ : base (TypeManager.int32_type, loc)
+ { }
+
+ public void Initialize (EmitContext ec)
{
- this.ec = ec;
- this.Location = loc;
- this.element_type = TypeManager.object_type;
- this.enumerator_type = TypeManager.ienumerator_type;
- this.is_disposable = true;
+ EmitThis (ec);
+ ec.ig.Emit (OpCodes.Ldc_I4_0);
+ EmitStore (ec.ig);
+ }
+
+ public void Increment (EmitContext ec)
+ {
+ EmitThis (ec);
+ Emit (ec);
+ ec.ig.Emit (OpCodes.Ldc_I4_1);
+ ec.ig.Emit (OpCodes.Add);
+ EmitStore (ec.ig);
}
}
-
- static bool GetEnumeratorFilter (MemberInfo m, object criteria)
+
+ protected class ArrayForeach : Statement
{
- if (m == null)
- return false;
-
- if (!(m is MethodInfo))
- return false;
-
- if (m.Name != "GetEnumerator")
- return false;
+ Expression variable, expr, conv;
+ Statement statement;
+ Type array_type;
+ Type var_type;
+ TemporaryVariable[] lengths;
+ ArrayCounter[] counter;
+ int rank;
- MethodInfo mi = (MethodInfo) m;
- Type [] args = TypeManager.GetArgumentTypes (mi);
- if (args != null){
- if (args.Length != 0)
- return false;
+ TemporaryVariable copy;
+ Expression access;
+
+ public ArrayForeach (Type var_type, Expression var,
+ Expression expr, Statement stmt, Location l)
+ {
+ this.var_type = var_type;
+ this.variable = var;
+ this.expr = expr;
+ statement = stmt;
+ loc = l;
}
- ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
- // Check whether GetEnumerator is public
- if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
- return false;
+ public override bool Resolve (EmitContext ec)
+ {
+ array_type = expr.Type;
+ rank = array_type.GetArrayRank ();
- if ((mi.ReturnType == 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
- // CharEnumerator instead. This allows us to avoid the
- // try-finally block and the boxing.
- //
- return false;
+ copy = new TemporaryVariable (array_type, loc);
+ copy.Resolve (ec);
- //
- // Ok, we can access it, now make sure that we can do something
- // with this `GetEnumerator'
- //
-
- Type return_type = mi.ReturnType;
- if (mi.ReturnType == TypeManager.ienumerator_type ||
- TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
- (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, 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 (return_type.IsInterface ||
- (hm.move_next = FetchMethodMoveNext (return_type)) == null ||
- (hm.get_current = FetchMethodGetCurrent (return_type)) == null) {
-
- hm.move_next = TypeManager.bool_movenext_void;
- hm.get_current = TypeManager.object_getcurrent_void;
- return true;
- }
+ counter = new ArrayCounter [rank];
+ lengths = new TemporaryVariable [rank];
- } else {
+ ArrayList list = new ArrayList ();
+ for (int i = 0; i < rank; i++) {
+ counter [i] = new ArrayCounter (loc);
+ counter [i].Resolve (ec);
- if (return_type.IsPointer || return_type.IsArray) {
- Report.SymbolRelatedToPreviousError (mi);
- Type t = return_type.GetElementType ();
- Report.SymbolRelatedToPreviousError (t);
- Report.Error (202, hm.Location, "foreach requires that the return type '{0}' of '{1}' must have a suitable public MoveNext method and public Current property",
- TypeManager.CSharpName (return_type), TypeManager.GetFullNameSignature (m));
- hm.get_enumerator = mi;
- return false;
+ lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
+ lengths [i].Resolve (ec);
+
+ list.Add (counter [i]);
}
- //
- // Ok, so they dont return an IEnumerable, we will have to
- // find if they support the GetEnumerator pattern.
- //
-
- hm.move_next = FetchMethodMoveNext (return_type);
- if (hm.move_next == null)
+ access = new ElementAccess (copy, list).Resolve (ec);
+ if (access == null)
return false;
-
- hm.get_current = FetchMethodGetCurrent (return_type);
- if (hm.get_current == null)
+
+ conv = Convert.ExplicitConversion (ec, access, var_type, loc);
+ if (conv == null)
return false;
- }
-
- hm.element_type = hm.get_current.ReturnType;
- hm.enumerator_type = return_type;
- hm.is_disposable = !hm.enumerator_type.IsSealed ||
- TypeManager.ImplementsInterface (
- hm.enumerator_type, TypeManager.idisposable_type);
- return true;
- }
-
- ///
- /// This filter is used to find the GetEnumerator method
- /// on which IEnumerator operates
- ///
- static MemberFilter FilterEnumerator;
-
- static Foreach ()
- {
- FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
- }
+ bool ok = true;
- void error1579 (Type t)
- {
- Report.Error (1579, loc,
- "foreach statement cannot operate on variables of type `" +
- t.FullName + "' because that class does not provide a " +
- " GetEnumerator method or it is inaccessible");
- }
+ ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
+ ec.CurrentBranching.CreateSibling ();
- static bool TryType (Type t, ForeachHelperMethods hm)
- {
- MemberList mi;
-
- mi = TypeContainer.FindMembers (t, MemberTypes.Method,
- BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Instance | BindingFlags.DeclaredOnly,
- FilterEnumerator, hm);
+ variable = variable.ResolveLValue (ec, conv, loc);
+ if (variable == null)
+ ok = false;
- if (mi.Count == 0)
- return false;
+ if (!statement.Resolve (ec))
+ ok = false;
- hm.get_enumerator = (MethodInfo) mi [0];
- return true;
- }
-
- //
- // Looks for a usable GetEnumerator in the Type, and if found returns
- // the three methods that participate: GetEnumerator, MoveNext and get_Current
- //
- ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
- {
- ForeachHelperMethods hm = new ForeachHelperMethods (ec, loc);
+ ec.EndFlowBranching ();
- for (Type tt = t; tt != null && tt != TypeManager.object_type;){
- if (TryType (tt, hm))
- return hm;
- tt = tt.BaseType;
+ return ok;
}
- //
- // Now try to find the method in the interfaces
- //
- while (t != null){
- Type [] ifaces = t.GetInterfaces ();
+ protected override void DoEmit (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ copy.Store (ec, expr);
+
+ Label[] test = new Label [rank];
+ Label[] loop = new Label [rank];
- foreach (Type i in ifaces){
- if (TryType (i, hm))
- return hm;
+ for (int i = 0; i < rank; i++) {
+ test [i] = ig.DefineLabel ();
+ loop [i] = ig.DefineLabel ();
+
+ lengths [i].EmitThis (ec);
+ ((ArrayAccess) access).EmitGetLength (ec, i);
+ lengths [i].EmitStore (ig);
}
-
- //
- // Since TypeBuilder.GetInterfaces only returns the interface
- // types for this type, we have to keep looping, but once
- // we hit a non-TypeBuilder (ie, a Type), then we know we are
- // done, because it returns all the types
- //
- if ((t is TypeBuilder))
- t = t.BaseType;
- else
- break;
- }
- return null;
+ for (int i = 0; i < rank; i++) {
+ counter [i].Initialize (ec);
+
+ ig.Emit (OpCodes.Br, test [i]);
+ ig.MarkLabel (loop [i]);
+ }
+
+ ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
+
+ statement.Emit (ec);
+
+ ig.MarkLabel (ec.LoopBegin);
+
+ for (int i = rank - 1; i >= 0; i--){
+ counter [i].Increment (ec);
+
+ ig.MarkLabel (test [i]);
+ counter [i].Emit (ec);
+ lengths [i].Emit (ec);
+ ig.Emit (OpCodes.Blt, loop [i]);
+ }
+
+ ig.MarkLabel (ec.LoopEnd);
+ }
}
- //
- // FIXME: possible optimization.
- // We might be able to avoid creating `empty' if the type is the sam
- //
- bool EmitCollectionForeach (EmitContext ec)
+ protected class CollectionForeach : ExceptionStatement
{
- ILGenerator ig = ec.ig;
+ Expression variable, expr;
+ Statement statement;
- enumerator = new VariableStorage (ec, hm.enumerator_type);
- enumerator.EmitThis (ig);
- //
- // Instantiate the enumerator
- //
- if (expr.Type.IsValueType) {
- IMemoryLocation ml = expr as IMemoryLocation;
- // Load the address of the value type.
- if (ml == null) {
- // This happens if, for example, you have a property
- // returning a struct which is IEnumerable
- LocalBuilder t = ec.GetTemporaryLocal (expr.Type);
- expr.Emit (ec);
- ig.Emit (OpCodes.Stloc, t);
- ig.Emit (OpCodes.Ldloca, t);
- ec.FreeTemporaryLocal (t, expr.Type);
- } else {
- ml.AddressOf (ec, AddressOp.Load);
+ TemporaryVariable enumerator;
+ Expression init;
+ Statement loop;
+
+ MethodGroupExpr get_enumerator;
+ PropertyExpr get_current;
+ MethodInfo move_next;
+ Type var_type, enumerator_type;
+ bool is_disposable;
+
+ public CollectionForeach (Type var_type, Expression var,
+ Expression expr, Statement stmt, Location l)
+ {
+ this.var_type = var_type;
+ this.variable = var;
+ this.expr = expr;
+ statement = stmt;
+ loc = l;
+ }
+
+ bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
+ {
+ Type [] args = TypeManager.GetArgumentTypes (mi);
+ if (args != null){
+ if (args.Length != 0)
+ return false;
}
-
- // Emit the call.
- if (hm.get_enumerator.DeclaringType.IsValueType) {
- // the method is declared on the value type
- ig.Emit (OpCodes.Call, hm.get_enumerator);
+
+ if (TypeManager.IsOverride (mi))
+ return false;
+
+ // Check whether GetEnumerator is public
+ if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
+ return false;
+
+ if ((mi.ReturnType == 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
+ // CharEnumerator instead. This allows us to avoid the
+ // try-finally block and the boxing.
+ //
+ return false;
+
+ //
+ // Ok, we can access it, now make sure that we can do something
+ // with this `GetEnumerator'
+ //
+
+ Type return_type = mi.ReturnType;
+ if (mi.ReturnType == TypeManager.ienumerator_type ||
+ TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
+ (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, 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 (return_type.IsInterface ||
+ !FetchMoveNext (ec, return_type) ||
+ !FetchGetCurrent (ec, return_type)) {
+ move_next = TypeManager.bool_movenext_void;
+ get_current = new PropertyExpr (
+ ec, TypeManager.ienumerator_getcurrent, loc);
+ return true;
+ }
} else {
- // it is an interface method, so we must box
- ig.Emit (OpCodes.Box, expr.Type);
- ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
+ //
+ // Ok, so they dont return an IEnumerable, we will have to
+ // find if they support the GetEnumerator pattern.
+ //
+
+ if (!FetchMoveNext (ec, return_type))
+ return false;
+
+ if (!FetchGetCurrent (ec, return_type))
+ return false;
}
- } else {
- expr.Emit (ec);
- ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
+
+ enumerator_type = return_type;
+ is_disposable = !enumerator_type.IsSealed ||
+ TypeManager.ImplementsInterface (
+ enumerator_type, TypeManager.idisposable_type);
+
+ return true;
}
- enumerator.EmitStore (ig);
//
- // Protect the code in a try/finalize block, so that
- // if the beast implement IDisposable, we get rid of it
+ // Retrieves a `public bool MoveNext ()' method from the Type `t'
//
- if (hm.is_disposable && emit_finally)
- ig.BeginExceptionBlock ();
-
- Label end_try = ig.DefineLabel ();
-
- ig.MarkLabel (ec.LoopBegin);
-
- enumerator.EmitCall (ig, hm.move_next);
-
- ig.Emit (OpCodes.Brfalse, end_try);
+ bool FetchMoveNext (EmitContext ec, Type t)
+ {
+ MemberList move_next_list;
- if (ec.InIterator)
- ig.Emit (OpCodes.Ldarg_0);
-
- enumerator.EmitCall (ig, hm.get_current);
+ move_next_list = TypeContainer.FindMembers (
+ t, MemberTypes.Method,
+ BindingFlags.Public | BindingFlags.Instance,
+ Type.FilterName, "MoveNext");
+ if (move_next_list.Count == 0)
+ return false;
- if (ec.InIterator){
- conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
- } else
- ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
+ foreach (MemberInfo m in move_next_list){
+ MethodInfo mi = (MethodInfo) m;
+ Type [] args;
- statement.Emit (ec);
- ig.Emit (OpCodes.Br, ec.LoopBegin);
- ig.MarkLabel (end_try);
-
+ args = TypeManager.GetArgumentTypes (mi);
+ if ((args != null) && (args.Length == 0) &&
+ TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
+ move_next = mi;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
//
- // Now the finally block
+ // Retrieves a `public T get_Current ()' method from the Type `t'
//
- if (hm.is_disposable) {
- DoEmitFinally (ec);
- if (emit_finally)
- ig.EndExceptionBlock ();
+ bool FetchGetCurrent (EmitContext ec, Type t)
+ {
+ PropertyExpr pe = Expression.MemberLookup (
+ ec, t, "Current", MemberTypes.Property,
+ Expression.AllBindingFlags, loc) as PropertyExpr;
+ if (pe == null)
+ return false;
+
+ get_current = pe;
+ return true;
}
- ig.MarkLabel (ec.LoopEnd);
- return false;
- }
+ //
+ // Retrieves a `public void Dispose ()' method from the Type `t'
+ //
+ static MethodInfo FetchMethodDispose (Type t)
+ {
+ MemberList dispose_list;
- public override void EmitFinally (EmitContext ec)
- {
- ILGenerator ig = ec.ig;
+ dispose_list = TypeContainer.FindMembers (
+ t, MemberTypes.Method,
+ BindingFlags.Public | BindingFlags.Instance,
+ Type.FilterName, "Dispose");
+ if (dispose_list.Count == 0)
+ return null;
- if (hm.enumerator_type.IsValueType) {
- enumerator.EmitThis (ig);
+ foreach (MemberInfo m in dispose_list){
+ MethodInfo mi = (MethodInfo) m;
+ Type [] args;
- MethodInfo mi = FetchMethodDispose (hm.enumerator_type);
- if (mi != null) {
- enumerator.EmitLoadAddress (ig);
- ig.Emit (OpCodes.Call, mi);
- } else {
- enumerator.EmitLoad (ig);
- ig.Emit (OpCodes.Box, hm.enumerator_type);
- ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ args = TypeManager.GetArgumentTypes (mi);
+ if (args != null && args.Length == 0){
+ if (mi.ReturnType == TypeManager.void_type)
+ return mi;
+ }
}
- } else {
- Label call_dispose = ig.DefineLabel ();
+ return null;
+ }
- enumerator.EmitThis (ig);
- enumerator.EmitLoad (ig);
- ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
- ig.Emit (OpCodes.Dup);
- ig.Emit (OpCodes.Brtrue_S, call_dispose);
- ig.Emit (OpCodes.Pop);
+ public void error1579 ()
+ {
+ 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));
+ }
- Label end_finally = ig.DefineLabel ();
- ig.Emit (OpCodes.Br, end_finally);
+ bool TryType (EmitContext ec, Type t)
+ {
+ MethodGroupExpr mg = Expression.MemberLookup (
+ ec, t, "GetEnumerator", MemberTypes.Method,
+ Expression.AllBindingFlags, loc) as MethodGroupExpr;
+ if (mg == null)
+ return false;
- ig.MarkLabel (call_dispose);
- ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
- ig.MarkLabel (end_finally);
+ foreach (MethodBase mb in mg.Methods) {
+ if (!GetEnumeratorFilter (ec, (MethodInfo) mb))
+ continue;
- if (emit_finally)
- ig.Emit (OpCodes.Endfinally);
- }
- }
+ MethodInfo[] mi = new MethodInfo[] { (MethodInfo) mb };
+ get_enumerator = new MethodGroupExpr (mi, loc);
- //
- // FIXME: possible optimization.
- // We might be able to avoid creating `empty' if the type is the sam
- //
- bool EmitArrayForeach (EmitContext ec)
- {
- int rank = array_type.GetArrayRank ();
- ILGenerator ig = ec.ig;
-
- VariableStorage copy = new VariableStorage (ec, array_type);
-
- //
- // Make our copy of the array
- //
- copy.EmitThis (ig);
- expr.Emit (ec);
- copy.EmitStore (ig);
-
- if (rank == 1){
- VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type);
+ if (t != expr.Type) {
+ expr = Convert.ExplicitConversion (
+ ec, expr, t, loc);
+ if (expr == null)
+ throw new InternalErrorException ();
+ }
- Label loop, test;
+ get_enumerator.InstanceExpression = expr;
+ get_enumerator.IsBase = t != expr.Type;
- counter.EmitThis (ig);
- ig.Emit (OpCodes.Ldc_I4_0);
- counter.EmitStore (ig);
- test = ig.DefineLabel ();
- ig.Emit (OpCodes.Br, test);
+ return true;
+ }
- loop = ig.DefineLabel ();
- ig.MarkLabel (loop);
+ return false;
+ }
- if (ec.InIterator)
- ig.Emit (OpCodes.Ldarg_0);
-
- copy.EmitThis (ig);
- copy.EmitLoad (ig);
- counter.EmitThis (ig);
- counter.EmitLoad (ig);
+ bool ProbeCollectionType (EmitContext ec, Type t)
+ {
+ for (Type tt = t; tt != null && tt != TypeManager.object_type;){
+ if (TryType (ec, tt))
+ return true;
+ tt = tt.BaseType;
+ }
//
- // Load the value, we load the value using the underlying type,
- // then we use the variable.EmitAssign to load using the proper cast.
+ // Now try to find the method in the interfaces
//
- ArrayAccess.EmitLoadOpcode (ig, element_type);
- if (ec.InIterator){
- conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
- } else
- ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
-
- statement.Emit (ec);
+ while (t != null){
+ Type [] ifaces = t.GetInterfaces ();
- ig.MarkLabel (ec.LoopBegin);
- counter.EmitThis (ig);
- counter.EmitThis (ig);
- counter.EmitLoad (ig);
- ig.Emit (OpCodes.Ldc_I4_1);
- ig.Emit (OpCodes.Add);
- counter.EmitStore (ig);
-
- ig.MarkLabel (test);
- counter.EmitThis (ig);
- counter.EmitLoad (ig);
- copy.EmitThis (ig);
- copy.EmitLoad (ig);
- ig.Emit (OpCodes.Ldlen);
- ig.Emit (OpCodes.Conv_I4);
- ig.Emit (OpCodes.Blt, loop);
- } else {
- VariableStorage [] dim_len = new VariableStorage [rank];
- VariableStorage [] dim_count = new VariableStorage [rank];
- Label [] loop = new Label [rank];
- Label [] test = new Label [rank];
- int dim;
+ foreach (Type i in ifaces){
+ if (TryType (ec, i))
+ return true;
+ }
- for (dim = 0; dim < rank; dim++){
- dim_len [dim] = new VariableStorage (ec, TypeManager.int32_type);
- dim_count [dim] = new VariableStorage (ec, TypeManager.int32_type);
- test [dim] = ig.DefineLabel ();
- loop [dim] = ig.DefineLabel ();
- }
-
- for (dim = 0; dim < rank; dim++){
- dim_len [dim].EmitThis (ig);
- copy.EmitThis (ig);
- copy.EmitLoad (ig);
- IntLiteral.EmitInt (ig, dim);
- ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
- dim_len [dim].EmitStore (ig);
-
+ //
+ // Since TypeBuilder.GetInterfaces only returns the interface
+ // types for this type, we have to keep looping, but once
+ // we hit a non-TypeBuilder (ie, a Type), then we know we are
+ // done, because it returns all the types
+ //
+ if ((t is TypeBuilder))
+ t = t.BaseType;
+ else
+ break;
}
- for (dim = 0; dim < rank; dim++){
- dim_count [dim].EmitThis (ig);
- ig.Emit (OpCodes.Ldc_I4_0);
- dim_count [dim].EmitStore (ig);
- ig.Emit (OpCodes.Br, test [dim]);
- ig.MarkLabel (loop [dim]);
+ return false;
+ }
+
+ public override bool Resolve (EmitContext ec)
+ {
+ enumerator_type = TypeManager.ienumerator_type;
+ is_disposable = true;
+
+ if (!ProbeCollectionType (ec, expr.Type)) {
+ error1579 ();
+ return false;
}
- if (ec.InIterator)
- ig.Emit (OpCodes.Ldarg_0);
-
- copy.EmitThis (ig);
- copy.EmitLoad (ig);
- for (dim = 0; dim < rank; dim++){
- dim_count [dim].EmitThis (ig);
- dim_count [dim].EmitLoad (ig);
+ enumerator = new TemporaryVariable (enumerator_type, loc);
+ enumerator.Resolve (ec);
+
+ init = new Invocation (get_enumerator, new ArrayList ());
+ init = init.Resolve (ec);
+ if (init == null)
+ return false;
+
+ Expression move_next_expr;
+ {
+ MemberInfo[] mi = new MemberInfo[] { move_next };
+ MethodGroupExpr mg = new MethodGroupExpr (mi, loc);
+ mg.InstanceExpression = enumerator;
+
+ move_next_expr = new Invocation (mg, new ArrayList ());
}
+ get_current.InstanceExpression = enumerator;
+
+ Statement block = new CollectionForeachStatement (
+ var_type, variable, get_current, statement, loc);
+
+ loop = new While (move_next_expr, block, loc);
+
+ bool ok = true;
+
+ ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
+ ec.CurrentBranching.CreateSibling ();
+
+ FlowBranchingException branching = null;
+ if (is_disposable)
+ branching = ec.StartFlowBranching (this);
+
+ if (!loop.Resolve (ec))
+ ok = false;
+
+ if (is_disposable) {
+ ResolveFinally (branching);
+ ec.EndFlowBranching ();
+ } else
+ emit_finally = true;
+
+ ec.EndFlowBranching ();
+
+ return ok;
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ enumerator.Store (ec, init);
+
//
- // FIXME: Maybe we can cache the computation of `get'?
+ // Protect the code in a try/finalize block, so that
+ // if the beast implement IDisposable, we get rid of it
//
- Type [] args = new Type [rank];
- MethodInfo get;
-
- for (int i = 0; i < rank; i++)
- args [i] = TypeManager.int32_type;
-
- ModuleBuilder mb = CodeGen.Module.Builder;
- get = mb.GetArrayMethod (
- array_type, "Get",
- CallingConventions.HasThis| CallingConventions.Standard,
- var_type, args);
- ig.Emit (OpCodes.Call, get);
- if (ec.InIterator){
- conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
- } else
- ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
- statement.Emit (ec);
- ig.MarkLabel (ec.LoopBegin);
- for (dim = rank - 1; dim >= 0; dim--){
- dim_count [dim].EmitThis (ig);
- dim_count [dim].EmitThis (ig);
- dim_count [dim].EmitLoad (ig);
- ig.Emit (OpCodes.Ldc_I4_1);
- ig.Emit (OpCodes.Add);
- dim_count [dim].EmitStore (ig);
-
- ig.MarkLabel (test [dim]);
- dim_count [dim].EmitThis (ig);
- dim_count [dim].EmitLoad (ig);
- dim_len [dim].EmitThis (ig);
- dim_len [dim].EmitLoad (ig);
- ig.Emit (OpCodes.Blt, loop [dim]);
+ if (is_disposable && emit_finally)
+ ig.BeginExceptionBlock ();
+
+ loop.Emit (ec);
+
+ //
+ // Now the finally block
+ //
+ if (is_disposable) {
+ DoEmitFinally (ec);
+ if (emit_finally)
+ ig.EndExceptionBlock ();
+ }
+ }
+
+
+ public override void EmitFinally (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ if (enumerator_type.IsValueType) {
+ enumerator.Emit (ec);
+
+ MethodInfo mi = FetchMethodDispose (enumerator_type);
+ if (mi != null) {
+ enumerator.EmitLoadAddress (ec);
+ ig.Emit (OpCodes.Call, mi);
+ } else {
+ enumerator.Emit (ec);
+ ig.Emit (OpCodes.Box, enumerator_type);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ }
+ } else {
+ Label call_dispose = ig.DefineLabel ();
+
+ enumerator.Emit (ec);
+ ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
+ ig.Emit (OpCodes.Dup);
+ ig.Emit (OpCodes.Brtrue_S, call_dispose);
+ ig.Emit (OpCodes.Pop);
+
+ Label end_finally = ig.DefineLabel ();
+ ig.Emit (OpCodes.Br, end_finally);
+
+ ig.MarkLabel (call_dispose);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ ig.MarkLabel (end_finally);
}
}
- ig.MarkLabel (ec.LoopEnd);
-
- return false;
}
-
- protected override void DoEmit (EmitContext ec)
+
+ protected class CollectionForeachStatement : Statement
{
- ILGenerator ig = ec.ig;
+ Type type;
+ Expression variable, current, conv;
+ Statement statement;
+ Assign assign;
- Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
- ec.LoopBegin = ig.DefineLabel ();
- ec.LoopEnd = ig.DefineLabel ();
-
- if (hm != null)
- EmitCollectionForeach (ec);
- else
- EmitArrayForeach (ec);
-
- ec.LoopBegin = old_begin;
- ec.LoopEnd = old_end;
+ public CollectionForeachStatement (Type type, Expression variable,
+ Expression current, Statement statement,
+ Location loc)
+ {
+ this.type = type;
+ this.variable = variable;
+ this.current = current;
+ this.statement = statement;
+ this.loc = loc;
+ }
+
+ public override bool Resolve (EmitContext ec)
+ {
+ current = current.Resolve (ec);
+ if (current == null)
+ return false;
+
+ conv = Convert.ExplicitConversion (ec, current, type, loc);
+ if (conv == null)
+ return false;
+
+ assign = new Assign (variable, conv, loc);
+ if (assign.Resolve (ec) == null)
+ return false;
+
+ if (!statement.Resolve (ec))
+ return false;
+
+ return true;
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ assign.EmitStatement (ec);
+ statement.Emit (ec);
+ }
}
}
}