//
// statement.cs: Statement representation for the IL tree.
//
// Author:
// Miguel de Icaza (miguel@ximian.com)
// Martin Baulig (martin@ximian.com)
// Marek Safar (marek.safar@seznam.cz)
//
// Copyright 2001, 2002, 2003 Ximian, Inc.
// Copyright 2003, 2004 Novell, Inc.
//
using System;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
using System.Collections.Generic;
namespace Mono.CSharp {
public abstract class Statement {
public Location loc;
///
/// Resolves the statement, true means that all sub-statements
/// did resolve ok.
//
public virtual bool Resolve (BlockContext ec)
{
return true;
}
///
/// We already know that the statement is unreachable, but we still
/// need to resolve it to catch errors.
///
public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
{
//
// This conflicts with csc's way of doing this, but IMHO it's
// the right thing to do.
//
// If something is unreachable, we still check whether it's
// correct. This means that you cannot use unassigned variables
// in unreachable code, for instance.
//
if (warn)
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
ec.KillFlowBranching ();
return ok;
}
///
/// Return value indicates whether all code paths emitted return.
///
protected abstract void DoEmit (EmitContext ec);
public virtual void Emit (EmitContext ec)
{
ec.Mark (loc);
DoEmit (ec);
}
//
// This routine must be overrided in derived classes and make copies
// of all the data that might be modified if resolved
//
protected abstract void CloneTo (CloneContext clonectx, Statement target);
public Statement Clone (CloneContext clonectx)
{
Statement s = (Statement) this.MemberwiseClone ();
CloneTo (clonectx, s);
return s;
}
public virtual Expression CreateExpressionTree (ResolveContext ec)
{
ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
return null;
}
public Statement PerformClone ()
{
CloneContext clonectx = new CloneContext ();
return Clone (clonectx);
}
}
public sealed class EmptyStatement : Statement
{
public EmptyStatement (Location loc)
{
this.loc = loc;
}
public override bool Resolve (BlockContext ec)
{
return true;
}
public override bool ResolveUnreachable (BlockContext ec, bool warn)
{
return true;
}
public override void Emit (EmitContext ec)
{
}
protected override void DoEmit (EmitContext ec)
{
throw new NotSupportedException ();
}
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// nothing needed.
}
}
public class If : Statement {
Expression expr;
public Statement TrueStatement;
public Statement FalseStatement;
bool is_true_ret;
public If (Expression bool_expr, Statement true_statement, Location l)
: this (bool_expr, true_statement, null, l)
{
}
public If (Expression bool_expr,
Statement true_statement,
Statement false_statement,
Location l)
{
this.expr = bool_expr;
TrueStatement = true_statement;
FalseStatement = false_statement;
loc = l;
}
public override bool Resolve (BlockContext ec)
{
bool ok = true;
Report.Debug (1, "START IF BLOCK", loc);
expr = expr.Resolve (ec);
if (expr == null) {
ok = false;
} else {
//
// Dead code elimination
//
if (expr is Constant) {
bool take = !((Constant) expr).IsDefaultValue;
if (take) {
if (!TrueStatement.Resolve (ec))
return false;
if ((FalseStatement != null) &&
!FalseStatement.ResolveUnreachable (ec, true))
return false;
FalseStatement = null;
} else {
if (!TrueStatement.ResolveUnreachable (ec, true))
return false;
TrueStatement = null;
if ((FalseStatement != null) &&
!FalseStatement.Resolve (ec))
return false;
}
return true;
}
}
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
ok &= TrueStatement.Resolve (ec);
is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
ec.CurrentBranching.CreateSibling ();
if (FalseStatement != null)
ok &= FalseStatement.Resolve (ec);
ec.EndFlowBranching ();
Report.Debug (1, "END IF BLOCK", loc);
return ok;
}
protected override void DoEmit (EmitContext ec)
{
Label false_target = ec.DefineLabel ();
Label end;
//
// If we're a boolean constant, Resolve() already
// eliminated dead code for us.
//
Constant c = expr as Constant;
if (c != null){
c.EmitSideEffect (ec);
if (!c.IsDefaultValue)
TrueStatement.Emit (ec);
else if (FalseStatement != null)
FalseStatement.Emit (ec);
return;
}
expr.EmitBranchable (ec, false_target, false);
TrueStatement.Emit (ec);
if (FalseStatement != null){
bool branch_emitted = false;
end = ec.DefineLabel ();
if (!is_true_ret){
ec.Emit (OpCodes.Br, end);
branch_emitted = true;
}
ec.MarkLabel (false_target);
FalseStatement.Emit (ec);
if (branch_emitted)
ec.MarkLabel (end);
} else {
ec.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 Statement EmbeddedStatement;
public Do (Statement statement, BooleanExpression bool_expr, Location l)
{
expr = bool_expr;
EmbeddedStatement = statement;
loc = l;
}
public override bool Resolve (BlockContext ec)
{
bool ok = true;
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
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.IsUnreachable && !was_unreachable)
ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
expr = expr.Resolve (ec);
if (expr == null)
ok = false;
else if (expr is Constant){
bool infinite = !((Constant) expr).IsDefaultValue;
if (infinite)
ec.CurrentBranching.CurrentUsageVector.Goto ();
}
ec.EndFlowBranching ();
return ok;
}
protected override void DoEmit (EmitContext ec)
{
Label loop = ec.DefineLabel ();
Label old_begin = ec.LoopBegin;
Label old_end = ec.LoopEnd;
ec.LoopBegin = ec.DefineLabel ();
ec.LoopEnd = ec.DefineLabel ();
ec.MarkLabel (loop);
EmbeddedStatement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
//
// Dead code elimination
//
if (expr is Constant){
bool res = !((Constant) expr).IsDefaultValue;
expr.EmitSideEffect (ec);
if (res)
ec.Emit (OpCodes.Br, loop);
} else
expr.EmitBranchable (ec, loop, true);
ec.MarkLabel (ec.LoopEnd);
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 Statement Statement;
bool infinite, empty;
public While (BooleanExpression bool_expr, Statement statement, Location l)
{
this.expr = bool_expr;
Statement = statement;
loc = l;
}
public override bool Resolve (BlockContext ec)
{
bool ok = true;
expr = expr.Resolve (ec);
if (expr == null)
ok = false;
//
// Inform whether we are infinite or not
//
if (expr is Constant){
bool value = !((Constant) expr).IsDefaultValue;
if (value == false){
if (!Statement.ResolveUnreachable (ec, true))
return false;
empty = true;
return true;
} else
infinite = true;
}
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
if (!infinite)
ec.CurrentBranching.CreateSibling ();
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
if (!Statement.Resolve (ec))
ok = false;
ec.EndFlowBranching ();
// There's no direct control flow from the end of the embedded statement to the end of the loop
ec.CurrentBranching.CurrentUsageVector.Goto ();
ec.EndFlowBranching ();
return ok;
}
protected override void DoEmit (EmitContext ec)
{
if (empty) {
expr.EmitSideEffect (ec);
return;
}
Label old_begin = ec.LoopBegin;
Label old_end = ec.LoopEnd;
ec.LoopBegin = ec.DefineLabel ();
ec.LoopEnd = ec.DefineLabel ();
//
// Inform whether we are infinite or not
//
if (expr is Constant){
// expr is 'true', since the 'empty' case above handles the 'false' case
ec.MarkLabel (ec.LoopBegin);
expr.EmitSideEffect (ec);
Statement.Emit (ec);
ec.Emit (OpCodes.Br, ec.LoopBegin);
//
// Inform that we are infinite (ie, `we return'), only
// if we do not `break' inside the code.
//
ec.MarkLabel (ec.LoopEnd);
} else {
Label while_loop = ec.DefineLabel ();
ec.Emit (OpCodes.Br, ec.LoopBegin);
ec.MarkLabel (while_loop);
Statement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
ec.Mark (loc);
expr.EmitBranchable (ec, while_loop, true);
ec.MarkLabel (ec.LoopEnd);
}
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
}
public override void Emit (EmitContext ec)
{
DoEmit (ec);
}
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;
Statement InitStatement;
Statement Increment;
public Statement Statement;
bool infinite, empty;
public For (Statement init_statement,
BooleanExpression test,
Statement increment,
Statement statement,
Location l)
{
InitStatement = init_statement;
Test = test;
Increment = increment;
Statement = statement;
loc = l;
}
public override bool Resolve (BlockContext ec)
{
bool ok = true;
if (InitStatement != null){
if (!InitStatement.Resolve (ec))
ok = false;
}
if (Test != null){
Test = Test.Resolve (ec);
if (Test == null)
ok = false;
else if (Test is Constant){
bool value = !((Constant) Test).IsDefaultValue;
if (value == false){
if (!Statement.ResolveUnreachable (ec, true))
return false;
if ((Increment != null) &&
!Increment.ResolveUnreachable (ec, false))
return false;
empty = true;
return true;
} else
infinite = true;
}
} else
infinite = true;
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
if (!infinite)
ec.CurrentBranching.CreateSibling ();
bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
if (!Statement.Resolve (ec))
ok = false;
ec.EndFlowBranching ();
if (Increment != null){
if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
if (!Increment.ResolveUnreachable (ec, !was_unreachable))
ok = false;
} else {
if (!Increment.Resolve (ec))
ok = false;
}
}
// There's no direct control flow from the end of the embedded statement to the end of the loop
ec.CurrentBranching.CurrentUsageVector.Goto ();
ec.EndFlowBranching ();
return ok;
}
protected override void DoEmit (EmitContext ec)
{
if (InitStatement != null)
InitStatement.Emit (ec);
if (empty) {
Test.EmitSideEffect (ec);
return;
}
Label old_begin = ec.LoopBegin;
Label old_end = ec.LoopEnd;
Label loop = ec.DefineLabel ();
Label test = ec.DefineLabel ();
ec.LoopBegin = ec.DefineLabel ();
ec.LoopEnd = ec.DefineLabel ();
ec.Emit (OpCodes.Br, test);
ec.MarkLabel (loop);
Statement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
Increment.Emit (ec);
ec.MarkLabel (test);
//
// If test is null, there is no test, and we are just
// an infinite loop
//
if (Test != null){
//
// The Resolve code already catches the case for
// Test == Constant (false) so we know that
// this is true
//
if (Test is Constant) {
Test.EmitSideEffect (ec);
ec.Emit (OpCodes.Br, loop);
} else {
Test.EmitBranchable (ec, loop, true);
}
} else
ec.Emit (OpCodes.Br, loop);
ec.MarkLabel (ec.LoopEnd);
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 {
ExpressionStatement expr;
public StatementExpression (ExpressionStatement expr)
{
this.expr = expr;
loc = expr.Location;
}
public override bool Resolve (BlockContext ec)
{
expr = expr.ResolveStatement (ec);
return expr != null;
}
protected override void DoEmit (EmitContext ec)
{
expr.EmitStatement (ec);
}
public override string ToString ()
{
return "StatementExpression (" + expr + ")";
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
StatementExpression target = (StatementExpression) t;
target.expr = (ExpressionStatement) expr.Clone (clonectx);
}
}
//
// Simple version of statement list not requiring a block
//
public class StatementList : Statement
{
List statements;
public StatementList (Statement first, Statement second)
{
statements = new List () { first, second };
}
#region Properties
public IList Statements {
get {
return statements;
}
}
#endregion
public void Add (Statement statement)
{
statements.Add (statement);
}
public override bool Resolve (BlockContext ec)
{
foreach (var s in statements)
s.Resolve (ec);
return true;
}
protected override void DoEmit (EmitContext ec)
{
foreach (var s in statements)
s.Emit (ec);
}
protected override void CloneTo (CloneContext clonectx, Statement target)
{
StatementList t = (StatementList) target;
t.statements = new List (statements.Count);
foreach (Statement s in statements)
t.statements.Add (s.Clone (clonectx));
}
}
// A 'return' or a 'yield break'
public abstract class ExitStatement : Statement
{
protected bool unwind_protect;
protected abstract bool DoResolve (BlockContext ec);
public virtual void Error_FinallyClause (Report Report)
{
Report.Error (157, loc, "Control cannot leave the body of a finally clause");
}
public sealed override bool Resolve (BlockContext ec)
{
if (!DoResolve (ec))
return false;
unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
if (unwind_protect)
ec.NeedReturnLabel ();
ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
}
///
/// Implements the return statement
///
public class Return : ExitStatement
{
protected Expression Expr;
public Return (Expression expr, Location l)
{
Expr = expr;
loc = l;
}
#region Properties
public Expression Expression {
get {
return Expr;
}
}
#endregion
protected override bool DoResolve (BlockContext ec)
{
if (Expr == null) {
if (ec.ReturnType == TypeManager.void_type)
return true;
if (ec.CurrentIterator != null) {
Error_ReturnFromIterator (ec);
} else {
ec.Report.Error (126, loc,
"An object of a type convertible to `{0}' is required for the return statement",
ec.ReturnType.GetSignatureForError ());
}
return false;
}
Expr = Expr.Resolve (ec);
AnonymousExpression am = ec.CurrentAnonymousMethod;
if (am == null) {
if (ec.ReturnType == TypeManager.void_type) {
ec.Report.Error (127, loc,
"`{0}': A return keyword must not be followed by any expression when method returns void",
ec.GetSignatureForError ());
}
} else {
if (am.IsIterator) {
Error_ReturnFromIterator (ec);
return false;
}
var l = am as AnonymousMethodBody;
if (l != null && l.ReturnTypeInference != null && Expr != null) {
l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
return true;
}
}
if (Expr == null)
return false;
if (Expr.Type != ec.ReturnType) {
Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
if (Expr == null) {
if (am != null) {
ec.Report.Error (1662, loc,
"Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
am.ContainerType, am.GetSignatureForError ());
}
return false;
}
}
return true;
}
protected override void DoEmit (EmitContext ec)
{
if (Expr != null) {
Expr.Emit (ec);
if (unwind_protect)
ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
if (unwind_protect)
ec.Emit (OpCodes.Leave, ec.ReturnLabel);
else
ec.Emit (OpCodes.Ret);
}
void Error_ReturnFromIterator (ResolveContext rc)
{
rc.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");
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Return target = (Return) t;
// It's null for simple return;
if (Expr != null)
target.Expr = Expr.Clone (clonectx);
}
}
public class Goto : Statement {
string target;
LabeledStatement label;
bool unwind_protect;
public override bool Resolve (BlockContext ec)
{
unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
public Goto (string label, Location l)
{
loc = l;
target = label;
}
public string Target {
get { return target; }
}
public void SetResolvedTarget (LabeledStatement label)
{
this.label = label;
label.AddReference ();
}
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// Nothing to clone
}
protected override void DoEmit (EmitContext ec)
{
if (label == null)
throw new InternalErrorException ("goto emitted before target resolved");
Label l = label.LabelTarget (ec);
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
}
}
public class LabeledStatement : Statement {
string name;
bool defined;
bool referenced;
Label label;
FlowBranching.UsageVector vectors;
public LabeledStatement (string name, Location l)
{
this.name = name;
this.loc = l;
}
public Label LabelTarget (EmitContext ec)
{
if (defined)
return label;
label = ec.DefineLabel ();
defined = true;
return label;
}
public string Name {
get { return name; }
}
public bool IsDefined {
get { return defined; }
}
public bool HasBeenReferenced {
get { return referenced; }
}
public FlowBranching.UsageVector JumpOrigins {
get { return vectors; }
}
public void AddUsageVector (FlowBranching.UsageVector vector)
{
vector = vector.Clone ();
vector.Next = vectors;
vectors = vector;
}
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// nothing to clone
}
public override bool Resolve (BlockContext ec)
{
// this flow-branching will be terminated when the surrounding block ends
ec.StartFlowBranching (this);
return true;
}
protected override void DoEmit (EmitContext ec)
{
LabelTarget (ec);
ec.MarkLabel (label);
}
public void AddReference ()
{
referenced = true;
}
}
///
/// `goto default' statement
///
public class GotoDefault : Statement {
public GotoDefault (Location l)
{
loc = l;
}
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// nothing to clone
}
public override bool Resolve (BlockContext ec)
{
ec.CurrentBranching.CurrentUsageVector.Goto ();
if (ec.Switch == null) {
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
return false;
}
if (!ec.Switch.GotDefault) {
FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
return false;
}
return true;
}
protected override void DoEmit (EmitContext ec)
{
ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
}
}
///
/// `goto case' statement
///
public class GotoCase : Statement {
Expression expr;
SwitchLabel sl;
public GotoCase (Expression e, Location l)
{
expr = e;
loc = l;
}
public override bool Resolve (BlockContext ec)
{
if (ec.Switch == null){
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
return false;
}
ec.CurrentBranching.CurrentUsageVector.Goto ();
expr = expr.Resolve (ec);
if (expr == null)
return false;
Constant c = expr as Constant;
if (c == null) {
ec.Report.Error (150, expr.Location, "A constant value is expected");
return false;
}
TypeSpec type = ec.Switch.SwitchType;
Constant res = c.TryReduce (ec, type, c.Location);
if (res == null) {
c.Error_ValueCannotBeConverted (ec, loc, type, true);
return false;
}
if (!Convert.ImplicitStandardConversionExists (c, type))
ec.Report.Warning (469, 2, loc,
"The `goto case' value is not implicitly convertible to type `{0}'",
TypeManager.CSharpName (type));
object val = res.GetValue ();
if (val == null)
val = SwitchLabel.NullStringCase;
if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
(c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
return false;
}
return true;
}
protected override void DoEmit (EmitContext ec)
{
ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
GotoCase target = (GotoCase) t;
target.expr = expr.Clone (clonectx);
}
}
public class Throw : Statement {
Expression expr;
public Throw (Expression expr, Location l)
{
this.expr = expr;
loc = l;
}
public override bool Resolve (BlockContext ec)
{
if (expr == null) {
ec.CurrentBranching.CurrentUsageVector.Goto ();
return ec.CurrentBranching.CheckRethrow (loc);
}
expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
ec.CurrentBranching.CurrentUsageVector.Goto ();
if (expr == null)
return false;
if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
else
ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
return true;
}
protected override void DoEmit (EmitContext ec)
{
if (expr == null)
ec.Emit (OpCodes.Rethrow);
else {
expr.Emit (ec);
ec.Emit (OpCodes.Throw);
}
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Throw target = (Throw) t;
if (expr != null)
target.expr = expr.Clone (clonectx);
}
}
public class Break : Statement {
public Break (Location l)
{
loc = l;
}
bool unwind_protect;
public override bool Resolve (BlockContext ec)
{
unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
protected override void DoEmit (EmitContext ec)
{
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
// nothing needed
}
}
public class Continue : Statement {
public Continue (Location l)
{
loc = l;
}
bool unwind_protect;
public override bool Resolve (BlockContext ec)
{
unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
protected override void DoEmit (EmitContext ec)
{
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
// nothing needed.
}
}
public interface ILocalVariable
{
void Emit (EmitContext ec);
void EmitAssign (EmitContext ec);
void EmitAddressOf (EmitContext ec);
}
public interface INamedBlockVariable
{
Block Block { get; }
Expression CreateReferenceExpression (ResolveContext rc, Location loc);
bool IsDeclared { get; }
Location Location { get; }
}
public class BlockVariableDeclaration : Statement
{
public class Declarator
{
LocalVariable li;
Expression initializer;
public Declarator (LocalVariable li, Expression initializer)
{
if (li.Type != null)
throw new ArgumentException ("Expected null variable type");
this.li = li;
this.initializer = initializer;
}
#region Properties
public LocalVariable Variable {
get {
return li;
}
}
public Expression Initializer {
get {
return initializer;
}
set {
initializer = value;
}
}
#endregion
}
Expression initializer;
protected FullNamedExpression type_expr;
LocalVariable li;
protected List declarators;
public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
{
this.type_expr = type;
this.li = li;
this.loc = type_expr.Location;
}
protected BlockVariableDeclaration (LocalVariable li)
{
this.li = li;
}
#region Properties
public List Declarators {
get {
return declarators;
}
}
public Expression Initializer {
get {
return initializer;
}
set {
initializer = value;
}
}
public FullNamedExpression TypeExpression {
get {
return type_expr;
}
}
public LocalVariable Variable {
get {
return li;
}
}
#endregion
public void AddDeclarator (Declarator decl)
{
if (declarators == null)
declarators = new List ();
declarators.Add (decl);
}
void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
{
var container = bc.CurrentMemberDefinition.Parent;
Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
new MemberName (li.Name, li.Location), null);
container.AddField (f);
f.Define ();
Evaluator.QueueField (f);
li.HoistedVariant = new HoistedEvaluatorVariable (f);
li.SetIsUsed ();
}
public override bool Resolve (BlockContext bc)
{
TypeSpec type = null;
if (type_expr is VarExpr) {
//
// C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
// same name exists or as a keyword when no type was found
//
var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
if (texpr == null) {
if (RootContext.Version < LanguageVersion.V_3)
bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
if (li.IsFixed) {
bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
return false;
}
if (li.IsConstant) {
bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
return false;
}
if (Initializer == null) {
bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
return false;
}
if (declarators != null) {
bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
declarators = null;
}
Initializer = Initializer.Resolve (bc);
if (Initializer != null) {
((VarExpr) type_expr).InferType (bc, Initializer);
type = type_expr.Type;
}
}
}
if (type == null) {
var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
if (texpr == null)
return false;
type = texpr.Type;
if (li.IsConstant && !type.IsConstantCompatible) {
Const.Error_InvalidConstantType (type, loc, bc.Report);
}
}
if (type.IsStatic)
FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
if (type.IsPointer && !bc.IsUnsafe)
Expression.UnsafeError (bc, loc);
li.Type = type;
bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
if (eval_global) {
CreateEvaluatorVariable (bc, li);
} else {
li.PrepareForFlowAnalysis (bc);
}
if (initializer != null) {
initializer = ResolveInitializer (bc, li, initializer);
// li.Variable.DefinitelyAssigned
}
if (declarators != null) {
foreach (var d in declarators) {
d.Variable.Type = type;
if (eval_global) {
CreateEvaluatorVariable (bc, d.Variable);
} else {
d.Variable.PrepareForFlowAnalysis (bc);
}
if (d.Initializer != null) {
d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
// d.Variable.DefinitelyAssigned
}
}
}
return true;
}
protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
{
var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
return a.ResolveStatement (bc);
}
protected override void DoEmit (EmitContext ec)
{
if (li.IsConstant)
return;
li.CreateBuilder (ec);
if (Initializer != null)
((ExpressionStatement) Initializer).EmitStatement (ec);
if (declarators != null) {
foreach (var d in declarators) {
d.Variable.CreateBuilder (ec);
if (d.Initializer != null)
((ExpressionStatement) d.Initializer).EmitStatement (ec);
}
}
}
protected override void CloneTo (CloneContext clonectx, Statement target)
{
BlockVariableDeclaration t = (BlockVariableDeclaration) target;
t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
if (initializer != null)
t.initializer = initializer.Clone (clonectx);
if (declarators != null) {
t.declarators = null;
foreach (var d in declarators)
t.AddDeclarator (new Declarator (d.Variable, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
}
}
}
class BlockConstantDeclaration : BlockVariableDeclaration
{
public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
: base (type, li)
{
}
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
{
initializer = initializer.Resolve (bc);
if (initializer == null)
return null;
var c = initializer as Constant;
if (c == null) {
initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
return null;
}
c = c.ConvertImplicitly (bc, li.Type);
if (c == null) {
if (TypeManager.IsReferenceType (li.Type))
initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
else
initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
return null;
}
li.ConstantValue = c;
return initializer;
}
}
//
// The information about a user-perceived local variable
//
public class LocalVariable : INamedBlockVariable, ILocalVariable
{
[Flags]
public enum Flags
{
Used = 1,
IsThis = 1 << 1,
AddressTaken = 1 << 2,
CompilerGenerated = 1 << 3,
Constant = 1 << 4,
ForeachVariable = 1 << 5,
FixedVariable = 1 << 6,
UsingVariable = 1 << 7,
// DefinitelyAssigned = 1 << 8,
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
}
TypeSpec type;
readonly string name;
readonly Location loc;
readonly Block block;
Flags flags;
Constant const_value;
public VariableInfo VariableInfo;
HoistedVariable hoisted_variant;
LocalBuilder builder;
public LocalVariable (Block block, string name, Location loc)
{
this.block = block;
this.name = name;
this.loc = loc;
}
public LocalVariable (Block block, string name, Flags flags, Location loc)
: this (block, name, loc)
{
this.flags = flags;
}
//
// Used by variable declarators
//
public LocalVariable (LocalVariable li, string name, Location loc)
: this (li.block, name, li.flags, loc)
{
}
#region Properties
public Block Block {
get {
return block;
}
}
public Constant ConstantValue {
get {
return const_value;
}
set {
const_value = value;
}
}
//
// Hoisted local variable variant
//
public HoistedVariable HoistedVariant {
get {
return hoisted_variant;
}
set {
hoisted_variant = value;
}
}
public bool IsDeclared {
get {
return type != null;
}
}
public bool IsConstant {
get {
return (flags & Flags.Constant) != 0;
}
}
public bool IsThis {
get {
return (flags & Flags.IsThis) != 0;
}
}
public bool IsFixed {
get {
return (flags & Flags.FixedVariable) != 0;
}
}
public bool IsReadonly {
get {
return (flags & Flags.ReadonlyMask) != 0;
}
}
public Location Location {
get {
return loc;
}
}
public string Name {
get {
return name;
}
}
public TypeSpec Type {
get {
return type;
}
set {
type = value;
}
}
#endregion
public void CreateBuilder (EmitContext ec)
{
if ((flags & Flags.Used) == 0) {
if (VariableInfo == null) {
// Missing flow analysis or wrong variable flags
throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
}
if (VariableInfo.IsEverAssigned)
ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
else
ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
}
if (HoistedVariant != null)
return;
if (builder != null) {
if ((flags & Flags.CompilerGenerated) != 0)
return;
// To avoid Used warning duplicates
throw new InternalErrorException ("Already created variable `{0}'", name);
}
//
// All fixed variabled are pinned, a slot has to be alocated
//
builder = ec.DeclareLocal (Type, IsFixed);
if (SymbolWriter.HasSymbolWriter)
ec.DefineLocalVariable (name, builder);
}
public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
{
LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
li.Type = type;
return li;
}
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
{
if (IsConstant && const_value != null)
return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
return new LocalVariableReference (this, loc);
}
public void Emit (EmitContext ec)
{
ec.Emit (OpCodes.Ldloc, builder);
}
public void EmitAssign (EmitContext ec)
{
// TODO: Need something better for temporary variables
if ((flags & Flags.CompilerGenerated) != 0)
CreateBuilder (ec);
ec.Emit (OpCodes.Stloc, builder);
}
public void EmitAddressOf (EmitContext ec)
{
ec.Emit (OpCodes.Ldloca, builder);
}
public bool IsThisAssigned (BlockContext ec, Block block)
{
if (VariableInfo == null)
throw new Exception ();
if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
return true;
return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
}
public bool IsAssigned (BlockContext ec)
{
if (VariableInfo == null)
throw new Exception ();
return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
}
public void PrepareForFlowAnalysis (BlockContext bc)
{
//
// No need for definitely assigned check for these guys
//
if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
return;
VariableInfo = new VariableInfo (this, bc.FlowOffset);
bc.FlowOffset += VariableInfo.Length;
}
//
// Mark the variables as referenced in the user code
//
public void SetIsUsed ()
{
flags |= Flags.Used;
}
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})", name, type, VariableInfo, Location);
}
public string GetReadOnlyContext ()
{
switch (flags & Flags.ReadonlyMask) {
case Flags.FixedVariable:
return "fixed variable";
case Flags.ForeachVariable:
return "foreach iteration variable";
case Flags.UsingVariable:
return "using variable";
}
throw new InternalErrorException ("Variable is not readonly");
}
}
///
/// Block represents a C# block.
///
///
///
/// This class is used in a number of places: either to represent
/// explicit blocks that the programmer places or implicit blocks.
///
/// Implicit blocks are used as labels or to introduce variable
/// declarations.
///
/// Top-level blocks derive from Block, and they are called ToplevelBlock
/// they contain extra information that is not necessary on normal blocks.
///
public class Block : Statement {
[Flags]
public enum Flags
{
Unchecked = 1,
HasRet = 8,
Unsafe = 16,
IsIterator = 32,
HasCapturedVariable = 64,
HasCapturedThis = 1 << 7,
IsExpressionTree = 1 << 8,
CompilerGenerated = 1 << 9
}
public Block Parent;
public Location StartLocation;
public Location EndLocation;
public ExplicitBlock Explicit;
public ParametersBlock ParametersBlock;
protected Flags flags;
//
// The statements in this block
//
protected List statements;
//
// Labels. (label, block) pairs.
//
protected Dictionary labels;
//
// If this is a switch section, the enclosing switch block.
//
protected ExplicitBlock switch_block;
protected List scope_initializers;
int? resolving_init_idx;
Block original;
#if DEBUG
static int id;
public int ID = id++;
static int clone_id_counter;
int clone_id;
#endif
// int assignable_slots;
bool unreachable_shown;
bool unreachable;
public Block (Block parent, Location start, Location end)
: this (parent, 0, start, end)
{
}
public Block (Block parent, Flags flags, Location start, Location end)
{
if (parent != null) {
// the appropriate constructors will fixup these fields
ParametersBlock = parent.ParametersBlock;
Explicit = parent.Explicit;
}
this.Parent = parent;
this.flags = flags;
this.StartLocation = start;
this.EndLocation = end;
this.loc = start;
statements = new List (4);
this.original = this;
}
#region Properties
public bool HasRet {
get { return (flags & Flags.HasRet) != 0; }
}
public Block Original {
get {
return original;
}
}
public bool IsCompilerGenerated {
get { return (flags & Flags.CompilerGenerated) != 0; }
set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
}
public bool Unchecked {
get { return (flags & Flags.Unchecked) != 0; }
set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
}
public bool Unsafe {
get { return (flags & Flags.Unsafe) != 0; }
set { flags |= Flags.Unsafe; }
}
#endregion
public ExplicitBlock CreateSwitchBlock (Location start)
{
// FIXME: Only explicit block should be created
var new_block = new ExplicitBlock (this, start, start);
new_block.switch_block = Explicit;
return new_block;
}
public void SetEndLocation (Location loc)
{
EndLocation = loc;
}
protected void Error_158 (string name, Location loc)
{
ParametersBlock.TopBlock.Report.Error (158, loc, "The label `{0}' shadows another label " +
"by the same name in a contained scope", name);
}
///
/// Adds a label to the current block.
///
///
///
/// false if the name already exists in this block. true
/// otherwise.
///
///
public bool AddLabel (LabeledStatement target)
{
if (switch_block != null)
return switch_block.AddLabel (target);
string name = target.Name;
Block cur = this;
while (cur != null) {
LabeledStatement s = cur.DoLookupLabel (name);
if (s != null) {
ParametersBlock.TopBlock.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
ParametersBlock.TopBlock.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
return false;
}
if (this == Explicit)
break;
cur = cur.Parent;
}
while (cur != null) {
if (cur.DoLookupLabel (name) != null) {
Error_158 (name, target.loc);
return false;
}
/*
if (children != null) {
foreach (Block b in children) {
LabeledStatement s = b.DoLookupLabel (name);
if (s == null)
continue;
Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
Error_158 (name, target.loc);
return false;
}
}
*/
cur = cur.Parent;
}
ParametersBlock.TopBlock.CheckError158 (name, target.loc);
if (labels == null)
labels = new Dictionary ();
labels.Add (name, target);
return true;
}
public LabeledStatement LookupLabel (string name)
{
LabeledStatement s = DoLookupLabel (name);
if (s != null)
return s;
/*
if (children == null)
return null;
foreach (Block child in children) {
if (Explicit != child.Explicit)
continue;
s = child.LookupLabel (name);
if (s != null)
return s;
}
*/
return null;
}
LabeledStatement DoLookupLabel (string name)
{
if (switch_block != null)
return switch_block.LookupLabel (name);
if (labels != null)
if (labels.ContainsKey (name))
return labels [name];
return null;
}
public void AddLocalName (LocalVariable li)
{
AddLocalName (li.Name, li);
}
public virtual void AddLocalName (string name, INamedBlockVariable li)
{
ParametersBlock.TopBlock.AddLocalName (name, li);
}
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
{
if (reason == null) {
Error_AlreadyDeclared (name, variable);
return;
}
ParametersBlock.TopBlock.Report.Error (136, variable.Location,
"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 to denote something else",
name, reason);
}
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
{
var pi = variable as ParametersBlock.ParameterInfo;
if (pi != null) {
var p = pi.Parameter;
if (p is AnonymousTypeClass.GeneratedParameter) {
ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
p.Name);
} else {
ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
}
return;
}
ParametersBlock.TopBlock.Report.Error (128, variable.Location,
"A local variable named `{0}' is already defined in this scope", name);
}
public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
{
ParametersBlock.TopBlock.Report.Error (412, loc,
"The type parameter name `{0}' is the same as local variable or parameter name",
name);
}
//
// It should be used by expressions which require to
// register a statement during resolve process.
//
public void AddScopeStatement (Statement s)
{
if (scope_initializers == null)
scope_initializers = new List ();
//
// Simple recursive helper, when resolve scope initializer another
// new scope initializer can be added, this ensures it's initialized
// before existing one. For now this can happen with expression trees
// in base ctor initializer only
//
if (resolving_init_idx.HasValue) {
scope_initializers.Insert (resolving_init_idx.Value, s);
++resolving_init_idx;
} else {
scope_initializers.Add (s);
}
}
public void AddStatement (Statement s)
{
statements.Add (s);
}
public int AssignableSlots {
get {
// FIXME: HACK, we don't know the block available variables count now, so set this high enough
return 4096;
// return assignable_slots;
}
}
public override bool Resolve (BlockContext ec)
{
Block prev_block = ec.CurrentBlock;
bool ok = true;
ec.CurrentBlock = this;
ec.StartFlowBranching (this);
Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
//
// Compiler generated scope statements
//
if (scope_initializers != null) {
for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
scope_initializers[resolving_init_idx.Value].Resolve (ec);
}
resolving_init_idx = null;
}
//
// 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.
//
int statement_count = statements.Count;
for (int ix = 0; ix < statement_count; ix++){
Statement s = statements [ix];
//
// Warn if we detect unreachable code.
//
if (unreachable) {
if (s is EmptyStatement)
continue;
if (!unreachable_shown && !(s is LabeledStatement)) {
ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
unreachable_shown = true;
}
Block c_block = s as Block;
if (c_block != null)
c_block.unreachable = c_block.unreachable_shown = true;
}
//
// 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;
if (ec.IsInProbingMode)
break;
statements [ix] = new EmptyStatement (s.loc);
continue;
}
if (unreachable && !(s is LabeledStatement) && !(s is Block))
statements [ix] = new EmptyStatement (s.loc);
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);
while (ec.CurrentBranching is FlowBranchingLabeled)
ec.EndFlowBranching ();
bool flow_unreachable = ec.EndFlowBranching ();
ec.CurrentBlock = prev_block;
if (flow_unreachable)
flags |= Flags.HasRet;
// 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 == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
ok = false;
if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
foreach (LabeledStatement label in labels.Values)
if (!label.HasBeenReferenced)
ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
}
return ok;
}
public override bool ResolveUnreachable (BlockContext ec, bool warn)
{
unreachable_shown = true;
unreachable = true;
if (warn)
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
ec.KillFlowBranching ();
return ok;
}
protected override void DoEmit (EmitContext ec)
{
for (int ix = 0; ix < statements.Count; ix++){
statements [ix].Emit (ec);
}
}
public override void Emit (EmitContext ec)
{
if (scope_initializers != null)
EmitScopeInitializers (ec);
ec.Mark (StartLocation);
DoEmit (ec);
if (SymbolWriter.HasSymbolWriter)
EmitSymbolInfo (ec);
}
protected void EmitScopeInitializers (EmitContext ec)
{
SymbolWriter.OpenCompilerGeneratedBlock (ec);
using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
foreach (Statement s in scope_initializers)
s.Emit (ec);
}
SymbolWriter.CloseCompilerGeneratedBlock (ec);
}
protected virtual void EmitSymbolInfo (EmitContext ec)
{
}
#if DEBUG
public override string ToString ()
{
return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
}
#endif
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Block target = (Block) t;
#if DEBUG
target.clone_id = clone_id_counter++;
#endif
clonectx.AddBlockMap (this, target);
target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
if (Parent != null)
target.Parent = clonectx.RemapBlockCopy (Parent);
target.statements = new List (statements.Count);
foreach (Statement s in statements)
target.statements.Add (s.Clone (clonectx));
}
}
public class ExplicitBlock : Block
{
protected AnonymousMethodStorey am_storey;
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;
}
#region Properties
public AnonymousMethodStorey AnonymousMethodStorey {
get {
return am_storey;
}
}
public bool HasCapturedThis {
set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
get {
return (flags & Flags.HasCapturedThis) != 0;
}
}
public bool HasCapturedVariable {
set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
get {
return (flags & Flags.HasCapturedVariable) != 0;
}
}
#endregion
//
// Creates anonymous method storey in current block
//
public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
{
//
// An iterator has only 1 storey block
//
if (ec.CurrentIterator != null)
return ec.CurrentIterator.Storey;
//
// When referencing a variable in iterator storey from children anonymous method
//
if (ParametersBlock.am_storey is IteratorStorey) {
return ParametersBlock.am_storey;
}
//
// Switch block does not follow sequential flow and we cannot emit
// storey initialization inside the block because it can be jumped over
// for all non-first cases. Instead we push it up to the parent block to be
// always initialized
//
if (switch_block != null)
return switch_block.CreateAnonymousMethodStorey (ec);
if (am_storey == null) {
MemberBase mc = ec.MemberContext as MemberBase;
//
// Creates anonymous method storey for this block
//
am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
}
return am_storey;
}
public override void Emit (EmitContext ec)
{
if (am_storey != null) {
DefineAnonymousStorey (ec);
am_storey.EmitStoreyInstantiation (ec);
}
bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
if (emit_debug_info)
ec.BeginScope ();
base.Emit (ec);
if (emit_debug_info)
ec.EndScope ();
}
void DefineAnonymousStorey (EmitContext ec)
{
//
// Creates anonymous method storey
//
if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
//
// Creates parent storey reference when hoisted this is accessible
//
if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
//
// Hoisted this exists in top-level parent storey only
//
while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
parent = parent.Parent.Explicit;
am_storey.AddParentStoreyReference (ec, parent.am_storey);
}
am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
// TODO MemberCache: Review
am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
}
am_storey.CreateType ();
if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
am_storey.DefineType ();
am_storey.ResolveTypeParameters ();
var ref_blocks = am_storey.ReferencesFromChildrenBlock;
if (ref_blocks != null) {
foreach (ExplicitBlock ref_block in ref_blocks) {
for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
if (b.am_storey != null) {
b.am_storey.AddParentStoreyReference (ec, am_storey);
// Stop propagation inside same top block
if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
break;
b = b.ParametersBlock;
}
b.HasCapturedVariable = true;
}
}
}
am_storey.Define ();
am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
}
public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
{
tryBlock.statements = statements;
statements = new List (1);
statements.Add (tf);
}
}
//
// ParametersBlock was introduced to support anonymous methods
// and lambda expressions
//
public class ParametersBlock : ExplicitBlock
{
public class ParameterInfo : INamedBlockVariable
{
readonly ParametersBlock block;
readonly int index;
public VariableInfo VariableInfo;
public ParameterInfo (ParametersBlock block, int index)
{
this.block = block;
this.index = index;
}
#region Properties
public Block Block {
get {
return block;
}
}
public bool IsDeclared {
get {
return true;
}
}
public Location Location {
get {
return Parameter.Location;
}
}
public Parameter Parameter {
get {
return block.Parameters [index];
}
}
public TypeSpec ParameterType {
get {
return Parameter.Type;
}
}
#endregion
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
{
return new ParameterReference (this, loc);
}
}
//
// Block is converted to an expression
//
sealed class BlockScopeExpression : Expression
{
Expression child;
readonly ParametersBlock block;
public BlockScopeExpression (Expression child, ParametersBlock block)
{
this.child = child;
this.block = block;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
throw new NotSupportedException ();
}
protected override Expression DoResolve (ResolveContext ec)
{
if (child == null)
return null;
child = child.Resolve (ec);
if (child == null)
return null;
eclass = child.eclass;
type = child.Type;
return this;
}
public override void Emit (EmitContext ec)
{
block.EmitScopeInitializers (ec);
child.Emit (ec);
}
}
protected ParametersCompiled parameters;
protected ParameterInfo[] parameter_info;
bool resolved;
protected bool unreachable;
protected ToplevelBlock top_block;
public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
: base (parent, 0, start, start)
{
if (parameters == null)
throw new ArgumentNullException ("parameters");
this.parameters = parameters;
ParametersBlock = this;
this.top_block = parent.ParametersBlock.top_block;
ProcessParameters ();
}
protected ParametersBlock (ParametersCompiled parameters, Location start)
: base (null, 0, start, start)
{
if (parameters == null)
throw new ArgumentNullException ("parameters");
this.parameters = parameters;
ParametersBlock = this;
}
protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
: base (null, 0, source.StartLocation, source.EndLocation)
{
this.parameters = parameters;
this.statements = source.statements;
this.scope_initializers = source.scope_initializers;
this.switch_block = source.switch_block;
this.resolved = true;
this.unreachable = source.unreachable;
this.am_storey = source.am_storey;
ParametersBlock = this;
}
#region Properties
//
// Block has been converted to expression tree
//
public bool IsExpressionTree {
get {
return (flags & Flags.IsExpressionTree) != 0;
}
}
//
// The parameters for the block.
//
public ParametersCompiled Parameters {
get {
return parameters;
}
}
public ToplevelBlock TopBlock {
get {
return top_block;
}
}
public bool Resolved {
get {
return resolved;
}
}
#endregion
//
// Check whether all `out' parameters have been assigned.
//
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;
TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
var.Name);
}
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
if (statements.Count == 1) {
Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
if (scope_initializers != null)
expr = new BlockScopeExpression (expr, this);
return expr;
}
return base.CreateExpressionTree (ec);
}
public ParameterInfo GetParameterInfo (Parameter p)
{
for (int i = 0; i < parameters.Count; ++i) {
if (parameters[i] == p)
return parameter_info[i];
}
throw new ArgumentException ("Invalid parameter");
}
public Expression GetParameterReference (int index, Location loc)
{
return new ParameterReference (parameter_info[index], loc);
}
protected void ProcessParameters ()
{
if (parameters.Count == 0)
return;
parameter_info = new ParameterInfo[parameters.Count];
for (int i = 0; i < parameter_info.Length; ++i) {
var p = parameters.FixedParameters[i];
if (p == null)
continue;
// TODO: Should use Parameter only and more block there
parameter_info[i] = new ParameterInfo (this, i);
AddLocalName (p.Name, parameter_info[i]);
}
}
public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
{
if (resolved)
return true;
resolved = true;
if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
flags |= Flags.IsExpressionTree;
try {
ResolveMeta (rc);
using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
if (!Resolve (rc))
return false;
unreachable = top_level.End ();
}
} catch (Exception e) {
if (e is CompletionResult || rc.Report.IsDisabled)
throw;
if (rc.CurrentBlock != null) {
rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
} else {
rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
}
if (Report.DebugFlags > 0)
throw;
}
if (rc.ReturnType != TypeManager.void_type && !unreachable) {
if (rc.CurrentAnonymousMethod == null) {
// FIXME: Missing FlowAnalysis for generated iterator MoveNext method
if (md is IteratorMethod) {
unreachable = true;
} else {
rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
return false;
}
} else {
rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
rc.CurrentAnonymousMethod.GetSignatureForError ());
return false;
}
}
return true;
}
void ResolveMeta (BlockContext ec)
{
int orig_count = parameters.Count;
for (int i = 0; i < orig_count; ++i) {
Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
continue;
VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
parameter_info[i].VariableInfo = vi;
ec.FlowOffset += vi.Length;
}
}
public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
{
ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
pb.EndLocation = EndLocation;
pb.statements = statements;
var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
am_storey = new IteratorStorey (iterator);
statements = new List (1);
AddStatement (new Return (iterator, iterator.Location));
}
}
//
//
//
public class ToplevelBlock : ParametersBlock
{
LocalVariable this_variable;
CompilerContext compiler;
Dictionary names;
public HoistedVariable HoistedThisVariable;
public Report Report {
get { return compiler.Report; }
}
public ToplevelBlock (CompilerContext ctx, Location loc)
: this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
{
}
public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
: base (parameters, start)
{
this.compiler = ctx;
top_block = this;
ProcessParameters ();
}
//
// Recreates a top level block from parameters block. Used for
// compiler generated methods where the original block comes from
// explicit child block. This works for already resolved blocks
// only to ensure we resolve them in the correct flow order
//
public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
: base (source, parameters)
{
this.compiler = source.TopBlock.compiler;
top_block = this;
}
public override void AddLocalName (string name, INamedBlockVariable li)
{
if (names == null)
names = new Dictionary ();
object value;
if (!names.TryGetValue (name, out value)) {
names.Add (name, li);
return;
}
INamedBlockVariable existing = value as INamedBlockVariable;
List existing_list;
if (existing != null) {
existing_list = new List ();
existing_list.Add (existing);
names[name] = existing_list;
} else {
existing_list = (List) value;
}
//
// A collision checking between local names
//
for (int i = 0; i < existing_list.Count; ++i) {
existing = existing_list[i];
Block b = existing.Block;
// Collision at same level
if (li.Block == b) {
li.Block.Error_AlreadyDeclared (name, li);
break;
}
// Collision with parent
b = li.Block;
while ((b = b.Parent) != null) {
if (existing.Block == b) {
li.Block.Error_AlreadyDeclared (name, li, "parent or current");
i = existing_list.Count;
break;
}
}
// Collision with with children
b = existing.Block;
while ((b = b.Parent) != null) {
if (li.Block == b) {
li.Block.Error_AlreadyDeclared (name, li, "child");
i = existing_list.Count;
break;
}
}
}
existing_list.Add (li);
}
public bool CheckError158 (string name, Location loc)
{
/*
if (AnonymousChildren != null) {
foreach (ToplevelBlock child in AnonymousChildren) {
if (!child.CheckError158 (name, loc))
return false;
}
}
for (ToplevelBlock c = Container; c != null; c = c.Container) {
if (!c.DoCheckError158 (name, loc))
return false;
}
*/
return true;
}
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;
}
//
// Creates an arguments set from all parameters, useful for method proxy calls
//
public Arguments GetAllParametersArguments ()
{
int count = parameters.Count;
Arguments args = new Arguments (count);
for (int i = 0; i < count; ++i) {
var arg_expr = GetParameterReference (i, parameter_info[i].Location);
args.Add (new Argument (arg_expr));
}
return args;
}
//
// Lookup inside a block, the returned value can represent 3 states
//
// true+variable: A local name was found and it's valid
// false+variable: A local name was found in a child block only
// false+null: No local name was found
//
public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
{
if (names == null)
return false;
object value;
if (!names.TryGetValue (name, out value))
return false;
variable = value as INamedBlockVariable;
Block b = block;
if (variable != null) {
do {
if (variable.Block == b)
return true;
b = b.Parent;
} while (b != null);
b = variable.Block;
do {
if (block == b)
return false;
b = b.Parent;
} while (b != null);
} else {
List list = (List) value;
for (int i = 0; i < list.Count; ++i) {
variable = list[i];
do {
if (variable.Block == b)
return true;
b = b.Parent;
} while (b != null);
b = variable.Block;
do {
if (block == b)
return false;
b = b.Parent;
} while (b != null);
b = block;
}
}
variable = null;
return false;
}
//
// Returns the "this" instance variable of this block.
// See AddThisVariable() for more information.
//
public LocalVariable 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 LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
{
if (this_variable == null) {
this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
this_variable.Type = ds.CurrentType;
this_variable.PrepareForFlowAnalysis (bc);
}
return this_variable;
}
public bool IsIterator {
get { return (flags & Flags.IsIterator) != 0; }
set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
}
public bool IsThisAssigned (BlockContext ec)
{
return this_variable == null || this_variable.IsThisAssigned (ec, this);
}
public override void Emit (EmitContext ec)
{
if (Report.Errors > 0)
return;
#if PRODUCTION
try {
#endif
if (ec.HasReturnLabel)
ec.ReturnLabel = ec.DefineLabel ();
base.Emit (ec);
ec.Mark (EndLocation);
if (ec.HasReturnLabel)
ec.MarkLabel (ec.ReturnLabel);
if (ec.return_value != null) {
ec.Emit (OpCodes.Ldloc, ec.return_value);
ec.Emit (OpCodes.Ret);
} else {
//
// If `HasReturnLabel' is set, then we already emitted a
// jump to the end of the method, so we must emit a `ret'
// there.
//
// Unfortunately, System.Reflection.Emit automatically emits
// a leave to the end of a finally block. This is a problem
// if no code is following the try/finally block since we may
// jump to a point after the end of the method.
// As a workaround, we're always creating a return label in
// this case.
//
if (ec.HasReturnLabel || !unreachable) {
if (ec.ReturnType != TypeManager.void_type)
ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
ec.Emit (OpCodes.Ret);
}
}
#if PRODUCTION
} catch (Exception e){
Console.WriteLine ("Exception caught by the compiler while emitting:");
Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
throw;
}
#endif
}
protected override void EmitSymbolInfo (EmitContext ec)
{
AnonymousExpression ae = ec.CurrentAnonymousMethod;
if ((ae != null) && (ae.Storey != null))
SymbolWriter.DefineScopeVariable (ae.Storey.ID);
base.EmitSymbolInfo (ec);
}
}
public class SwitchLabel {
Expression label;
object converted;
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.
//
public SwitchLabel (Expression expr, Location l)
{
label = expr;
loc = l;
}
public Expression Label {
get {
return label;
}
}
public Location Location {
get { return loc; }
}
public object Converted {
get {
return converted;
}
}
public Label GetILLabel (EmitContext ec)
{
if (!il_label_set){
il_label = ec.DefineLabel ();
il_label_set = true;
}
return il_label;
}
public Label GetILLabelCode (EmitContext ec)
{
if (!il_label_code_set){
il_label_code = ec.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 (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
{
Expression e = label.Resolve (ec);
if (e == null)
return false;
Constant c = e as Constant;
if (c == null){
ec.Report.Error (150, loc, "A constant value is expected");
return false;
}
if (required_type == TypeManager.string_type && c.GetValue () == null) {
converted = NullStringCase;
return true;
}
if (allow_nullable && c.GetValue () == null) {
converted = NullStringCase;
return true;
}
c = c.ImplicitConversionRequired (ec, required_type, loc);
if (c == null)
return false;
converted = c.GetValue ();
return true;
}
public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
{
string label;
if (converted == null)
label = "default";
else if (converted == NullStringCase)
label = "null";
else
label = converted.ToString ();
ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
ec.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 {
// An array of SwitchLabels.
public readonly List Labels;
public readonly Block Block;
public SwitchSection (List labels, Block block)
{
Labels = labels;
Block = block;
}
public SwitchSection Clone (CloneContext clonectx)
{
var cloned_labels = new List ();
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 List Sections;
public Expression Expr;
///
/// Maps constants whose type type SwitchType to their SwitchLabels.
///
public IDictionary