{
ec.Mark (loc, true);
DoEmit (ec);
- }
+ }
+
+ //
+ // This routine must be overrided in derived classes and make copies
+ // of all the data that might be modified if resolved
+ //
+ protected virtual void CloneTo (CloneContext clonectx, Statement target)
+ {
+ throw new Exception (String.Format ("Statement.CloneTo not implemented for {0}", this.GetType ()));
+ }
+
+ public Statement Clone (CloneContext clonectx)
+ {
+ Statement s = (Statement) this.MemberwiseClone ();
+ CloneTo (clonectx, s);
+ return s;
+ }
+
+ public Statement PerformClone ()
+ {
+ CloneContext clonectx = new CloneContext ();
+
+ return Clone (clonectx);
+ }
+
}
+ //
+ // This class is used during the Statement.Clone operation
+ // to remap objects that have been cloned.
+ //
+ // Since blocks are cloned by Block.Clone, we need a way for
+ // expressions that must reference the block to be cloned
+ // pointing to the new cloned block.
+ //
+ public class CloneContext {
+ Hashtable block_map = new Hashtable ();
+ Hashtable variable_map;
+
+ public void AddBlockMap (Block from, Block to)
+ {
+ if (block_map.Contains (from))
+ return;
+ block_map [from] = to;
+ }
+
+ public Block LookupBlock (Block from)
+ {
+ Block result = (Block) block_map [from];
+
+ if (result == null){
+ result = (Block) from.Clone (this);
+ block_map [from] = result;
+ }
+
+ return result;
+ }
+
+ public void AddVariableMap (LocalInfo from, LocalInfo to)
+ {
+ if (variable_map == null)
+ variable_map = new Hashtable ();
+
+ if (variable_map.Contains (from))
+ return;
+ variable_map [from] = to;
+ }
+
+ public LocalInfo LookupVariable (LocalInfo from)
+ {
+ LocalInfo result = (LocalInfo) variable_map [from];
+
+ if (result == null)
+ throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
+
+ return result;
+ }
+ }
+
public sealed class EmptyStatement : Statement {
private EmptyStatement () {}
ok &= TrueStatement.Resolve (ec);
- is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
+ is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
ec.CurrentBranching.CreateSibling ();
ig.MarkLabel (false_target);
}
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ If target = (If) t;
+
+ target.expr = expr.Clone (clonectx);
+ target.TrueStatement = TrueStatement.Clone (clonectx);
+ if (FalseStatement != null)
+ target.FalseStatement = FalseStatement.Clone (clonectx);
+ }
}
public class Do : Statement {
public Expression expr;
- public readonly Statement EmbeddedStatement;
+ public Statement EmbeddedStatement;
bool infinite;
public Do (Statement statement, Expression boolExpr, Location l)
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
- bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
+ bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
if (!EmbeddedStatement.Resolve (ec))
ok = false;
ec.EndFlowBranching ();
- if (ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable && !was_unreachable)
+ if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
Report.Warning (162, 2, expr.Location, "Unreachable code detected");
expr = Expression.ResolveBoolean (ec, expr, loc);
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Do target = (Do) t;
+
+ target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
+ target.expr = expr.Clone (clonectx);
+ }
}
public class While : Statement {
public Expression expr;
- public readonly Statement Statement;
+ public Statement Statement;
bool infinite, empty;
public While (Expression boolExpr, Statement statement, Location l)
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ While target = (While) t;
+
+ target.expr = expr.Clone (clonectx);
+ target.Statement = Statement.Clone (clonectx);
+ }
}
public class For : Statement {
Expression Test;
- readonly Statement InitStatement;
- readonly Statement Increment;
- public readonly Statement Statement;
+ Statement InitStatement;
+ Statement Increment;
+ public Statement Statement;
bool infinite, empty;
public For (Statement initStatement,
if (!infinite)
ec.CurrentBranching.CreateSibling ();
- bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
+ bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
if (!Statement.Resolve (ec))
ec.EndFlowBranching ();
if (Increment != null){
- if (ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable) {
+ if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
if (!Increment.ResolveUnreachable (ec, !was_unreachable))
ok = false;
} else {
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ For target = (For) t;
+
+ if (InitStatement != null)
+ target.InitStatement = InitStatement.Clone (clonectx);
+ if (Test != null)
+ target.Test = Test.Clone (clonectx);
+ if (Increment != null)
+ target.Increment = Increment.Clone (clonectx);
+ target.Statement = Statement.Clone (clonectx);
+ }
}
public class StatementExpression : Statement {
{
return "StatementExpression (" + expr + ")";
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ StatementExpression target = (StatementExpression) t;
+
+ target.expr = (ExpressionStatement) expr.Clone (clonectx);
+ }
}
/// <summary>
if (ec.ReturnType == null){
if (Expr != null){
- if (ec.CurrentAnonymousMethod != null){
+ if (am != null){
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 ());
+ am.GetSignatureForError ());
}
Error (127, "A return keyword must not be followed by any expression when method returns void");
return false;
return false;
if (Expr.Type != ec.ReturnType) {
- Expr = Convert.ImplicitConversionRequired (
- ec, Expr, ec.ReturnType, loc);
- if (Expr == null)
- return false;
+ if (ec.InferReturnType) {
+ ec.ReturnType = Expr.Type;
+ } else {
+ Expr = Convert.ImplicitConversionRequired (
+ ec, Expr, ec.ReturnType, loc);
+ if (Expr == null)
+ return false;
+ }
}
}
unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
if (unwind_protect)
ec.NeedReturnLabel ();
- ec.CurrentBranching.CurrentUsageVector.Return ();
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
return errors == Report.Errors;
}
else
ec.ig.Emit (OpCodes.Ret);
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Return target = (Return) t;
+
+ target.Expr = Expr.Clone (clonectx);
+ }
}
public class Goto : Statement {
}
if (!ec.Switch.GotDefault){
- Report.Error (159, loc, "No such label `default:' within the scope of the goto statement");
+ FlowBranchingBlock.Error_UnknownLabel (loc, "default");
return;
}
ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
sl = (SwitchLabel) ec.Switch.Elements [val];
if (sl == null){
- Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", c.GetValue () == null ? "null" : val.ToString ());
+ FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
+ (c.GetValue () == null ? "null" : val.ToString ()));
return false;
}
{
ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ GotoCase target = (GotoCase) t;
+
+ target.expr = expr.Clone (clonectx);
+ target.sl = sl.Clone (clonectx);
+ }
}
public class Throw : Statement {
public override bool Resolve (EmitContext ec)
{
- ec.CurrentBranching.CurrentUsageVector.Throw ();
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
if (expr != null){
expr = expr.Resolve (ec);
ec.ig.Emit (OpCodes.Throw);
}
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Throw target = (Throw) t;
+
+ target.expr = expr.Clone (clonectx);
+ }
}
public class Break : Statement {
public abstract void EmitAddressOf (EmitContext ec);
}
+ public interface IKnownVariable {
+ Block Block { get; }
+ Location Location { get; }
+ }
+
//
// The information about a user-perceived local variable
//
- public class LocalInfo {
+ public class LocalInfo : IKnownVariable {
public Expression Type;
public Type VariableType;
Flags flags;
ReadOnlyContext ro_context;
+ LocalBuilder builder;
public LocalInfo (Expression type, string name, Block block, Location l)
{
public LocalInfo (DeclSpace ds, Block block, Location l)
{
- VariableType = ds.TypeBuilder;
+ VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
Block = block;
Location = l;
}
public void ResolveVariable (EmitContext ec)
{
Block theblock = Block;
- while (theblock.Implicit)
- theblock = theblock.Parent;
if (theblock.ScopeInfo != null)
var = theblock.ScopeInfo.GetCapturedVariable (this);
if (var == null) {
- LocalBuilder builder;
if (Pinned)
//
// This is needed to compile on both .NET 1.x and .NET 2.x
}
}
- public bool IsThisAssigned (EmitContext ec, Location loc)
+ public void EmitSymbolInfo (EmitContext ec, string name)
+ {
+ if (builder != null)
+ ec.DefineLocalVariable (name, builder);
+ }
+
+ public bool IsThisAssigned (EmitContext ec)
{
if (VariableInfo == null)
throw new Exception ();
if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
return true;
- return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
+ return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
}
public bool IsAssigned (EmitContext ec)
public bool Resolve (EmitContext ec)
{
if (VariableType == null) {
- TypeExpr texpr = Type.ResolveAsTypeTerminal (ec, false);
+ TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
if (texpr == null)
return false;
VariableType = texpr.Type;
}
+ if (TypeManager.IsGenericParameter (VariableType))
+ return true;
+
if (VariableType == TypeManager.void_type) {
Expression.Error_VoidInvalidInTheContext (Location);
return false;
}
if (VariableType.IsAbstract && VariableType.IsSealed) {
- FieldMember.Error_VariableOfStaticClass (Location, Name, VariableType);
+ FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
return false;
}
}
public bool IsCaptured {
- get {
- return (flags & Flags.Captured) != 0;
- }
-
- set {
- flags |= Flags.Captured;
- }
+ get { return (flags & Flags.Captured) != 0; }
+ set { flags |= Flags.Captured; }
}
public bool IsConstant {
- get {
- return (flags & Flags.IsConstant) != 0;
- }
- set {
- flags |= Flags.IsConstant;
- }
+ get { return (flags & Flags.IsConstant) != 0; }
+ set { flags |= Flags.IsConstant; }
}
public bool AddressTaken {
- get {
- return (flags & Flags.AddressTaken) != 0;
- }
-
- set {
- flags |= Flags.AddressTaken;
- }
+ get { return (flags & Flags.AddressTaken) != 0; }
+ set { flags |= Flags.AddressTaken; }
}
public bool CompilerGenerated {
- get {
- return (flags & Flags.CompilerGenerated) != 0;
- }
-
- set {
- flags |= Flags.CompilerGenerated;
- }
+ get { return (flags & Flags.CompilerGenerated) != 0; }
+ set { flags |= Flags.CompilerGenerated; }
}
public override string ToString ()
}
public bool Used {
- get {
- return (flags & Flags.Used) != 0;
- }
- set {
- flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
- }
+ get { return (flags & Flags.Used) != 0; }
+ set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
}
public bool ReadOnly {
- get {
- return (flags & Flags.ReadOnly) != 0;
- }
+ get { return (flags & Flags.ReadOnly) != 0; }
}
public void SetReadOnlyContext (ReadOnlyContext context)
// allocated in a pinned slot with DeclareLocal.
//
public bool Pinned {
- get {
- return (flags & Flags.Pinned) != 0;
- }
- set {
- flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned);
- }
+ get { return (flags & Flags.Pinned) != 0; }
+ set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
}
public bool IsThis {
- get {
- return (flags & Flags.IsThis) != 0;
- }
- set {
- flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis);
- }
+ get { return (flags & Flags.IsThis) != 0; }
+ set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
+ }
+
+ Block IKnownVariable.Block {
+ get { return Block; }
+ }
+
+ Location IKnownVariable.Location {
+ get { return Location; }
}
protected class LocalVariable : Variable
ec.ig.Emit (OpCodes.Ldloca, builder);
}
}
+
+ public LocalInfo Clone (CloneContext clonectx)
+ {
+ // Only this kind is created by the parser.
+ return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
+ }
}
-
+
/// <summary>
/// Block represents a C# block.
/// </summary>
public readonly Location StartLocation;
public Location EndLocation = Location.Null;
- public readonly ToplevelBlock Toplevel;
+ public ExplicitBlock Explicit;
+ public ToplevelBlock Toplevel;
[Flags]
- public enum Flags : ushort {
- Implicit = 1,
- Unchecked = 2,
- BlockUsed = 4,
- VariablesInitialized = 8,
- HasRet = 16,
- IsDestructor = 32,
- IsToplevel = 64,
- Unsafe = 128,
- HasVarargs = 256, // Used in ToplevelBlock
- IsIterator = 512
-
+ public enum Flags : byte {
+ Unchecked = 1,
+ BlockUsed = 2,
+ VariablesInitialized = 4,
+ HasRet = 8,
+ IsDestructor = 16,
+ Unsafe = 32,
+ HasVarargs = 64, // Used in ToplevelBlock
+ IsIterator = 128
}
protected Flags flags;
- public bool Implicit {
- get { return (flags & Flags.Implicit) != 0; }
- }
-
public bool Unchecked {
get { return (flags & Flags.Unchecked) != 0; }
set { flags |= Flags.Unchecked; }
// The statements in this block
//
protected ArrayList statements;
+ protected int current_statement;
int num_statements;
//
public Block (Block parent, Flags flags, Location start, Location end)
{
- if (parent != null)
+ if (parent != null) {
parent.AddChild (this);
+
+ // the appropriate constructors will fixup these fields
+ Toplevel = parent.Toplevel;
+ Explicit = parent.Explicit;
+ }
this.Parent = parent;
this.flags = flags;
this.loc = start;
this_id = id++;
statements = new ArrayList ();
-
- if ((flags & Flags.IsToplevel) != 0)
- Toplevel = (ToplevelBlock) this;
- else
- Toplevel = parent.Toplevel;
-
- if (parent != null && Implicit) {
- if (parent.known_variables == null)
- parent.known_variables = new Hashtable ();
- // share with parent
- known_variables = parent.known_variables;
- }
}
public Block CreateSwitchBlock (Location start)
{
- Block new_block = new Block (this, start, start);
+ // FIXME: should this be implicit?
+ Block new_block = new ExplicitBlock (this, start, start);
new_block.switch_block = this;
return new_block;
}
protected static void Error_158 (string name, Location loc)
{
Report.Error (158, loc, "The label `{0}' shadows another label " +
- "by the same name in a contained scope.", name);
+ "by the same name in a contained scope", name);
}
/// <summary>
Block cur = this;
while (cur != null) {
- if (cur.DoLookupLabel (name) != null) {
- Report.Error (140, target.loc,
- "The label `{0}' is a duplicate", name);
+ LabeledStatement s = cur.DoLookupLabel (name);
+ if (s != null) {
+ Report.SymbolRelatedToPreviousError (s.loc, s.Name);
+ Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
return false;
}
- if (!Implicit)
+ if (this == Explicit)
break;
cur = cur.Parent;
if (s == null)
continue;
+ Report.SymbolRelatedToPreviousError (s.loc, s.Name);
Error_158 (name, target.loc);
return false;
}
return null;
foreach (Block child in children) {
- if (!child.Implicit)
+ if (Explicit != child.Explicit)
continue;
s = child.LookupLabel (name);
return null;
}
- Hashtable known_variables;
-
- // <summary>
- // Marks a variable with name @name as being used in this or a child block.
- // If a variable name has been used in a child block, it's illegal to
- // declare a variable with the same name in the current block.
- // </summary>
- void AddKnownVariable (string name, LocalInfo info)
- {
- if (known_variables == null)
- known_variables = new Hashtable ();
-
- known_variables [name] = info;
- }
-
- LocalInfo GetKnownVariableInfo (string name, bool recurse)
- {
- if (known_variables != null) {
- LocalInfo vi = (LocalInfo) known_variables [name];
- if (vi != null)
- return vi;
- }
-
- if (!recurse || (children == null))
- return null;
-
- foreach (Block block in children) {
- LocalInfo vi = block.GetKnownVariableInfo (name, true);
- if (vi != null)
- return vi;
- }
-
- return null;
- }
-
public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
{
Block b = this;
- LocalInfo kvi = b.GetKnownVariableInfo (name, true);
+ IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
while (kvi == null) {
- while (b.Implicit)
- b = b.Parent;
- b = b.Parent;
+ b = b.Explicit.Parent;
if (b == null)
return true;
- kvi = b.GetKnownVariableInfo (name, false);
+ kvi = b.Explicit.GetKnownVariable (name);
}
if (kvi.Block == b)
return true;
// Is kvi.Block nested inside 'b'
- if (b.known_variables != kvi.Block.known_variables) {
+ if (b.Explicit != kvi.Block.Explicit) {
//
// If a variable by the same name it defined in a nested block of this
// block, we violate the invariant meaning in a block.
// 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 && b.GetLocalInfo (name) != null))
+ if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
return true;
//
return false;
}
- public bool CheckError136_InParents (string name, Location loc)
- {
- for (Block b = Parent; b != null; b = b.Parent) {
- if (!b.DoCheckError136 (name, "parent or current", loc))
- return false;
- }
-
- for (Block b = Toplevel.ContainerBlock; b != null; b = b.Toplevel.ContainerBlock) {
- if (!b.CheckError136_InParents (name, loc))
- return false;
- }
-
- return true;
- }
-
- public bool CheckError136_InChildren (string name, Location loc)
- {
- if (!DoCheckError136_InChildren (name, loc))
- return false;
-
- Block b = this;
- while (b.Implicit) {
- if (!b.Parent.DoCheckError136_InChildren (name, loc))
- return false;
- b = b.Parent;
- }
-
- return true;
- }
-
- protected bool DoCheckError136_InChildren (string name, Location loc)
- {
- if (!DoCheckError136 (name, "child", loc))
- return false;
-
- if (AnonymousChildren != null) {
- foreach (ToplevelBlock child in AnonymousChildren) {
- if (!child.DoCheckError136_InChildren (name, loc))
- return false;
- }
- }
-
- if (children != null) {
- foreach (Block child in children) {
- if (!child.DoCheckError136_InChildren (name, loc))
- return false;
- }
- }
-
- return true;
- }
-
- public bool CheckError136 (string name, string scope, bool check_parents,
- bool check_children, Location loc)
- {
- if (!DoCheckError136 (name, scope, loc))
- return false;
-
- if (check_parents) {
- if (!CheckError136_InParents (name, loc))
- return false;
- }
-
- if (check_children) {
- if (!CheckError136_InChildren (name, loc))
- return false;
- }
-
- for (Block c = Toplevel.ContainerBlock; c != null; c = c.Toplevel.ContainerBlock) {
- if (!c.DoCheckError136 (name, "parent or current", loc))
- return false;
- }
-
- return true;
- }
-
- protected bool DoCheckError136 (string name, string scope, Location loc)
- {
- LocalInfo vi = GetKnownVariableInfo (name, false);
- if (vi != null) {
- Report.SymbolRelatedToPreviousError (vi.Location, name);
- Error_AlreadyDeclared (loc, name, scope != null ? scope : "child");
- return false;
- }
-
- int idx;
- Parameter p = Toplevel.Parameters.GetParameterByName (name, out idx);
- if (p != null) {
- Report.SymbolRelatedToPreviousError (p.Location, name);
- Error_AlreadyDeclared (
- loc, name, scope != null ? scope : "method argument");
- return false;
- }
-
- return true;
- }
-
public LocalInfo AddVariable (Expression type, string name, Location l)
{
LocalInfo vi = GetLocalInfo (name);
if (vi != null) {
Report.SymbolRelatedToPreviousError (vi.Location, name);
- if (known_variables == vi.Block.known_variables)
+ if (Explicit == vi.Block.Explicit)
Report.Error (128, l,
"A local variable named `{0}' is already defined in this scope", name);
else
return null;
}
- if (!CheckError136 (name, null, true, true, l))
+ ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
+ if (pi != null) {
+ Report.SymbolRelatedToPreviousError (pi.Location, name);
+ Error_AlreadyDeclared (loc, name,
+ pi.Block == Toplevel ? "method argument" : "parent or current");
+ }
+
+ IKnownVariable kvi = Explicit.GetKnownVariable (name);
+ if (kvi != null) {
+ Report.SymbolRelatedToPreviousError (kvi.Location, name);
+ Error_AlreadyDeclared (l, name, "child");
return null;
+ }
vi = new LocalInfo (type, name, this, l);
Variables.Add (name, vi);
- AddKnownVariable (name, vi);
+ Explicit.AddKnownVariable (name, vi);
if ((flags & Flags.VariablesInitialized) != 0)
- throw new Exception ();
+ throw new InternalErrorException ("block has already been resolved");
return vi;
}
- void Error_AlreadyDeclared (Location loc, string var, string reason)
+ protected static 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 " +
statements.Add (s);
flags |= Flags.BlockUsed;
}
+
+ public void InsertStatementAfterCurrent (Statement statement)
+ {
+ statements.Insert (current_statement + 1, statement);
+ flags |= Flags.BlockUsed;
+ }
public bool Used {
get { return (flags & Flags.BlockUsed) != 0; }
flags |= Flags.IsDestructor;
}
- VariableMap param_map, local_map;
-
- public VariableMap ParameterMap {
+ int assignable_slots;
+ public int AssignableSlots {
get {
if ((flags & Flags.VariablesInitialized) == 0)
throw new Exception ("Variables have not been initialized yet");
-
- return param_map;
+ return assignable_slots;
}
}
- public VariableMap LocalMap {
- get {
- if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ("Variables have not been initialized yet");
+ protected ScopeInfo scope_info;
- return local_map;
- }
+ public ScopeInfo ScopeInfo {
+ get { return scope_info; }
}
- public ScopeInfo ScopeInfo;
-
public ScopeInfo CreateScopeInfo ()
{
- if (Implicit)
- return Parent.CreateScopeInfo ();
-
- if (ScopeInfo == null)
- ScopeInfo = new ScopeInfo (Toplevel.AnonymousMethodHost, this);
+ if (scope_info == null)
+ scope_info = ScopeInfo.CreateScope (this);
- return ScopeInfo;
+ return scope_info;
}
public ArrayList AnonymousChildren {
anonymous_children.Add (b);
}
- /// <summary>
- /// Emits the variable declarations and labels.
- /// </summary>
- /// <remarks>
- /// tc: is our typecontainer (to resolve type references)
- /// ig: is the code generator:
- /// </remarks>
- public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, Parameters ip)
+ void DoResolveConstants (EmitContext ec)
{
- Report.Debug (64, "BLOCK RESOLVE META", this, Parent, toplevel);
-
- // If some parent block was unsafe, we remain unsafe even if this block
- // isn't explicitly marked as such.
- using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
- //
- // Compute the VariableMap's.
- //
- // Unfortunately, we don't know the type when adding variables with
- // AddVariable(), so we need to compute this info here.
- //
-
- LocalInfo[] locals;
- if (variables != null) {
- foreach (LocalInfo li in variables.Values)
- li.Resolve (ec);
-
- locals = new LocalInfo [variables.Count];
- variables.Values.CopyTo (locals, 0);
- } else
- locals = new LocalInfo [0];
-
- if (Parent != null)
- local_map = new VariableMap (Parent.LocalMap, locals);
- else
- local_map = new VariableMap (locals);
+ if (constants == null)
+ return;
- param_map = new VariableMap (ip);
- flags |= Flags.VariablesInitialized;
+ if (variables == null)
+ throw new InternalErrorException ("cannot happen");
- //
- // Process this block variables
- //
- if (variables != null) {
- foreach (DictionaryEntry de in variables) {
- string name = (string) de.Key;
- LocalInfo vi = (LocalInfo) de.Value;
- Type variable_type = vi.VariableType;
+ foreach (DictionaryEntry de in variables) {
+ string name = (string) de.Key;
+ LocalInfo vi = (LocalInfo) de.Value;
+ Type variable_type = vi.VariableType;
- if (variable_type == null)
- continue;
+ if (variable_type == null)
+ continue;
- if (variable_type.IsPointer) {
- //
- // Am not really convinced that this test is required (Microsoft does it)
- // but the fact is that you would not be able to use the pointer variable
- // *anyways*
- //
- if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
- vi.Location))
- continue;
- }
+ Expression cv = (Expression) constants [name];
+ if (cv == null)
+ continue;
- if (constants == null)
- continue;
+ // Don't let 'const int Foo = Foo;' succeed.
+ // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
+ // which in turn causes the 'must be constant' error to be triggered.
+ constants.Remove (name);
- Expression cv = (Expression) constants [name];
- if (cv == null)
- continue;
+ if (!Const.IsConstantTypeValid (variable_type)) {
+ Const.Error_InvalidConstantType (variable_type, loc);
+ continue;
+ }
- // Don't let 'const int Foo = Foo;' succeed.
- // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
- // which in turn causes the 'must be constant' error to be triggered.
- constants.Remove (name);
+ ec.CurrentBlock = this;
+ Expression e;
+ using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
+ e = cv.Resolve (ec);
+ }
+ if (e == null)
+ continue;
- if (!Const.IsConstantTypeValid (variable_type)) {
- Const.Error_InvalidConstantType (variable_type, loc);
- continue;
- }
+ Constant ce = e as Constant;
+ if (ce == null) {
+ Const.Error_ExpressionMustBeConstant (vi.Location, name);
+ continue;
+ }
- using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
- ec.CurrentBlock = this;
- Expression e = cv.Resolve (ec);
- if (e == null)
- continue;
+ e = ce.ConvertImplicitly (variable_type);
+ if (e == null) {
+ if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
+ Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
+ else
+ ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
+ continue;
+ }
- Constant ce = e as Constant;
- if (ce == null) {
- Const.Error_ExpressionMustBeConstant (vi.Location, name);
- continue;
- }
+ constants.Add (name, e);
+ vi.IsConstant = true;
+ }
+ }
- e = ce.ImplicitConversionRequired (variable_type, vi.Location);
- if (e == null)
- continue;
+ protected void ResolveMeta (EmitContext ec, int offset)
+ {
+ Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
- if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue) {
- Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
- continue;
- }
+ // If some parent block was unsafe, we remain unsafe even if this block
+ // isn't explicitly marked as such.
+ using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
+ flags |= Flags.VariablesInitialized;
- constants.Add (name, e);
- vi.IsConstant = true;
- }
+ if (variables != null) {
+ foreach (LocalInfo li in variables.Values) {
+ if (!li.Resolve (ec))
+ continue;
+ li.VariableInfo = new VariableInfo (li, offset);
+ offset += li.VariableInfo.Length;
}
}
+ assignable_slots = offset;
- //
- // Now, handle the children
- //
- if (children != null) {
- foreach (Block b in children)
- b.ResolveMeta (toplevel, ec, ip);
- }
+ DoResolveConstants (ec);
+
+ if (children == null)
+ return;
+ foreach (Block b in children)
+ b.ResolveMeta (ec, offset);
}
}
//
public virtual void EmitMeta (EmitContext ec)
{
- Report.Debug (64, "BLOCK EMIT META", this, Toplevel, ScopeInfo, ec);
+ Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
if (ScopeInfo != null) {
scope_init = ScopeInfo.GetScopeInitializer (ec);
Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
// from the beginning of the function. The outer Resolve() that detected the unreachability is
// responsible for handling the situation.
//
- int statement_count = statements.Count;
- for (int ix = 0; ix < statement_count; ix++){
- Statement s = (Statement) statements [ix];
+ for (current_statement = 0; current_statement < statements.Count; current_statement++) {
+ Statement s = (Statement) statements [current_statement];
// Check possible empty statement (CS0642)
if (RootContext.WarningLevel >= 3 &&
- ix + 1 < statement_count &&
- statements [ix + 1] is Block)
+ current_statement + 1 < statements.Count &&
+ statements [current_statement + 1] is Block)
CheckPossibleMistakenEmptyStatement (s);
//
if (!s.Resolve (ec)) {
ok = false;
- statements [ix] = EmptyStatement.Value;
+ statements [current_statement] = EmptyStatement.Value;
continue;
}
if (unreachable && !(s is LabeledStatement) && !(s is Block))
- statements [ix] = EmptyStatement.Value;
+ statements [current_statement] = EmptyStatement.Value;
- num_statements = ix + 1;
+ num_statements = current_statement + 1;
- unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
+ unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
if (unreachable && s is LabeledStatement)
throw new InternalErrorException ("should not happen");
}
Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
- ec.CurrentBranching, statement_count, num_statements);
+ ec.CurrentBranching, statements.Count, num_statements);
+
+ if (!ok)
+ return false;
while (ec.CurrentBranching is FlowBranchingLabeled)
ec.EndFlowBranching ();
// 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 ((flags & Flags.IsToplevel) != 0 &&
- !Toplevel.IsThisAssigned (ec) &&
- !vector.Reachability.AlwaysThrows)
+ if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
ok = false;
if ((labels != null) && (RootContext.WarningLevel >= 2)) {
Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
- if (vector.Reachability.IsUnreachable)
+ if (vector.IsUnreachable)
flags |= Flags.HasRet;
if (ok && (errors == Report.Errors)) {
{
for (int ix = 0; ix < num_statements; ix++){
Statement s = (Statement) statements [ix];
-
- // Check whether we are the last statement in a
- // top-level block.
-
- if (((Parent == null) || Implicit) && (ix+1 == num_statements) && !(s is Block))
- ec.IsLastStatement = true;
- else
- ec.IsLastStatement = false;
-
s.Emit (ec);
}
}
ec.CurrentBlock = this;
bool emit_debug_info = (CodeGen.SymbolWriter != null);
- bool is_lexical_block = !Implicit && (Parent != null);
+ bool is_lexical_block = this == Explicit && Parent != null;
+
+ if (emit_debug_info) {
+ if (is_lexical_block)
+ ec.BeginScope ();
+ }
+ ec.Mark (StartLocation, true);
+ if (scope_init != null)
+ scope_init.EmitStatement (ec);
+ DoEmit (ec);
+ ec.Mark (EndLocation, true);
+
+ if (emit_debug_info) {
+ if (is_lexical_block)
+ ec.EndScope ();
+
+ if (variables != null) {
+ foreach (DictionaryEntry de in variables) {
+ string name = (string) de.Key;
+ LocalInfo vi = (LocalInfo) de.Value;
+
+ vi.EmitSymbolInfo (ec, name);
+ }
+ }
+ }
+
+ ec.CurrentBlock = prev_block;
+ }
+
+ public override string ToString ()
+ {
+ return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
+ }
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Block target = (Block) t;
+
+ clonectx.AddBlockMap (this, target);
+
+ target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
+ target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
+ if (Parent != null)
+ target.Parent = clonectx.LookupBlock (Parent);
+
+ target.statements = new ArrayList ();
+ if (target.children != null){
+ target.children = new ArrayList ();
+ foreach (Block b in children){
+ Block newblock = (Block) b.Clone (clonectx);
+
+ target.children.Add (newblock);
+ }
+
+ }
+
+ foreach (Statement s in statements)
+ target.statements.Add (s.Clone (clonectx));
+
+ if (variables != null){
+ target.variables = new Hashtable ();
+
+ foreach (DictionaryEntry de in variables){
+ LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
+ target.variables [de.Key] = newlocal;
+ clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
+ }
+ }
+
+ //
+ // TODO: labels, switch_block, constants (?), anonymous_children
+ //
+ }
+ }
+
+ public class ExplicitBlock : Block {
+ public ExplicitBlock (Block parent, Location start, Location end)
+ : this (parent, (Flags) 0, start, end)
+ {
+ }
+
+ public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
+ : base (parent, flags, start, end)
+ {
+ this.Explicit = this;
+ }
+
+ Hashtable known_variables;
+
+ // <summary>
+ // Marks a variable with name @name as being used in this or a child block.
+ // If a variable name has been used in a child block, it's illegal to
+ // declare a variable with the same name in the current block.
+ // </summary>
+ internal void AddKnownVariable (string name, IKnownVariable info)
+ {
+ if (known_variables == null)
+ known_variables = new Hashtable ();
- if (emit_debug_info) {
- if (is_lexical_block)
- ec.BeginScope ();
- }
- ec.Mark (StartLocation, true);
- if (scope_init != null)
- scope_init.EmitStatement (ec);
- DoEmit (ec);
- ec.Mark (EndLocation, true);
+ known_variables [name] = info;
- if (emit_debug_info && is_lexical_block)
- ec.EndScope ();
+ if (Parent != null)
+ Parent.Explicit.AddKnownVariable (name, info);
+ }
- ec.CurrentBlock = prev_block;
+ internal IKnownVariable GetKnownVariable (string name)
+ {
+ return known_variables == null ? null : (IKnownVariable) known_variables [name];
}
- //
- // Returns true if we ar ea child of `b'.
- //
- public bool IsChildOf (Block b)
+ protected override void CloneTo (CloneContext clonectx, Statement t)
{
- Block current = this;
-
- do {
- if (current.Parent == b)
- return true;
- current = current.Parent;
- } while (current != null);
- return false;
+ ExplicitBlock target = (ExplicitBlock) t;
+ target.known_variables = null;
+ base.CloneTo (clonectx, t);
}
+ }
- public override string ToString ()
+ public class ToplevelParameterInfo : IKnownVariable {
+ public readonly ToplevelBlock Block;
+ public readonly int Index;
+ public VariableInfo VariableInfo;
+
+ Block IKnownVariable.Block {
+ get { return Block; }
+ }
+ public Parameter Parameter {
+ get { return Block.Parameters [Index]; }
+ }
+ public Location Location {
+ get { return Parameter.Location; }
+ }
+
+ public ToplevelParameterInfo (ToplevelBlock block, int idx)
{
- return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
+ this.Block = block;
+ this.Index = idx;
}
}
// 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
- //
- Block container;
- ToplevelBlock child;
+ public class ToplevelBlock : ExplicitBlock {
GenericMethod generic;
FlowBranchingToplevel top_level_branching;
- AnonymousMethodHost anonymous_method_host;
+ AnonymousContainer anonymous_container;
+ RootScopeInfo root_scope;
public bool HasVarargs {
get { return (flags & Flags.HasVarargs) != 0; }
public bool CompleteContexts (EmitContext ec)
{
- Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this,
- container, anonymous_method_host);
+ Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
- if (anonymous_method_host != null)
- anonymous_method_host.LinkScopes ();
+ if (root_scope != null)
+ root_scope.LinkScopes ();
- if ((container == null) && (anonymous_method_host != null)) {
- Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this,
- anonymous_method_host);
+ if (Parent == null && root_scope != null) {
+ Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
- if (anonymous_method_host.DefineType () == null)
+ if (root_scope.DefineType () == null)
return false;
- if (!anonymous_method_host.ResolveType ())
+ if (!root_scope.ResolveType ())
return false;
- if (!anonymous_method_host.ResolveMembers ())
+ if (!root_scope.ResolveMembers ())
return false;
- if (!anonymous_method_host.DefineMembers ())
+ if (!root_scope.DefineMembers ())
return false;
}
}
public ToplevelBlock Container {
- get { return container != null ? container.Toplevel : null; }
+ get { return Parent == null ? null : Parent.Toplevel; }
}
- public Block ContainerBlock {
- get { return container; }
+ public AnonymousContainer AnonymousContainer {
+ get { return anonymous_container; }
+ set { anonymous_container = value; }
}
- //
- // Parent is only used by anonymous blocks to link back to their
- // parents
- //
- public ToplevelBlock (Block container, Parameters parameters, Location start) :
- this (container, (Flags) 0, parameters, start)
+ public ToplevelBlock (Block parent, Parameters parameters, Location start) :
+ this (parent, (Flags) 0, parameters, start)
{
}
- public ToplevelBlock (Block container, Parameters parameters, GenericMethod generic,
- Location start) :
- this (container, parameters, start)
+ public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
+ this (parent, parameters, start)
{
this.generic = generic;
}
{
}
- public ToplevelBlock (Block container, Flags flags, Parameters parameters, Location start) :
- base (null, flags | Flags.IsToplevel, start, Location.Null)
+ // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
+ // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
+ public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
+ base (null, flags, start, Location.Null)
{
+ this.Toplevel = this;
+
this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
- this.container = container;
+ this.Parent = parent;
+ if (parent != null)
+ parent.AddAnonymousChild (this);
+
+ if (this.parameters.Count != 0)
+ ProcessParameters ();
}
public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
{
}
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ ToplevelBlock target = (ToplevelBlock) t;
+ base.CloneTo (clonectx, t);
+
+ if (parameters.Count != 0)
+ target.parameter_info = new ToplevelParameterInfo [parameters.Count];
+ for (int i = 0; i < parameters.Count; ++i)
+ target.parameter_info [i] = new ToplevelParameterInfo (target, i);
+ }
+
public bool CheckError158 (string name, Location loc)
{
if (AnonymousChildren != null) {
return true;
}
+ ToplevelParameterInfo [] parameter_info;
+ void ProcessParameters ()
+ {
+ int n = parameters.Count;
+ parameter_info = new ToplevelParameterInfo [n];
+ for (int i = 0; i < n; ++i) {
+ parameter_info [i] = new ToplevelParameterInfo (this, i);
+
+ string name = parameters [i].Name;
+
+ LocalInfo vi = GetLocalInfo (name);
+ if (vi != null) {
+ Report.SymbolRelatedToPreviousError (vi.Location, name);
+ Error_AlreadyDeclared (loc, name, "parent or current");
+ continue;
+ }
+
+ ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
+ if (pi != null) {
+ Report.SymbolRelatedToPreviousError (pi.Location, name);
+ Error_AlreadyDeclared (loc, name, "parent or current");
+ continue;
+ }
+
+ AddKnownVariable (name, parameter_info [i]);
+ }
+
+ // mark this block as "used" so that we create local declarations in a sub-block
+ // FIXME: This appears to uncover a lot of bugs
+ //this.Use ();
+ }
+
bool DoCheckError158 (string name, Location loc)
{
LabeledStatement s = LookupLabel (name);
if (s != null) {
+ Report.SymbolRelatedToPreviousError (s.loc, s.Name);
Error_158 (name, loc);
return false;
}
return true;
}
- public AnonymousMethodHost CreateAnonymousMethodHost (TypeContainer host)
+ public RootScopeInfo CreateRootScope (TypeContainer host)
{
- if (anonymous_method_host != null)
- return anonymous_method_host;
+ if (root_scope != null)
+ return root_scope;
- if (Container != null)
- anonymous_method_host = new AnonymousMethodHost (
- this, Container.anonymous_method_host, null, StartLocation);
- else
- anonymous_method_host = new AnonymousMethodHost (
+ if (Container == null)
+ root_scope = new RootScopeInfo (
this, host, generic, StartLocation);
- ScopeInfo = anonymous_method_host;
- return anonymous_method_host;
+ if (scope_info != null)
+ throw new InternalErrorException ();
+
+ scope_info = root_scope;
+ return root_scope;
}
- public void CreateIteratorHost (AnonymousMethodHost root_scope)
+ public void CreateIteratorHost (RootScopeInfo root)
{
- Report.Debug (64, "CREATE ITERATOR HOST", this, root_scope,
- container, anonymous_method_host);
+ Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
- if ((container != null) || (anonymous_method_host != null))
+ if (Parent != null || root_scope != null)
throw new InternalErrorException ();
- ScopeInfo = anonymous_method_host = root_scope;
+ scope_info = root_scope = root;
}
- public AnonymousMethodHost AnonymousMethodHost {
+ public RootScopeInfo RootScope {
get {
- if (anonymous_method_host != null)
- return anonymous_method_host;
+ if (root_scope != null)
+ return root_scope;
else if (Container != null)
- return Container.AnonymousMethodHost;
+ return Container.RootScope;
else
return null;
}
}
- public void EmitScopeInstance (EmitContext ec, ScopeInfo scope)
- {
- AnonymousMethodHost root_scope = AnonymousMethodHost;
-
- root_scope.EmitScopeInstance (ec);
- while (root_scope != scope.Host) {
- ec.ig.Emit (OpCodes.Ldfld, root_scope.ParentLink.FieldBuilder);
- root_scope = root_scope.ParentHost;
-
- if (root_scope == null)
- throw new InternalErrorException (
- "Never found scope {0} starting at block {1}",
- scope, ec.CurrentBlock.ID);
- }
-
- if (scope != scope.Host)
- scope.ScopeInstance.Emit (ec);
- }
-
public FlowBranchingToplevel TopLevelBranching {
get { return top_level_branching; }
}
//
public void ReParent (ToplevelBlock new_parent)
{
- container = new_parent;
- Parent = new_parent;
- new_parent.child = this;
+ if ((flags & Flags.VariablesInitialized) != 0)
+ throw new InternalErrorException ("block has already been resolved");
-#if FIXME
- if (container != null)
- container.AddAnonymousChild (this);
-#endif
+ Parent = new_parent;
}
//
//
public ParameterReference GetParameterReference (string name, Location loc)
{
- Parameter par;
- int idx;
+ ToplevelParameterInfo p = GetParameterInfo (name);
+ return p == null ? null : new ParameterReference (this, p, loc);
+ }
+ public ToplevelParameterInfo GetParameterInfo (string name)
+ {
+ int idx;
for (ToplevelBlock t = this; t != null; t = t.Container) {
- Parameters pars = t.Parameters;
- par = pars.GetParameterByName (name, out idx);
+ Parameter par = t.Parameters.GetParameterByName (name, out idx);
if (par != null)
- return new ParameterReference (par, this, idx, loc);
+ return t.parameter_info [idx];
}
return null;
}
public bool IsThisAssigned (EmitContext ec)
{
- return this_variable == null || this_variable.IsThisAssigned (ec, loc);
+ return this_variable == null || this_variable.IsThisAssigned (ec);
}
public bool ResolveMeta (EmitContext ec, Parameters ip)
{
int errors = Report.Errors;
+ int orig_count = parameters.Count;
if (top_level_branching != null)
return true;
if (ip != null)
parameters = ip;
- if (!IsIterator && (container != null) && (parameters != null)) {
- foreach (Parameter p in parameters.FixedParameters) {
- if (!CheckError136_InParents (p.Name, loc))
- return false;
- }
- }
+ // Assert: orig_count != parameter.Count => orig_count == 0
+ if (orig_count != 0 && orig_count != parameters.Count)
+ throw new InternalErrorException ("parameter information mismatch");
+
+ int offset = Parent == null ? 0 : Parent.AssignableSlots;
- ResolveMeta (this, ec, ip);
+ for (int i = 0; i < orig_count; ++i) {
+ Parameter.Modifier mod = parameters.ParameterModifier (i);
+
+ if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
+ continue;
+
+ VariableInfo vi = new VariableInfo (ip, i, offset);
+ parameter_info [i].VariableInfo = vi;
+ offset += vi.Length;
+ }
- if (child != null)
- child.ResolveMeta (this, ec, ip);
+ ResolveMeta (ec, offset);
top_level_branching = ec.StartFlowBranching (this);
return Report.Errors == errors;
}
+ // <summary>
+ // Check whether all `out' parameters have been assigned.
+ // </summary>
+ public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
+ {
+ if (vector.IsUnreachable)
+ return;
+
+ int n = parameter_info == null ? 0 : parameter_info.Length;
+
+ for (int i = 0; i < n; i++) {
+ VariableInfo var = parameter_info [i].VariableInfo;
+
+ if (var == null)
+ continue;
+
+ if (vector.IsAssigned (var, false))
+ continue;
+
+ Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
+ var.Name);
+ }
+ }
+
public override void EmitMeta (EmitContext ec)
{
base.EmitMeta (ec);
{
flags |= Flags.IsIterator;
- Block block = new Block (this);
+ Block block = new ExplicitBlock (this, StartLocation, EndLocation);
foreach (Statement stmt in statements)
block.AddStatement (stmt);
statements = new ArrayList ();
public override string ToString ()
{
- return String.Format ("{0} ({1}:{2}{3})", GetType (), ID, StartLocation,
- anonymous_method_host);
+ return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
+ root_scope, anonymous_container != null ?
+ anonymous_container.Scope : null);
}
}
return true;
}
- public void Erorr_AlreadyOccurs ()
+ public void Erorr_AlreadyOccurs (Type switchType, SwitchLabel collisionWith)
{
string label;
if (converted == null)
label = "default";
else if (converted == NullStringCase)
label = "null";
+ else if (TypeManager.IsEnumType (switchType))
+ label = TypeManager.CSharpEnumValue (switchType, converted);
else
label = converted.ToString ();
-
+
+ Report.SymbolRelatedToPreviousError (collisionWith.loc, null);
Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
}
+
+ public SwitchLabel Clone (CloneContext clonectx)
+ {
+ return new SwitchLabel (label.Clone (clonectx), loc);
+ }
}
public class SwitchSection {
Labels = labels;
Block = block;
}
+
+ public SwitchSection Clone (CloneContext clonectx)
+ {
+ ArrayList cloned_labels = new ArrayList ();
+
+ foreach (SwitchLabel sl in cloned_labels)
+ cloned_labels.Add (sl.Clone (clonectx));
+
+ return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
+ }
}
public class Switch : Statement {
- public readonly ArrayList Sections;
+ public ArrayList Sections;
public Expression Expr;
/// <summary>
//
Expression SwitchGoverningType (EmitContext ec, Expression expr)
{
- Type t = expr.Type;
+ Type t = TypeManager.DropGenericTypeArguments (expr.Type);
if (t == TypeManager.byte_type ||
t == TypeManager.sbyte_type ||
foreach (SwitchLabel sl in ss.Labels){
if (sl.Label == null){
if (default_section != null){
- sl.Erorr_AlreadyOccurs ();
+ sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
error = true;
}
default_section = ss;
try {
Elements.Add (key, sl);
} catch (ArgumentException) {
- sl.Erorr_AlreadyOccurs ();
+ sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
error = true;
}
}
if (!fFoundDefault) {
ig.MarkLabel (lblDefault);
+ if (HaveUnwrap && !fFoundNull) {
+ ig.MarkLabel (null_target);
+ }
}
+
ig.MarkLabel (lblEnd);
}
//
bool pending_goto_end = false;
bool null_marked = false;
bool null_found;
+ int section_count = Sections.Count;
+
+ // TODO: implement switch optimization for string by using Hashtable
+ //if (SwitchType == TypeManager.string_type && section_count > 7)
+ // Console.WriteLine ("Switch optimization possible " + loc);
ig.Emit (OpCodes.Ldloc, val);
ig.Emit (OpCodes.Brfalse, default_target);
ig.Emit (OpCodes.Ldloc, val);
- ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
+ ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
ig.Emit (OpCodes.Stloc, val);
- int section_count = Sections.Count;
for (int section = 0; section < section_count; section++){
SwitchSection ss = (SwitchSection) Sections [section];
// Validate switch.
SwitchType = new_expr.Type;
+ if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
+ Report.FeatureIsNotISO1 (loc, "switch expression of boolean type");
+ return false;
+ }
+
if (!CheckSwitch (ec))
return false;
ec.CurrentBranching.CreateSibling (
null, FlowBranching.SiblingType.SwitchSection);
- FlowBranching.Reachability reachability = ec.EndFlowBranching ();
+ ec.EndFlowBranching ();
ec.Switch = old_switch;
- Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching,
- reachability);
+ Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
return true;
}
ec.LoopEnd = old_end;
ec.Switch = old_switch;
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Switch target = (Switch) t;
+
+ target.Expr = Expr.Clone (clonectx);
+ target.Sections = new ArrayList ();
+ foreach (SwitchSection ss in Sections){
+ target.Sections.Add (ss.Clone (clonectx));
+ }
+ }
}
public abstract class ExceptionStatement : Statement
FlowBranchingException branching = ec.StartFlowBranching (this);
bool ok = Statement.Resolve (ec);
- if (!ok) {
- ec.KillFlowBranching ();
- return false;
- }
ResolveFinally (branching);
- FlowBranching.Reachability reachability = ec.EndFlowBranching ();
- if (!reachability.AlwaysReturns) {
- // Unfortunately, System.Reflection.Emit automatically emits
- // a leave to the end of the finally block.
- // This is a problem if `returns' is true since we may jump
- // to a point after the end of the method.
- // As a workaround, emit an explicit ret here.
- ec.NeedReturnLabel ();
- }
+ ec.EndFlowBranching ();
+
+ // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
+ // So, ensure there's some IL code after the finally block.
+ ec.NeedReturnLabel ();
// Avoid creating libraries that reference the internal
// mcs NullType:
temp = new TemporaryVariable (t, loc);
temp.Resolve (ec);
- return true;
+ return ok;
}
protected override void DoEmit (EmitContext ec)
temp.Emit (ec);
ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Lock target = (Lock) t;
+
+ target.expr = expr.Clone (clonectx);
+ target.Statement = Statement.Clone (clonectx);
+ }
}
public class Unchecked : Statement {
- public readonly Block Block;
+ public Block Block;
public Unchecked (Block b)
{
using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
Block.Emit (ec);
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Unchecked target = (Unchecked) t;
+
+ target.Block = clonectx.LookupBlock (Block);
+ }
}
public class Checked : Statement {
- public readonly Block Block;
+ public Block Block;
public Checked (Block b)
{
using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
Block.Emit (ec);
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Checked target = (Checked) t;
+
+ target.Block = clonectx.LookupBlock (Block);
+ }
}
public class Unsafe : Statement {
- public readonly Block Block;
+ public Block Block;
public Unsafe (Block b)
{
using (ec.With (EmitContext.Flags.InUnsafe, true))
Block.Emit (ec);
}
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Unsafe target = (Unsafe) t;
+
+ target.Block = clonectx.LookupBlock (Block);
+ }
}
//
foreach (Pair p in declarators){
LocalInfo vi = (LocalInfo) p.First;
Expression e = (Expression) p.Second;
-
+
vi.VariableInfo.SetAssigned (ec);
vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
return false;
if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
- e.Error_ValueCannotBeConverted (e.Location, expr_type, false);
+ e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
return false;
}
}
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
+ bool ok = statement.Resolve (ec);
+ bool flow_unreachable = ec.EndFlowBranching ();
+ has_ret = flow_unreachable;
- if (!statement.Resolve (ec)) {
- ec.KillFlowBranching ();
- return false;
- }
-
- FlowBranching.Reachability reachability = ec.EndFlowBranching ();
- has_ret = reachability.IsUnreachable;
-
- return true;
+ return ok;
}
protected override void DoEmit (EmitContext ec)
data [i].EmitExit (ec);
}
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Fixed target = (Fixed) t;
+
+ target.type = type.Clone (clonectx);
+ target.declarators = new ArrayList ();
+ foreach (LocalInfo var in declarators)
+ target.declarators.Add (clonectx.LookupVariable (var));
+ target.statement = statement.Clone (clonectx);
+ }
}
public class Catch : Statement {
public readonly string Name;
- public readonly Block Block;
- public readonly Block VarBlock;
+ public Block Block;
+ public Block VarBlock;
Expression type_expr;
Type type;
return true;
}
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Catch target = (Catch) t;
+
+ target.type_expr = type_expr.Clone (clonectx);
+ target.Block = clonectx.LookupBlock (Block);
+ target.VarBlock = clonectx.LookupBlock (VarBlock);
+ }
}
public class Try : ExceptionStatement {
- public readonly Block Fini, Block;
- public readonly ArrayList Specific;
- public readonly Catch General;
+ public Block Fini, Block;
+ public ArrayList Specific;
+ public Catch General;
bool need_exc_block;
} else
emit_finally = Fini != null;
- FlowBranching.Reachability reachability = ec.EndFlowBranching ();
+ ec.EndFlowBranching ();
- FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
+ // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
+ // So, ensure there's some IL code after the finally block.
+ ec.NeedReturnLabel ();
- Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
+ FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
- if (!reachability.AlwaysReturns) {
- // Unfortunately, System.Reflection.Emit automatically emits
- // a leave to the end of the finally block. This is a problem
- // if `returns' is true since we may jump to a point after the
- // end of the method.
- // As a workaround, emit an explicit ret here.
- ec.NeedReturnLabel ();
- }
+ Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
return ok;
}
return General != null || Specific.Count > 0;
}
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Try target = (Try) t;
+
+ target.Block = clonectx.LookupBlock (Block);
+ if (Fini != null)
+ target.Fini = clonectx.LookupBlock (Fini);
+ if (General != null)
+ target.General = (Catch) General.Clone (clonectx);
+ if (Specific != null){
+ target.Specific = new ArrayList ();
+ foreach (Catch c in Specific)
+ target.Specific.Add (c.Clone (clonectx));
+ }
+ }
}
public class Using : ExceptionStatement {
Type expr_type;
Expression [] resolved_vars;
Expression [] converted_vars;
- ExpressionStatement [] assign;
+ Expression [] assign;
TemporaryVariable local_copy;
public Using (object expression_or_block, Statement stmt, Location l)
//
bool ResolveLocalVariableDecls (EmitContext ec)
{
- int i = 0;
-
- TypeExpr texpr = expr.ResolveAsTypeTerminal (ec, false);
- if (texpr == null)
- return false;
-
- expr_type = texpr.Type;
-
- //
- // The type must be an IDisposable or an implicit conversion
- // must exist.
- //
- converted_vars = new Expression [var_list.Count];
- resolved_vars = new Expression [var_list.Count];
- assign = new ExpressionStatement [var_list.Count];
-
- bool need_conv = !TypeManager.ImplementsInterface (
- expr_type, TypeManager.idisposable_type);
+ resolved_vars = new Expression[var_list.Count];
+ assign = new Expression [var_list.Count];
+ converted_vars = new Expression[var_list.Count];
- foreach (DictionaryEntry e in var_list){
+ for (int i = 0; i < assign.Length; ++i) {
+ DictionaryEntry e = (DictionaryEntry) var_list [i];
Expression var = (Expression) e.Key;
+ Expression new_expr = (Expression) e.Value;
- var = var.ResolveLValue (ec, new EmptyExpression (), loc);
- if (var == null)
+ Expression a = new Assign (var, new_expr, loc);
+ a = a.Resolve (ec);
+ if (a == null)
return false;
resolved_vars [i] = var;
+ assign [i] = a;
- if (!need_conv) {
- i++;
+ if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
+ converted_vars [i] = var;
continue;
}
- converted_vars [i] = Convert.ImplicitConversion (
- ec, var, TypeManager.idisposable_type, loc);
-
- if (converted_vars [i] == null) {
- Error_IsNotConvertibleToIDisposable ();
+ a = Convert.ImplicitConversion (ec, a, TypeManager.idisposable_type, var.Location);
+ if (a == null) {
+ Error_IsNotConvertibleToIDisposable (var);
return false;
}
- i++;
- }
-
- i = 0;
- foreach (DictionaryEntry e in var_list){
- Expression var = resolved_vars [i];
- Expression new_expr = (Expression) e.Value;
- Expression a;
-
- a = new Assign (var, new_expr, loc);
- a = a.Resolve (ec);
- if (a == null)
- return false;
-
- if (!need_conv)
- converted_vars [i] = var;
- assign [i] = (ExpressionStatement) a;
- i++;
+ converted_vars [i] = a;
}
return true;
}
- void Error_IsNotConvertibleToIDisposable ()
+ static void Error_IsNotConvertibleToIDisposable (Expression expr)
{
- Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
- TypeManager.CSharpName (expr_type));
+ Report.SymbolRelatedToPreviousError (expr.Type);
+ Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
+ expr.GetSignatureForError ());
}
bool ResolveExpression (EmitContext ec)
{
if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
- Error_IsNotConvertibleToIDisposable ();
+ Error_IsNotConvertibleToIDisposable (expr);
return false;
}
}
int i = 0;
for (i = 0; i < assign.Length; i++) {
- assign [i].EmitStatement (ec);
+ assign [i].Emit (ec);
if (emit_finally)
ig.BeginExceptionBlock ();
bool ok = Statement.Resolve (ec);
- if (!ok) {
- ec.KillFlowBranching ();
- return false;
- }
-
ResolveFinally (branching);
- FlowBranching.Reachability reachability = ec.EndFlowBranching ();
- if (!reachability.AlwaysReturns) {
- // Unfortunately, System.Reflection.Emit automatically emits a leave
- // to the end of the finally block. This is a problem if `returns'
- // is true since we may jump to a point after the end of the method.
- // As a workaround, emit an explicit ret here.
- ec.NeedReturnLabel ();
- }
+ ec.EndFlowBranching ();
- return true;
+ // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
+ // So, ensure there's some IL code after the finally block.
+ ec.NeedReturnLabel ();
+
+ return ok;
}
protected override void DoEmit (EmitContext ec)
else if (expression_or_block is Expression)
EmitExpressionFinally (ec);
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Using target = (Using) t;
+
+ if (expression_or_block is Expression)
+ target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
+ else
+ target.expression_or_block = ((Statement) expression_or_block).Clone (clonectx);
+
+ target.Statement = Statement.Clone (clonectx);
+ }
}
/// <summary>
if (expr == null)
return false;
- Constant c = expr as Constant;
- if (c != null && c.GetValue () == null) {
+ if (expr.Type == TypeManager.null_type) {
Report.Error (186, loc, "Use of null is not valid in this context");
return false;
}
- TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
- if (texpr == null)
- return false;
-
- Type var_type = texpr.Type;
-
if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
expr.ExprClassName);
}
if (expr.Type.IsArray) {
- array = new ArrayForeach (var_type, variable, expr, statement, loc);
+ array = new ArrayForeach (type, variable, expr, statement, loc);
return array.Resolve (ec);
- } else {
- collection = new CollectionForeach (
- var_type, variable, expr, statement, loc);
- return collection.Resolve (ec);
}
+
+ collection = new CollectionForeach (type, variable, expr, statement, loc);
+ return collection.Resolve (ec);
}
protected override void DoEmit (EmitContext ec)
Expression variable, expr, conv;
Statement statement;
Type array_type;
- Type var_type;
+ Expression var_type;
TemporaryVariable[] lengths;
ArrayCounter[] counter;
int rank;
TemporaryVariable copy;
Expression access;
- public ArrayForeach (Type var_type, Expression var,
+ public ArrayForeach (Expression var_type, Expression var,
Expression expr, Statement stmt, Location l)
{
this.var_type = var_type;
if (access == null)
return false;
- conv = Convert.ExplicitConversion (ec, access, var_type, loc);
+ VarExpr ve = var_type as VarExpr;
+ if (ve != null) {
+ // Infer implicitly typed local variable from foreach array type
+ var_type = new TypeExpression (access.Type, ve.Location);
+ }
+
+ var_type = var_type.ResolveAsTypeTerminal (ec, false);
+ if (var_type == null)
+ return false;
+
+ conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
if (conv == null)
return false;
MethodGroupExpr get_enumerator;
PropertyExpr get_current;
MethodInfo move_next;
- Type var_type, enumerator_type;
+ Expression var_type;
+ Type enumerator_type;
bool is_disposable;
bool enumerator_found;
- public CollectionForeach (Type var_type, Expression var,
+ public CollectionForeach (Expression var_type, Expression var,
Expression expr, Statement stmt, Location l)
{
this.var_type = var_type;
TypeManager.CSharpName (expr.Type));
}
+ public static bool IsOverride (MethodInfo m)
+ {
+ m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
+
+ if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
+ return false;
+ if (m is MethodBuilder)
+ return true;
+
+ MethodInfo base_method = m.GetBaseDefinition ();
+ return base_method != m;
+ }
+
bool TryType (EmitContext ec, Type t)
{
MethodGroupExpr mg = Expression.MemberLookup (
if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
continue;
- if (TypeManager.IsOverride (mi))
+ if (IsOverride (mi))
continue;
enumerator_found = true;
if (!TypeManager.IsGenericType (mi.ReturnType))
continue;
- Report.SymbolRelatedToPreviousError(t);
+ MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
+ Report.SymbolRelatedToPreviousError (t);
Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
- "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
- TypeManager.CSharpName (t), TypeManager.CSharpSignature (mi));
+ "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
+ TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
+ return false;
+ }
+
+ // Always prefer generics enumerators
+ if (!TypeManager.IsGenericType (mi.ReturnType)) {
+ if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
+ TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
+ continue;
+
+ Report.SymbolRelatedToPreviousError (result);
+ Report.SymbolRelatedToPreviousError (mi);
+ Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
+ TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
return false;
}
- Report.SymbolRelatedToPreviousError (result);
- Report.SymbolRelatedToPreviousError (mi);
- Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
- TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
}
result = mi;
tmp_move_next = move_next;
//
// Now try to find the method in the interfaces
//
- while (t != null){
- Type [] ifaces = t.GetInterfaces ();
-
- foreach (Type i in ifaces){
- if (TryType (ec, i))
- return true;
- }
-
- //
- // 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;
+ Type [] ifaces = TypeManager.GetInterfaces (t);
+ foreach (Type i in ifaces){
+ if (TryType (ec, i))
+ return true;
}
return false;
return false;
}
+ VarExpr ve = var_type as VarExpr;
+ if (ve != null) {
+ // Infer implicitly typed local variable from foreach enumerable type
+ var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
+ }
+
+ var_type = var_type.ResolveAsTypeTerminal (ec, false);
+ if (var_type == null)
+ return false;
+
enumerator = new TemporaryVariable (enumerator_type, loc);
enumerator.Resolve (ec);
get_current.InstanceExpression = enumerator;
Statement block = new CollectionForeachStatement (
- var_type, variable, get_current, statement, loc);
+ var_type.Type, variable, get_current, statement, loc);
loop = new While (move_next_expr, block, loc);
statement.Emit (ec);
}
}
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ Foreach target = (Foreach) t;
+
+ target.type = type.Clone (clonectx);
+ target.variable = variable.Clone (clonectx);
+ target.expr = expr.Clone (clonectx);
+ target.statement = statement.Clone (clonectx);
+ }
}
}