//
// Author:
// Miguel de Icaza (miguel@ximian.com)
-// Martin Baulig (martin@gnome.org)
+// Martin Baulig (martin@ximian.com)
//
// (C) 2001, 2002, 2003 Ximian, Inc.
+// (C) 2003, 2004 Novell, Inc.
//
using System;
// in unreachable code, for instance.
//
+ if (warn && (RootContext.WarningLevel >= 2))
+ Report.Warning (162, loc, "Unreachable code detected");
+
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
ec.KillFlowBranching ();
- if (!ok)
- return false;
-
- if (warn && (RootContext.WarningLevel >= 2))
- Report.Warning (162, loc, "Unreachable code detected");
- return true;
+ return ok;
}
protected void CheckObsolete (Type type)
return false;
}
+ Assign ass = expr as Assign;
+ if (ass != null && ass.Source is Constant) {
+ Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
+ }
+
//
// Dead code elimination
//
{
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"));
+ }
Error (127, "Return with a value not allowed here");
return false;
}
} else
vector.CheckOutParameters (ec.CurrentBranching);
+ if (in_exc)
+ ec.NeedReturnLabel ();
+
ec.CurrentBranching.CurrentUsageVector.Return ();
return true;
}
ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
- if (in_exc) {
- ec.NeedReturnLabel ();
+ if (in_exc)
ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
- } else {
+ else
ec.ig.Emit (OpCodes.Ret);
- }
}
}
bool defined;
bool referenced;
Label label;
+ ILGenerator ig;
FlowBranching.UsageVector vectors;
{
if (defined)
return label;
+ ig = ec.ig;
label = ec.ig.DefineLabel ();
defined = true;
protected override void DoEmit (EmitContext ec)
{
+ if (ig != null && ig != ec.ig) {
+ Report.Error (1632, "Control cannot leave body of anonymous method");
+ return;
+ }
LabelTarget (ec);
ec.ig.MarkLabel (label);
}
/// </summary>
public class GotoCase : Statement {
Expression expr;
- Label label;
+ SwitchLabel sl;
public GotoCase (Expression e, Location l)
{
if (val == null)
return false;
- SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
+ sl = (SwitchLabel) ec.Switch.Elements [val];
if (sl == null){
Report.Error (
return false;
}
- label = sl.ILLabelCode;
-
ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
protected override void DoEmit (EmitContext ec)
{
- ec.ig.Emit (OpCodes.Br, label);
+ ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
}
}
public override bool Resolve (EmitContext ec)
{
- bool in_catch = ec.CurrentBranching.InCatch ();
ec.CurrentBranching.CurrentUsageVector.Throw ();
if (expr != null){
ExprClass eclass = expr.eclass;
if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
- eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
+ eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
expr.Error_UnexpectedKind ("value, variable, property or indexer access ", loc);
return false;
}
Type t = expr.Type;
if ((t != TypeManager.exception_type) &&
- !t.IsSubclassOf (TypeManager.exception_type) &&
- !(expr is NullLiteral)) {
+ !t.IsSubclassOf (TypeManager.exception_type) &&
+ !(expr is NullLiteral)) {
Error (155,
- "The type caught or thrown must be derived " +
- "from System.Exception");
+ "The type caught or thrown must be derived " +
+ "from System.Exception");
return false;
}
- } else if (!in_catch) {
- Error (156,
- "A throw statement with no argument is only " +
- "allowed in a catch clause");
+ return true;
+ }
+
+ if (ec.CurrentBranching.InFinally (true)) {
+ Error (724, "A throw statement with no argument is only allowed in a catch clause nested inside of the innermost catch clause");
return false;
}
+ if (!ec.CurrentBranching.InCatch ()) {
+ Error (156, "A throw statement with no argument is only allowed in a catch clause");
+ return false;
+ }
return true;
}
crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
+ if (!crossing_exc)
+ ec.NeedReturnLabel ();
+
ec.CurrentBranching.CurrentUsageVector.Break ();
return true;
}
if (crossing_exc)
ig.Emit (OpCodes.Leave, ec.LoopEnd);
else {
- ec.NeedReturnLabel ();
ig.Emit (OpCodes.Br, ec.LoopEnd);
}
}
}
}
+ //
+ // The information about a user-perceived local variable
+ //
public class LocalInfo {
public Expression Type;
//
// Most of the time a variable will be stored in a LocalBuilder
//
- // But sometimes, it will be stored in a field. The context of the field will
+ // But sometimes, it will be stored in a field (variables that have been
+ // hoisted by iterators or by anonymous methods). The context of the field will
// be stored in the EmitContext
//
//
Used = 1,
ReadOnly = 2,
Pinned = 4,
- IsThis = 8
+ IsThis = 8,
+ Captured = 16,
+ AddressTaken = 32
}
Flags flags;
public bool Resolve (EmitContext ec)
{
- if (VariableType == null)
- VariableType = ec.DeclSpace.ResolveType (Type, false, Location);
+ if (VariableType == null) {
+ TypeExpr texpr = Type.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
+ return false;
+
+ VariableType = texpr.ResolveType (ec);
+ }
if (VariableType == TypeManager.void_type) {
Report.Error (1547, Location,
return false;
}
- if (VariableType == null)
- return false;
-
if (VariableType.IsAbstract && VariableType.IsSealed) {
Report.Error (723, Location, "Cannot declare variable of static type '{0}'", TypeManager.CSharpName (VariableType));
return false;
}
}
+ public bool IsCaptured {
+ get {
+ return (flags & Flags.Captured) != 0;
+ }
+
+ set {
+ flags |= Flags.Captured;
+ }
+ }
+
+ public bool AddressTaken {
+ get {
+ return (flags & Flags.AddressTaken) != 0;
+ }
+
+ set {
+ flags |= Flags.AddressTaken;
+ }
+ }
+
public override string ToString ()
{
return String.Format ("LocalInfo ({0},{1},{2},{3})",
return (flags & Flags.Used) != 0;
}
set {
- flags = value ? (flags | Flags.Used) : (flags & ~Flags.Used);
+ flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
}
}
return (flags & Flags.ReadOnly) != 0;
}
set {
- flags = value ? (flags | Flags.ReadOnly) : (flags & ~Flags.ReadOnly);
+ flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly));
}
}
/// they contain extra information that is not necessary on normal blocks.
/// </remarks>
public class Block : Statement {
- public readonly Block Parent;
+ public Block Parent;
public readonly Location StartLocation;
- public Location EndLocation = Location.Null;
+ public Location EndLocation = Location.Null;
[Flags]
- public enum Flags : byte {
+ public enum Flags {
Implicit = 1,
Unchecked = 2,
BlockUsed = 4,
VariablesInitialized = 8,
HasRet = 16,
IsDestructor = 32,
- HasVarargs = 64
+ HasVarargs = 64,
+ IsToplevel = 128,
+ Unsafe = 256
}
Flags flags;
}
}
+ public bool Unsafe {
+ get {
+ return (flags & Flags.Unsafe) != 0;
+ }
+ set {
+ flags |= Flags.Unsafe;
+ }
+ }
+
public bool HasVarargs {
get {
if (Parent != null)
// Keeps track of constants
Hashtable constants;
+ //
+ // The parameters for the block, this is only needed on the toplevel block really
+ // TODO: move `parameters' into ToplevelBlock
+ Parameters parameters;
+
//
// If this is a switch section, the enclosing switch block.
//
Block switch_block;
- static int id;
+ protected static int id;
int this_id;
// </summary>
public LocalInfo ThisVariable {
get {
- if (this_variable != null)
- return this_variable;
- else if (Parent != null)
- return Parent.ThisVariable;
- else
- return null;
+ for (Block b = this; b != null; b = b.Parent) {
+ if (b.this_variable != null)
+ return b.this_variable;
+ }
+
+ return null;
}
}
if (child_variable_names == null)
child_variable_names = new Hashtable ();
- if (!child_variable_names.Contains (name))
- child_variable_names.Add (name, true);
+ child_variable_names [name] = null;
}
// <summary>
return e != null;
}
-
- Parameters parameters = null;
- public Parameters Parameters {
- get {
- Block b = this;
- while (b.Parent != null)
- b = b.Parent;
- return b.parameters;
- }
+
+ //
+ // Returns a `ParameterReference' for the given name, or null if there
+ // is no such parameter
+ //
+ public ParameterReference GetParameterReference (string name, Location loc)
+ {
+ Block b = this;
+
+ do {
+ Parameters pars = b.parameters;
+
+ if (pars != null){
+ Parameter par;
+ int idx;
+
+ par = pars.GetParameterByName (name, out idx);
+ if (par != null){
+ ParameterReference pr;
+
+ pr = new ParameterReference (pars, this, idx, name, loc);
+ return pr;
+ }
+ }
+ b = b.Parent;
+ } while (b != null);
+ 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)
+ {
+ Block b = this;
+ int toplevel_count = 0;
+
+ do {
+ if (this is ToplevelBlock)
+ toplevel_count++;
+
+ Parameters pars = b.parameters;
+ if (pars != null){
+ if (pars.GetParameterByName (name) != null)
+ return true;
+ return false;
+ }
+ if (toplevel_count > 0)
+ return false;
+ b = b.Parent;
+ } while (b != null);
+ return false;
+ }
+
+ //
+ // Whether the `name' is a parameter reference
+ //
+ public bool IsParameterReference (string name)
+ {
+ Block b = this;
+
+ do {
+ Parameters pars = b.parameters;
+
+ if (pars != null)
+ if (pars.GetParameterByName (name) != null)
+ return true;
+ b = b.Parent;
+ } while (b != null);
+ return false;
+ }
+
/// <returns>
/// A list of labels that were not used within this block
/// </returns>
public VariableMap ParameterMap {
get {
if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ();
+ throw new Exception ("Variables have not been initialized yet");
return param_map;
}
public VariableMap LocalMap {
get {
if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ();
+ throw new Exception ("Variables have not been initialized yet");
return local_map;
}
}
- public bool LiftVariable (LocalInfo local_info)
- {
- return false;
- }
-
/// <summary>
/// Emits the variable declarations and labels.
/// </summary>
/// tc: is our typecontainer (to resolve type references)
/// ig: is the code generator:
/// </remarks>
- public void EmitMeta (EmitContext ec, InternalParameters ip)
+ public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip)
{
- ILGenerator ig = ec.ig;
+ bool old_unsafe = ec.InUnsafe;
+
+ // If some parent block was unsafe, we remain unsafe even if this block
+ // isn't explicitly marked as such.
+ ec.InUnsafe |= Unsafe;
//
// Compute the VariableMap's.
bool old_check_state = ec.ConstantCheckState;
ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
- bool remap_locals = ec.RemapToProxy;
-
+
//
// Process this block variables
//
continue;
}
+#if false
if (remap_locals)
vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
else if (vi.Pinned)
vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
else if (!vi.IsThis)
vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
+#endif
if (constants == null)
continue;
//
if (children != null){
foreach (Block b in children)
- b.EmitMeta (ec, ip);
+ b.ResolveMeta (toplevel, ec, ip);
+ }
+ ec.InUnsafe = old_unsafe;
+ }
+
+ //
+ // Emits the local variable declarations for a block
+ //
+ public void EmitMeta (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ if (variables != null){
+ bool have_captured_vars = ec.HaveCapturedVariables ();
+ bool remap_locals = ec.RemapToProxy;
+
+ foreach (DictionaryEntry de in variables){
+ LocalInfo vi = (LocalInfo) de.Value;
+
+ 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 (children != null){
+ foreach (Block b in children)
+ b.EmitMeta (ec);
}
}
ec.CurrentBlock = prev_block;
}
+
+ public ToplevelBlock Toplevel {
+ get {
+ Block b = this;
+ while (b.Parent != null){
+ if ((b.flags & Flags.IsToplevel) != 0)
+ break;
+ b = b.Parent;
+ }
+
+ return (ToplevelBlock) b;
+ }
+ }
+
+ //
+ // Returns true if we ar ea child of `b'.
+ //
+ public bool IsChildOf (Block b)
+ {
+ Block current = this;
+
+ do {
+ if (current.Parent == b)
+ return true;
+ current = current.Parent;
+ } while (current != null);
+ return false;
+ }
}
//
+ // A toplevel block contains extra information, the split is done
+ // only to separate information that would otherwise bloat the more
+ // lightweight Block.
+ //
+ // In particular, this was introduced when the support for Anonymous
+ // Methods was implemented.
//
public class ToplevelBlock : Block {
+ //
+ // Pointer to the host of this anonymous method, or null
+ // if we are the topmost block
+ //
+ public ToplevelBlock Container;
+ CaptureContext capture_context;
+
+ Hashtable capture_contexts;
+
+ static int did = 0;
+
+
+ public void RegisterCaptureContext (CaptureContext cc)
+ {
+ if (capture_contexts == null)
+ capture_contexts = new Hashtable ();
+ capture_contexts [cc] = cc;
+ }
+
+ public void CompleteContexts ()
+ {
+ if (capture_contexts == null)
+ return;
+
+ foreach (CaptureContext cc in capture_contexts.Keys){
+ cc.AdjustScopes ();
+ }
+ }
+
+ public CaptureContext ToplevelBlockCaptureContext {
+ get {
+ return capture_context;
+ }
+ }
+
+ //
+ // Parent is only used by anonymous blocks to link back to their
+ // parents
+ //
+ public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) :
+ base (null, Flags.IsToplevel, parameters, start, Location.Null)
+ {
+ Container = container;
+ }
+
public ToplevelBlock (Parameters parameters, Location start) :
- base (null, parameters, start, Location.Null)
+ base (null, Flags.IsToplevel, parameters, start, Location.Null)
{
}
public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
- base (null, flags, parameters, start, Location.Null)
+ base (null, flags | Flags.IsToplevel, parameters, start, Location.Null)
{
}
+
+ public ToplevelBlock (Location loc) : base (null, Flags.IsToplevel, loc, loc)
+ {
+ }
+
+ public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host)
+ {
+ if (capture_context == null)
+ capture_context = new CaptureContext (this, loc, host);
+ }
+
+ public CaptureContext CaptureContext {
+ get {
+ return capture_context;
+ }
+ }
}
public class SwitchLabel {
Expression label;
object converted;
public Location loc;
- public Label ILLabel;
- public Label ILLabelCode;
+
+ Label il_label;
+ bool il_label_set;
+ Label il_label_code;
+ bool il_label_code_set;
//
// if expr == null, then it is the default case.
}
}
+ public Label GetILLabel (EmitContext ec)
+ {
+ if (!il_label_set){
+ il_label = ec.ig.DefineLabel ();
+ il_label_set = true;
+ }
+ return il_label;
+ }
+
+ public Label GetILLabelCode (EmitContext ec)
+ {
+ if (!il_label_code_set){
+ il_label_code = ec.ig.DefineLabel ();
+ il_label_code_set = true;
+ }
+ return il_label_code;
+ }
+
//
// Resolves the expression, reduces it to a literal if possible
// and then converts it to the requested type.
//
public bool ResolveAndReduce (EmitContext ec, Type required_type)
{
- ILLabel = ec.ig.DefineLabel ();
- ILLabelCode = ec.ig.DefineLabel ();
-
if (label == null)
return true;
if (e is StringConstant || e is NullLiteral){
if (required_type == TypeManager.string_type){
converted = e;
- ILLabel = ec.ig.DefineLabel ();
return true;
}
}
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,
if (e == null)
continue;
+ //
+ // 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 (ue.Source != Expr)
+ e = null;
+ }
+
if (converted != null){
- Report.Error (-12, loc, "More than one conversion to an integral " +
- " type exists for type `" +
- TypeManager.CSharpName (Expr.Type)+"'");
+ 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
+ } else {
converted = e;
+ }
}
return converted;
}
ig.Emit (OpCodes.Ldloc, val);
EmitObjectInteger (ig, key);
SwitchLabel sl = (SwitchLabel) Elements [key];
- ig.Emit (OpCodes.Beq, sl.ILLabel);
+ ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
}
}
else
if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
{
SwitchLabel sl = (SwitchLabel) Elements [key];
- rgLabels [iJump] = sl.ILLabel;
+ rgLabels [iJump] = sl.GetILLabel (ec);
iKey++;
}
else
{
foreach (SwitchLabel sl in ss.Labels)
{
- ig.MarkLabel (sl.ILLabel);
- ig.MarkLabel (sl.ILLabelCode);
+ ig.MarkLabel (sl.GetILLabel (ec));
+ ig.MarkLabel (sl.GetILLabelCode (ec));
if (sl.Label == null)
{
ig.MarkLabel (lblDefault);
null_found = false;
for (int label = 0; label < label_count; label++){
SwitchLabel sl = (SwitchLabel) ss.Labels [label];
- ig.MarkLabel (sl.ILLabel);
+ ig.MarkLabel (sl.GetILLabel (ec));
if (!first_test){
ig.MarkLabel (next_test);
ig.MarkLabel (null_target);
ig.MarkLabel (sec_begin);
foreach (SwitchLabel sl in ss.Labels)
- ig.MarkLabel (sl.ILLabelCode);
+ ig.MarkLabel (sl.GetILLabelCode (ec));
if (mark_default)
ig.MarkLabel (default_target);
public Unsafe (Block b)
{
Block = b;
+ Block.Unsafe = true;
}
public override bool Resolve (EmitContext ec)
return false;
}
- expr_type = ec.DeclSpace.ResolveType (type, false, loc);
- if (expr_type == null)
+ TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
return false;
+ expr_type = texpr.ResolveType (ec);
+
CheckObsolete (expr_type);
if (ec.RemapToProxy){
public override bool Resolve (EmitContext ec)
{
if (type_expr != null) {
- type = ec.DeclSpace.ResolveType (type_expr, false, loc);
- if (type == null)
+ TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
+ if (te == null)
return false;
+ type = te.ResolveType (ec);
+
CheckObsolete (type);
if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
//
bool ResolveLocalVariableDecls (EmitContext ec)
{
- expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
int i = 0;
- if (expr_type == null)
+ TypeExpr texpr = expr.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
return false;
+ expr_type = texpr.ResolveType (ec);
+
//
// The type must be an IDisposable or an implicit conversion
// must exist.
ILGenerator ig = ec.ig;
int i = assign.Length;
- foreach (DictionaryEntry e in var_list){
+ for (int ii = 0; ii < var_list.Count; ++ii){
Expression var = resolved_vars [--i];
Label skip = ig.DefineLabel ();
void EmitExpressionFinally (EmitContext ec)
{
ILGenerator ig = ec.ig;
- Label skip = ig.DefineLabel ();
- ig.Emit (OpCodes.Ldloc, local_copy);
- ig.Emit (OpCodes.Brfalse, skip);
- ig.Emit (OpCodes.Ldloc, local_copy);
- ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
- ig.MarkLabel (skip);
+ if (!local_copy.LocalType.IsValueType) {
+ Label skip = ig.DefineLabel ();
+ ig.Emit (OpCodes.Ldloc, local_copy);
+ ig.Emit (OpCodes.Brfalse, skip);
+ ig.Emit (OpCodes.Ldloc, local_copy);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ ig.MarkLabel (skip);
+ } else {
+ Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, local_copy.LocalType, "Dispose", Mono.CSharp.Location.Null);
+
+ if (!(ml is MethodGroupExpr)) {
+ ig.Emit (OpCodes.Ldloc, local_copy);
+ ig.Emit (OpCodes.Box, local_copy.LocalType);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ } else {
+ MethodInfo mi = null;
+
+ foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
+ if (TypeManager.GetArgumentTypes (mk).Length == 0) {
+ mi = mk;
+ break;
+ }
+ }
+
+ if (mi == null) {
+ Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
+ return;
+ }
+
+ ig.Emit (OpCodes.Ldloca, local_copy);
+ ig.Emit (OpCodes.Call, mi);
+ }
+ }
}
public override bool Resolve (EmitContext ec)
if (expr == null)
return false;
- var_type = ec.DeclSpace.ResolveType (type, false, loc);
- if (var_type == null)
+ if (expr is NullLiteral) {
+ Report.Error (186, expr.Location, "Use of null is not valid in this context");
return false;
+ }
+
+ TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
+ return false;
+
+ var_type = texpr.ResolveType (ec);
//
// We need an instance variable. Not sure this is the best
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;
public Type element_type;
public Type enumerator_type;
public bool is_disposable;
+ public readonly Location Location;
- public ForeachHelperMethods (EmitContext ec)
+ public ForeachHelperMethods (EmitContext ec, Location loc)
{
this.ec = ec;
+ this.Location = loc;
this.element_type = TypeManager.object_type;
this.enumerator_type = TypeManager.ienumerator_type;
this.is_disposable = true;
return false;
}
ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
- EmitContext ec = hm.ec;
// Check whether GetEnumerator is public
if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
} else {
+ 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;
+ }
+
//
// Ok, so they dont return an IEnumerable, we will have to
// find if they support the GetEnumerator pattern.
//
ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
{
- ForeachHelperMethods hm = new ForeachHelperMethods (ec);
+ ForeachHelperMethods hm = new ForeachHelperMethods (ec, loc);
for (Type tt = t; tt != null && tt != TypeManager.object_type;){
if (TryType (tt, hm))
ig.Emit (OpCodes.Brfalse, end_try);
if (ec.InIterator)
- enumerator.EmitThis (ig);
+ ig.Emit (OpCodes.Ldarg_0);
+
enumerator.EmitCall (ig, hm.get_current);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
ig.MarkLabel (loop);
if (ec.InIterator)
- ec.EmitThis ();
+ ig.Emit (OpCodes.Ldarg_0);
copy.EmitThis (ig);
copy.EmitLoad (ig);
ArrayAccess.EmitLoadOpcode (ig, element_type);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
}
if (ec.InIterator)
- ec.EmitThis ();
+ ig.Emit (OpCodes.Ldarg_0);
+
copy.EmitThis (ig);
copy.EmitLoad (ig);
for (dim = 0; dim < rank; dim++){
ig.Emit (OpCodes.Call, get);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
statement.Emit (ec);