/mcs/mcs/statement.cs
C# | 6461 lines | 4695 code | 1203 blank | 563 comment | 1052 complexity | d1889fc01f02f011c13a627ec1b68019 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
- //
- // statement.cs: Statement representation for the IL tree.
- //
- // Authors:
- // Miguel de Icaza (miguel@ximian.com)
- // Martin Baulig (martin@ximian.com)
- // Marek Safar (marek.safar@gmail.com)
- //
- // Copyright 2001, 2002, 2003 Ximian, Inc.
- // Copyright 2003, 2004 Novell, Inc.
- // Copyright 2011 Xamarin Inc.
- //
- using System;
- using System.Collections.Generic;
- #if STATIC
- using IKVM.Reflection.Emit;
- #else
- using System.Reflection.Emit;
- #endif
- namespace Mono.CSharp {
-
- public abstract class Statement {
- public Location loc;
-
- /// <summary>
- /// Resolves the statement, true means that all sub-statements
- /// did resolve ok.
- // </summary>
- public virtual bool Resolve (BlockContext bc)
- {
- return true;
- }
- /// <summary>
- /// We already know that the statement is unreachable, but we still
- /// need to resolve it to catch errors.
- /// </summary>
- 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.
- //
- bool unreachable = false;
- if (warn && !ec.UnreachableReported) {
- ec.UnreachableReported = true;
- unreachable = true;
- ec.Report.Warning (162, 2, loc, "Unreachable code detected");
- }
- ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
- bool ok = Resolve (ec);
- ec.KillFlowBranching ();
- if (unreachable) {
- ec.UnreachableReported = false;
- }
- return ok;
- }
-
- /// <summary>
- /// Return value indicates whether all code paths emitted return.
- /// </summary>
- protected abstract void DoEmit (EmitContext ec);
- public virtual void Emit (EmitContext ec)
- {
- ec.Mark (loc);
- DoEmit (ec);
- if (ec.StatementEpilogue != null) {
- ec.EmitEpilogue ();
- }
- }
- //
- // 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 virtual object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
-
- 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 Expression Expr {
- get {
- return this.expr;
- }
- }
-
- public override bool Resolve (BlockContext ec)
- {
- bool ok = true;
- 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 ();
- 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class Do : Statement {
- public Expression expr;
- public Statement EmbeddedStatement;
- public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
- {
- expr = bool_expr;
- EmbeddedStatement = statement;
- loc = doLocation;
- WhileLocation = whileLocation;
- }
- public Location WhileLocation {
- get; private set;
- }
- 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);
- // Mark start of while condition
- ec.Mark (WhileLocation);
- //
- // 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- 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);
- if (ec.EmitAccurateDebugInfo)
- ec.Emit (OpCodes.Nop);
- 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;
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- While target = (While) t;
- target.expr = expr.Clone (clonectx);
- target.Statement = Statement.Clone (clonectx);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class For : Statement
- {
- bool infinite, empty;
-
- public For (Location l)
- {
- loc = l;
- }
- public Statement Initializer {
- get; set;
- }
- public Expression Condition {
- get; set;
- }
- public Statement Iterator {
- get; set;
- }
- public Statement Statement {
- get; set;
- }
- public override bool Resolve (BlockContext ec)
- {
- bool ok = true;
- if (Initializer != null) {
- if (!Initializer.Resolve (ec))
- ok = false;
- }
- if (Condition != null) {
- Condition = Condition.Resolve (ec);
- if (Condition == null)
- ok = false;
- else if (Condition is Constant) {
- bool value = !((Constant) Condition).IsDefaultValue;
- if (value == false){
- if (!Statement.ResolveUnreachable (ec, true))
- return false;
- if ((Iterator != null) &&
- !Iterator.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 (Iterator != null){
- if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
- if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
- ok = false;
- } else {
- if (!Iterator.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 (Initializer != null)
- Initializer.Emit (ec);
- if (empty) {
- Condition.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);
- Iterator.Emit (ec);
- ec.MarkLabel (test);
- //
- // If test is null, there is no test, and we are just
- // an infinite loop
- //
- if (Condition != null) {
- ec.Mark (Condition.Location);
- //
- // The Resolve code already catches the case for
- // Test == Constant (false) so we know that
- // this is true
- //
- if (Condition is Constant) {
- Condition.EmitSideEffect (ec);
- ec.Emit (OpCodes.Br, loop);
- } else {
- Condition.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 (Initializer != null)
- target.Initializer = Initializer.Clone (clonectx);
- if (Condition != null)
- target.Condition = Condition.Clone (clonectx);
- if (Iterator != null)
- target.Iterator = Iterator.Clone (clonectx);
- target.Statement = Statement.Clone (clonectx);
- }
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
-
- public class StatementExpression : Statement
- {
- ExpressionStatement expr;
-
- public StatementExpression (ExpressionStatement expr)
- {
- this.expr = expr;
- loc = expr.Location;
- }
- public StatementExpression (ExpressionStatement expr, Location loc)
- {
- this.expr = expr;
- this.loc = loc;
- }
- public ExpressionStatement Expr {
- get {
- return this.expr;
- }
- }
-
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- StatementExpression target = (StatementExpression) t;
- target.expr = (ExpressionStatement) expr.Clone (clonectx);
- }
-
- protected override void DoEmit (EmitContext ec)
- {
- expr.EmitStatement (ec);
- }
- public override bool Resolve (BlockContext ec)
- {
- expr = expr.ResolveStatement (ec);
- return expr != null;
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class StatementErrorExpression : Statement
- {
- readonly Expression expr;
- public StatementErrorExpression (Expression expr)
- {
- this.expr = expr;
- }
- public Expression Expr {
- get {
- return expr;
- }
- }
- protected override void DoEmit (EmitContext ec)
- {
- throw new NotSupportedException ();
- }
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- throw new NotImplementedException ();
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- //
- // Simple version of statement list not requiring a block
- //
- public class StatementList : Statement
- {
- List<Statement> statements;
- public StatementList (Statement first, Statement second)
- {
- statements = new List<Statement> () { first, second };
- }
- #region Properties
- public IList<Statement> 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<Statement> (statements.Count);
- foreach (Statement s in statements)
- t.statements.Add (s.Clone (clonectx));
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- // 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)
- {
- var res = DoResolve (ec);
- unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- return res;
- }
- }
- /// <summary>
- /// Implements the return statement
- /// </summary>
- public class Return : ExitStatement
- {
- Expression expr;
- public Return (Expression expr, Location l)
- {
- this.expr = expr;
- loc = l;
- }
- #region Properties
- public Expression Expr {
- get {
- return expr;
- }
- protected set {
- expr = value;
- }
- }
- #endregion
- protected override bool DoResolve (BlockContext ec)
- {
- if (expr == null) {
- if (ec.ReturnType.Kind == MemberKind.Void)
- return true;
- //
- // Return must not be followed by an expression when
- // the method return type is Task
- //
- if (ec.CurrentAnonymousMethod is AsyncInitializer) {
- var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
- if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
- //
- // Extra trick not to emit ret/leave inside awaiter body
- //
- expr = EmptyExpression.Null;
- return true;
- }
- }
- if (ec.CurrentIterator != null) {
- Error_ReturnFromIterator (ec);
- } else if (ec.ReturnType != InternalType.ErrorType) {
- 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);
- TypeSpec block_return_type = ec.ReturnType;
- AnonymousExpression am = ec.CurrentAnonymousMethod;
- if (am == null) {
- if (block_return_type.Kind == MemberKind.Void) {
- ec.Report.Error (127, loc,
- "`{0}': A return keyword must not be followed by any expression when method returns void",
- ec.GetSignatureForError ());
- return false;
- }
- } else {
- if (am.IsIterator) {
- Error_ReturnFromIterator (ec);
- return false;
- }
- var async_block = am as AsyncInitializer;
- if (async_block != null) {
- if (expr != null) {
- var storey = (AsyncTaskStorey) am.Storey;
- var async_type = storey.ReturnType;
- if (async_type == null && async_block.ReturnTypeInference != null) {
- async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
- return true;
- }
- if (async_type.Kind == MemberKind.Void) {
- ec.Report.Error (127, loc,
- "`{0}': A return keyword must not be followed by any expression when method returns void",
- ec.GetSignatureForError ());
- return false;
- }
- if (!async_type.IsGenericTask) {
- if (this is ContextualReturn)
- return true;
- ec.Report.Error (1997, loc,
- "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
- ec.GetSignatureForError ());
- return false;
- }
- //
- // The return type is actually Task<T> type argument
- //
- if (expr.Type == async_type) {
- ec.Report.Error (4016, loc,
- "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
- ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
- } else {
- block_return_type = async_type.TypeArguments[0];
- }
- }
- } else {
- // Same error code as .NET but better error message
- if (block_return_type.Kind == MemberKind.Void) {
- ec.Report.Error (127, loc,
- "`{0}': A return keyword must not be followed by any expression when delegate returns void",
- am.GetSignatureForError ());
- 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 != block_return_type && expr.Type != InternalType.ErrorType) {
- expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
- if (expr == null) {
- if (am != null && block_return_type == ec.ReturnType) {
- 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);
- var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
- if (async_body != null) {
- var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
- // It's null for await without async
- if (async_return != null) {
- async_return.EmitAssign (ec);
- ec.EmitEpilogue ();
- }
- ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
- return;
- }
- ec.EmitEpilogue ();
- if (unwind_protect || ec.EmitAccurateDebugInfo)
- ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
- }
- if (unwind_protect) {
- ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
- } else if (ec.EmitAccurateDebugInfo) {
- ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
- } 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class LabeledStatement : Statement {
- string name;
- bool defined;
- bool referenced;
- Label label;
- Block block;
- FlowBranching.UsageVector vectors;
-
- public LabeledStatement (string name, Block block, Location l)
- {
- this.name = name;
- this.block = block;
- this.loc = l;
- }
- public Label LabelTarget (EmitContext ec)
- {
- if (defined)
- return label;
- label = ec.DefineLabel ();
- defined = true;
- return label;
- }
- public Block Block {
- get {
- return block;
- }
- }
- 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)
- {
- if (!HasBeenReferenced)
- ec.Report.Warning (164, 2, loc, "This label has not been referenced");
- LabelTarget (ec);
- ec.MarkLabel (label);
- }
- public void AddReference ()
- {
- referenced = true;
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
-
- /// <summary>
- /// `goto default' statement
- /// </summary>
- 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.DefaultLabel);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- /// <summary>
- /// `goto case' statement
- /// </summary>
- public class GotoCase : Statement {
- Expression expr;
- SwitchLabel sl;
-
- public GotoCase (Expression e, Location l)
- {
- expr = e;
- loc = l;
- }
- public Expression Expr {
- get {
- return this.expr;
- }
- }
-
- 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;
- }
- Constant res;
- if (ec.Switch.IsNullable && c is NullLiteral) {
- res = c;
- } else {
- TypeSpec type = ec.Switch.SwitchType;
- res = c.Reduce (ec, type);
- if (res == null) {
- c.Error_ValueCannotBeConverted (ec, 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));
- }
- sl = ec.Switch.ResolveGotoCase (ec, res);
- return true;
- }
- protected override void DoEmit (EmitContext ec)
- {
- ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- GotoCase target = (GotoCase) t;
- target.expr = expr.Clone (clonectx);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
-
- public class Throw : Statement {
- Expression expr;
-
- public Throw (Expression expr, Location l)
- {
- this.expr = expr;
- loc = l;
- }
- public Expression Expr {
- get {
- return this.expr;
- }
- }
- 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;
- var et = ec.BuiltinTypes.Exception;
- if (Convert.ImplicitConversionExists (ec, expr, et))
- expr = Convert.ImplicitConversion (ec, expr, et, 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- 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 override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- 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; }
- bool IsParameter { 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;
- }
- public Declarator (Declarator clone, Expression initializer)
- {
- this.li = clone.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;
- protected LocalVariable li;
- protected List<Declarator> declarators;
- TypeSpec type;
- 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<Declarator> 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<Declarator> ();
- declarators.Add (decl);
- }
- static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
- {
- if (bc.Report.Errors != 0)
- return;
- var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
- 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 ();
- li.HoistedVariant = new HoistedEvaluatorVariable (f);
- li.SetIsUsed ();
- }
- public override bool Resolve (BlockContext bc)
- {
- return Resolve (bc, true);
- }
- public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
- {
- if (type == null && !li.IsCompilerGenerated) {
- var vexpr = type_expr as 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
- //
- if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
- if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
- bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, 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;
- } else {
- // Set error type to indicate the var was placed correctly but could
- // not be infered
- //
- // var a = missing ();
- //
- type = InternalType.ErrorType;
- }
- }
- if (type == null) {
- type = type_expr.ResolveAsType (bc);
- if (type == null)
- return false;
- if (li.IsConstant && !type.IsConstantCompatible) {
- Const.Error_InvalidConstantType (type, loc, bc.Report);
- }
- }
- if (type.IsStatic)
- FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
- li.Type = type;
- }
- bool eval_global = bc.Module.Compiler.Settings.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 = li.Type;
- if (eval_global) {
- CreateEvaluatorVariable (bc, d.Variable);
- } else {
- d.Variable.PrepareForFlowAnalysis (bc);
- }
- if (d.Initializer != null && resolveDeclaratorInitializers) {
- 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)
- {
- 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) {
- ec.Mark (d.Variable.Location);
- ((ExpressionStatement) d.Initializer).EmitStatement (ec);
- }
- }
- }
- }
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- BlockVariableDeclaration t = (BlockVariableDeclaration) target;
- if (type_expr != null)
- 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, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
- }
- }
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class BlockConstantDeclaration : BlockVariableDeclaration
- {
- public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
- : base (type, li)
- {
- }
- public override void Emit (EmitContext ec)
- {
- // Nothing to emit, not even sequence point
- }
- 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 (li.Type);
- if (c == null) {
- if (TypeSpec.IsReferenceType (li.Type))
- initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
- else
- initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
- return null;
- }
- li.ConstantValue = c;
- return initializer;
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- //
- // 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,
- IsLocked = 1 << 9,
- 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 bool AddressTaken {
- get {
- return (flags & Flags.AddressTaken) != 0;
- }
- }
- 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 IsCompilerGenerated {
- get {
- return (flags & Flags.CompilerGenerated) != 0;
- }
- }
- public bool IsConstant {
- get {
- return (flags & Flags.Constant) != 0;
- }
- }
- public bool IsLocked {
- get {
- return (flags & Flags.IsLocked) != 0;
- }
- set {
- flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
- }
- }
- public bool IsThis {
- get {
- return (flags & Flags.IsThis) != 0;
- }
- }
- public bool IsFixed {
- get {
- return (flags & Flags.FixedVariable) != 0;
- }
- }
- bool INamedBlockVariable.IsParameter {
- get {
- return false;
- }
- }
- 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 (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
- ec.DefineLocalVariable (name, builder);
- }
- public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
- {
- LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (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);
- return new LocalVariableReference (this, loc);
- }
- public void Emit (EmitContext ec)
- {
- // TODO: Need something better for temporary variables
- if ((flags & Flags.CompilerGenerated) != 0)
- CreateBuilder (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 static string GetCompilerGeneratedName (Block block)
- {
- // HACK: Debugger depends on the name semantics
- return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
- }
- 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");
- }
- public bool IsThisAssigned (BlockContext ec, Block block)
- {
- if (VariableInfo == null)
- throw new Exception ();
- if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
- return true;
- return VariableInfo.IsFullyInitialized (ec, 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 void SetHasAddressTaken ()
- {
- flags |= (Flags.AddressTaken | Flags.Used);
- }
- public override string ToString ()
- {
- return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
- }
- }
- /// <summary>
- /// Block represents a C# block.
- /// </summary>
- ///
- /// <remarks>
- /// 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.
- /// </remarks>
- public class Block : Statement {
- [Flags]
- public enum Flags
- {
- Unchecked = 1,
- HasRet = 8,
- Unsafe = 16,
- HasCapturedVariable = 64,
- HasCapturedThis = 1 << 7,
- IsExpressionTree = 1 << 8,
- CompilerGenerated = 1 << 9,
- HasAsyncModifier = 1 << 10,
- Resolved = 1 << 11,
- YieldBlock = 1 << 12,
- AwaitBlock = 1 << 13
- }
- 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<Statement> statements;
- protected List<Statement> 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;
- 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<Statement> (4);
- this.original = this;
- }
- #region Properties
- public bool HasUnreachableClosingBrace {
- get {
- return (flags & Flags.HasRet) != 0;
- }
- set {
- flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
- }
- }
- public Block Original {
- get {
- return original;
- }
- protected set {
- original = value;
- }
- }
- 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; }
- }
- public List<Statement> Statements {
- get { return statements; }
- }
- #endregion
- public Block CreateSwitchBlock (Location start)
- {
- // FIXME: Only explicit block should be created
- var new_block = new Block (this, start, start);
- new_block.IsCompilerGenerated = true;
- return new_block;
- }
- public void SetEndLocation (Location loc)
- {
- EndLocation = loc;
- }
- public void AddLabel (LabeledStatement target)
- {
- ParametersBlock.TopBlock.AddLabel (target.Name, target);
- }
- public void AddLocalName (LocalVariable li)
- {
- AddLocalName (li.Name, li);
- }
- public void AddLocalName (string name, INamedBlockVariable li)
- {
- ParametersBlock.TopBlock.AddLocalName (name, li, false);
- }
- 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) {
- pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
- } else {
- 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<Statement> ();
- //
- // 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 LabeledStatement LookupLabel (string name)
- {
- return ParametersBlock.TopBlock.GetLabel (name, this);
- }
- public override bool Resolve (BlockContext ec)
- {
- if ((flags & Flags.Resolved) != 0)
- return true;
- Block prev_block = ec.CurrentBlock;
- bool ok = true;
- bool unreachable = ec.IsUnreachable;
- bool prev_unreachable = unreachable;
- ec.CurrentBlock = this;
- ec.StartFlowBranching (this);
- //
- // 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 (!ec.UnreachableReported && !(s is LabeledStatement)) {
- ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
- ec.UnreachableReported = 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)
- 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) {
- ec.IsUnreachable = true;
- } else if (ec.IsUnreachable)
- ec.IsUnreachable = false;
- }
- if (unreachable != prev_unreachable) {
- ec.IsUnreachable = prev_unreachable;
- ec.UnreachableReported = false;
- }
- 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;
- flags |= Flags.Resolved;
- return ok;
- }
- public override bool ResolveUnreachable (BlockContext ec, bool warn)
- {
- bool unreachable = false;
- if (warn && !ec.UnreachableReported) {
- ec.UnreachableReported = true;
- unreachable = true;
- ec.Report.Warning (162, 2, loc, "Unreachable code detected");
- }
- var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
- fb.CurrentUsageVector.IsUnreachable = true;
- bool ok = Resolve (ec);
- ec.KillFlowBranching ();
- if (unreachable)
- ec.UnreachableReported = false;
- 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);
- DoEmit (ec);
- }
- protected void EmitScopeInitializers (EmitContext ec)
- {
- foreach (Statement s in scope_initializers)
- s.Emit (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);
- if (original != this)
- clonectx.AddBlockMap (original, 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<Statement> (statements.Count);
- foreach (Statement s in statements)
- target.statements.Add (s.Clone (clonectx));
- }
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- 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 HasAwait {
- get {
- return (flags & Flags.AwaitBlock) != 0;
- }
- }
- public bool HasCapturedThis {
- set {
- flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
- }
- get {
- return (flags & Flags.HasCapturedThis) != 0;
- }
- }
- //
- // Used to indicate that the block has reference to parent
- // block and cannot be made static when defining anonymous method
- //
- public bool HasCapturedVariable {
- set {
- flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
- }
- get {
- return (flags & Flags.HasCapturedVariable) != 0;
- }
- }
- public bool HasYield {
- get {
- return (flags & Flags.YieldBlock) != 0;
- }
- }
- #endregion
- //
- // Creates anonymous method storey in current block
- //
- public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
- {
- //
- // Return same story for iterator and async blocks unless we are
- // in nested anonymous method
- //
- if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
- return ec.CurrentAnonymousMethod.Storey;
- 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", MemberKind.Class);
- }
- return am_storey;
- }
- public override void Emit (EmitContext ec)
- {
- if (am_storey != null) {
- DefineStoreyContainer (ec, am_storey);
- am_storey.EmitStoreyInstantiation (ec, this);
- }
- if (scope_initializers != null)
- EmitScopeInitializers (ec);
- if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
- ec.Emit (OpCodes.Nop);
- }
- if (Parent != null)
- ec.BeginScope ();
- DoEmit (ec);
- if (Parent != null)
- ec.EndScope ();
- if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
- ec.Emit (OpCodes.Nop);
- }
- }
- protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
- {
- if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
- storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
- storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
- }
- //
- // Creates anonymous method storey
- //
- storey.CreateContainer ();
- storey.DefineContainer ();
- if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
- //
- // Only first storey in path will hold this reference. All children blocks will
- // reference it indirectly using $ref field
- //
- for (Block b = Original.Explicit; b != null; b = b.Parent) {
- if (b.Parent != null) {
- var s = b.Parent.Explicit.AnonymousMethodStorey;
- if (s != null) {
- storey.HoistedThis = s.HoistedThis;
- break;
- }
- }
- if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
- storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
- if (storey.HoistedThis != null)
- break;
- }
- }
-
- //
- // We are the first storey on path and 'this' has to be hoisted
- //
- if (storey.HoistedThis == null) {
- foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
- //
- // ThisReferencesFromChildrenBlock holds all reference even if they
- // are not on this path. It saves some memory otherwise it'd have to
- // be in every explicit block. We run this check to see if the reference
- // is valid for this storey
- //
- Block block_on_path = ref_block;
- for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
- if (block_on_path == null)
- continue;
- if (storey.HoistedThis == null) {
- storey.AddCapturedThisField (ec);
- }
- for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
- if (b.AnonymousMethodStorey != null) {
- b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
- b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
- //
- // Stop propagation inside same top block
- //
- if (b.ParametersBlock == ParametersBlock.Original)
- break;
- b = b.ParametersBlock;
- }
- var pb = b as ParametersBlock;
- if (pb != null && pb.StateMachine != null) {
- if (pb.StateMachine == storey)
- break;
- pb.StateMachine.AddParentStoreyReference (ec, storey);
- }
-
- b.HasCapturedVariable = true;
- }
- }
- }
- }
- var ref_blocks = storey.ReferencesFromChildrenBlock;
- if (ref_blocks != null) {
- foreach (ExplicitBlock ref_block in ref_blocks) {
- for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
- if (b.AnonymousMethodStorey != null) {
- b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
- //
- // Stop propagation inside same top block
- //
- if (b.ParametersBlock == ParametersBlock.Original)
- break;
- b = b.ParametersBlock;
- }
- var pb = b as ParametersBlock;
- if (pb != null && pb.StateMachine != null) {
- if (pb.StateMachine == storey)
- break;
- pb.StateMachine.AddParentStoreyReference (ec, storey);
- }
- b.HasCapturedVariable = true;
- }
- }
- }
- storey.Define ();
- storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
- }
- public void RegisterAsyncAwait ()
- {
- var block = this;
- while ((block.flags & Flags.AwaitBlock) == 0) {
- block.flags |= Flags.AwaitBlock;
- if (block is ParametersBlock)
- return;
- block = block.Parent.Explicit;
- }
- }
- public void RegisterIteratorYield ()
- {
- var block = this;
- while ((block.flags & Flags.YieldBlock) == 0) {
- block.flags |= Flags.YieldBlock;
- if (block.Parent == null)
- return;
- block = block.Parent.Explicit;
- }
- }
- public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
- {
- tryBlock.statements = statements;
- statements = new List<Statement> (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;
- bool is_locked;
- public ParameterInfo (ParametersBlock block, int index)
- {
- this.block = block;
- this.index = index;
- }
- #region Properties
- public ParametersBlock Block {
- get {
- return block;
- }
- }
- Block INamedBlockVariable.Block {
- get {
- return block;
- }
- }
- public bool IsDeclared {
- get {
- return true;
- }
- }
- public bool IsParameter {
- get {
- return true;
- }
- }
- public bool IsLocked {
- get {
- return is_locked;
- }
- set {
- is_locked = value;
- }
- }
- 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 into 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 bool ContainsEmitWithAwait ()
- {
- return child.ContainsEmitWithAwait ();
- }
- 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;
- protected StateMachine state_machine;
- 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;
- flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
- 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;
- }
- //
- // It's supposed to be used by method body implementation of anonymous methods
- //
- 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.resolved = true;
- this.unreachable = source.unreachable;
- this.am_storey = source.am_storey;
- this.state_machine = source.state_machine;
- ParametersBlock = this;
- //
- // Overwrite original for comparison purposes when linking cross references
- // between anonymous methods
- //
- Original = source.Original;
- }
- #region Properties
- public bool IsAsync {
- get {
- return (flags & Flags.HasAsyncModifier) != 0;
- }
- set {
- flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
- }
- }
- //
- // 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 StateMachine StateMachine {
- get {
- return state_machine;
- }
- }
- public ToplevelBlock TopBlock {
- get {
- return top_block;
- }
- }
- public bool Resolved {
- get {
- return (flags & Flags.Resolved) != 0;
- }
- }
- public int TemporaryLocalsCount { get; set; }
- #endregion
- // <summary>
- // Check whether all `out' parameters have been assigned.
- // </summary>
- public void CheckOutParameters (FlowBranching.UsageVector vector)
- {
- 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;
- var p = parameter_info[i].Parameter;
- TopBlock.Report.Error (177, p.Location,
- "The out parameter `{0}' must be assigned to before control leaves the current method",
- p.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 override void Emit (EmitContext ec)
- {
- if (state_machine != null && state_machine.OriginalSourceBlock != this) {
- DefineStoreyContainer (ec, state_machine);
- state_machine.EmitStoreyInstantiation (ec, this);
- }
- base.Emit (ec);
- }
- public void EmitEmbedded (EmitContext ec)
- {
- if (state_machine != null && state_machine.OriginalSourceBlock != this) {
- DefineStoreyContainer (ec, state_machine);
- state_machine.EmitStoreyInstantiation (ec, this);
- }
- base.Emit (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 ParameterReference GetParameterReference (int index, Location loc)
- {
- return new ParameterReference (parameter_info[index], loc);
- }
- public Statement PerformClone ()
- {
- CloneContext clonectx = new CloneContext ();
- return Clone (clonectx);
- }
- 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);
- if (p.Name != null)
- 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 || e is FatalException)
- 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 (rc.Module.Compiler.Settings.DebugFlags > 0)
- throw;
- }
- if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
- if (rc.CurrentAnonymousMethod == null) {
- // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
- if (md is StateMachineMethod) {
- unreachable = true;
- } else {
- rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
- return false;
- }
- } else {
- //
- // If an asynchronous body of F is either an expression classified as nothing, or a
- // statement block where no return statements have expressions, the inferred return type is Task
- //
- if (IsAsync) {
- var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
- if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
- am.ReturnTypeInference = null;
- am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
- return true;
- }
- }
- 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) == 0)
- continue;
- VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
- parameter_info[i].VariableInfo = vi;
- ec.FlowOffset += vi.Length;
- }
- }
- public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
- {
- var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
- var stateMachine = new IteratorStorey (iterator);
- state_machine = stateMachine;
- iterator.SetStateMachine (stateMachine);
- var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
- tlb.Original = this;
- tlb.IsCompilerGenerated = true;
- tlb.state_machine = stateMachine;
- tlb.AddStatement (new Return (iterator, iterator.Location));
- return tlb;
- }
- public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
- {
- for (int i = 0; i < parameters.Count; i++) {
- Parameter p = parameters[i];
- Parameter.Modifier mod = p.ModFlags;
- if ((mod & Parameter.Modifier.RefOutMask) != 0) {
- host.Compiler.Report.Error (1988, p.Location,
- "Async methods cannot have ref or out parameters");
- return this;
- }
- if (p is ArglistParameter) {
- host.Compiler.Report.Error (4006, p.Location,
- "__arglist is not allowed in parameter list of async methods");
- return this;
- }
- if (parameters.Types[i].IsPointer) {
- host.Compiler.Report.Error (4005, p.Location,
- "Async methods cannot have unsafe parameters");
- return this;
- }
- }
- if (!HasAwait) {
- host.Compiler.Report.Warning (1998, 1, loc,
- "Async block lacks `await' operator and will run synchronously");
- }
- var block_type = host.Module.Compiler.BuiltinTypes.Void;
- var initializer = new AsyncInitializer (this, host, block_type);
- initializer.Type = block_type;
- var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
- state_machine = stateMachine;
- initializer.SetStateMachine (stateMachine);
- var b = this is ToplevelBlock ?
- new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
- new ParametersBlock (Parent, parameters, Location.Null) {
- IsAsync = true,
- };
- b.Original = this;
- b.IsCompilerGenerated = true;
- b.state_machine = stateMachine;
- b.AddStatement (new StatementExpression (initializer));
- return b;
- }
- }
- //
- //
- //
- public class ToplevelBlock : ParametersBlock
- {
- LocalVariable this_variable;
- CompilerContext compiler;
- Dictionary<string, object> names;
- Dictionary<string, object> labels;
- List<ExplicitBlock> this_references;
- 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;
- flags |= Flags.HasRet;
- 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;
- flags |= Flags.HasRet;
- }
- public bool IsIterator {
- get {
- return HasYield;
- }
- }
- public Report Report {
- get {
- return compiler.Report;
- }
- }
- //
- // Used by anonymous blocks to track references of `this' variable
- //
- public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
- get {
- return this_references;
- }
- }
- //
- // Returns the "this" instance variable of this block.
- // See AddThisVariable() for more information.
- //
- public LocalVariable ThisVariable {
- get {
- return this_variable;
- }
- }
- public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
- {
- if (names == null)
- names = new Dictionary<string, object> ();
- object value;
- if (!names.TryGetValue (name, out value)) {
- names.Add (name, li);
- return;
- }
- INamedBlockVariable existing = value as INamedBlockVariable;
- List<INamedBlockVariable> existing_list;
- if (existing != null) {
- existing_list = new List<INamedBlockVariable> ();
- existing_list.Add (existing);
- names[name] = existing_list;
- } else {
- existing_list = (List<INamedBlockVariable>) value;
- }
- //
- // A collision checking between local names
- //
- for (int i = 0; i < existing_list.Count; ++i) {
- existing = existing_list[i];
- Block b = existing.Block.Explicit;
- // Collision at same level
- if (li.Block.Explicit == b) {
- li.Block.Error_AlreadyDeclared (name, li);
- break;
- }
- // Collision with parent
- Block parent = li.Block.Explicit;
- while ((parent = parent.Parent) != null) {
- if (parent == b) {
- li.Block.Error_AlreadyDeclared (name, li, "parent or current");
- i = existing_list.Count;
- break;
- }
- }
- if (!ignoreChildrenBlocks) {
- // Collision with children
- while ((b = b.Parent) != null) {
- if (li.Block.Explicit == b) {
- li.Block.Error_AlreadyDeclared (name, li, "child");
- i = existing_list.Count;
- break;
- }
- }
- }
- }
- existing_list.Add (li);
- }
- public void AddLabel (string name, LabeledStatement label)
- {
- if (labels == null)
- labels = new Dictionary<string, object> ();
- object value;
- if (!labels.TryGetValue (name, out value)) {
- labels.Add (name, label);
- return;
- }
- LabeledStatement existing = value as LabeledStatement;
- List<LabeledStatement> existing_list;
- if (existing != null) {
- existing_list = new List<LabeledStatement> ();
- existing_list.Add (existing);
- labels[name] = existing_list;
- } else {
- existing_list = (List<LabeledStatement>) value;
- }
- //
- // A collision checking between labels
- //
- for (int i = 0; i < existing_list.Count; ++i) {
- existing = existing_list[i];
- Block b = existing.Block;
- // Collision at same level
- if (label.Block == b) {
- Report.SymbolRelatedToPreviousError (existing.loc, name);
- Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
- break;
- }
- // Collision with parent
- b = label.Block;
- while ((b = b.Parent) != null) {
- if (existing.Block == b) {
- Report.Error (158, label.loc,
- "The label `{0}' shadows another label by the same name in a contained scope", name);
- i = existing_list.Count;
- break;
- }
- }
- // Collision with with children
- b = existing.Block;
- while ((b = b.Parent) != null) {
- if (label.Block == b) {
- Report.Error (158, label.loc,
- "The label `{0}' shadows another label by the same name in a contained scope", name);
- i = existing_list.Count;
- break;
- }
- }
- }
- existing_list.Add (label);
- }
- public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
- {
- if (this_references == null)
- this_references = new List<ExplicitBlock> ();
- if (!this_references.Contains (block))
- this_references.Add (block);
- }
- public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
- {
- this_references.Remove (block);
- }
- //
- // 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.Original)
- 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<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
- for (int i = 0; i < list.Count; ++i) {
- variable = list[i];
- do {
- if (variable.Block == b.Original)
- 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;
- }
- public LabeledStatement GetLabel (string name, Block block)
- {
- if (labels == null)
- return null;
- object value;
- if (!labels.TryGetValue (name, out value)) {
- return null;
- }
- var label = value as LabeledStatement;
- Block b = block;
- if (label != null) {
- if (label.Block == b.Original)
- return label;
- // TODO: Temporary workaround for the switch block implicit label block
- if (label.Block.IsCompilerGenerated && (label.Block.Parent == b.Original || label.Block == b.Original.Parent))
- return label;
- } else {
- List<LabeledStatement> list = (List<LabeledStatement>) value;
- for (int i = 0; i < list.Count; ++i) {
- label = list[i];
- if (label.Block == b.Original)
- return label;
- // TODO: Temporary workaround for the switch block implicit label block
- if (label.Block.IsCompilerGenerated && (label.Block.Parent == b.Original || label.Block == b.Original.Parent))
- return label;
- }
- }
-
- return null;
- }
- // <summary>
- // 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.
- // </summary>
- public void AddThisVariable (BlockContext bc)
- {
- if (this_variable != null)
- throw new InternalErrorException (StartLocation.ToString ());
- this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
- this_variable.Type = bc.CurrentType;
- this_variable.PrepareForFlowAnalysis (bc);
- }
- 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 (IsCompilerGenerated) {
- using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- base.Emit (ec);
- }
- } else {
- base.Emit (ec);
- }
- //
- // 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.HasReturnLabel)
- ec.MarkLabel (ec.ReturnLabel);
- if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
- ec.Mark (EndLocation);
- if (ec.ReturnType.Kind != MemberKind.Void)
- 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
- }
- }
-
- public class SwitchLabel {
- Expression label;
- Constant converted;
- readonly Location loc;
- Label? il_label;
- //
- // if expr == null, then it is the default case.
- //
- public SwitchLabel (Expression expr, Location l)
- {
- label = expr;
- loc = l;
- }
- public bool IsDefault {
- get {
- return label == null;
- }
- }
- public Expression Label {
- get {
- return label;
- }
- }
- public Location Location {
- get {
- return loc;
- }
- }
- public Constant Converted {
- get {
- return converted;
- }
- set {
- converted = value;
- }
- }
- public Label GetILLabel (EmitContext ec)
- {
- if (il_label == null){
- il_label = ec.DefineLabel ();
- }
- return il_label.Value;
- }
- //
- // 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 (allow_nullable && c is NullLiteral) {
- converted = c;
- return true;
- }
- converted = c.ImplicitConversionRequired (ec, required_type, loc);
- return converted != null;
- }
- public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
- {
- string label;
- if (converted == null)
- label = "default";
- else
- label = converted.GetValueAsLiteral ();
-
- 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)
- {
- if (label == null)
- return this;
- return new SwitchLabel (label.Clone (clonectx), loc);
- }
- }
- public class SwitchSection {
- public readonly List<SwitchLabel> Labels;
- public readonly Block Block;
-
- public SwitchSection (List<SwitchLabel> labels, Block block)
- {
- Labels = labels;
- Block = block;
- }
- public SwitchSection Clone (CloneContext clonectx)
- {
- var cloned_labels = new List<SwitchLabel> ();
- foreach (SwitchLabel sl in Labels)
- cloned_labels.Add (sl.Clone (clonectx));
-
- return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
- }
- }
-
- public class Switch : Statement
- {
- // structure used to hold blocks of keys while calculating table switch
- sealed class LabelsRange : IComparable<LabelsRange>
- {
- public readonly long min;
- public long max;
- public readonly List<long> label_values;
- public LabelsRange (long value)
- {
- min = max = value;
- label_values = new List<long> ();
- label_values.Add (value);
- }
- public LabelsRange (long min, long max, ICollection<long> values)
- {
- this.min = min;
- this.max = max;
- this.label_values = new List<long> (values);
- }
- public long Range {
- get {
- return max - min + 1;
- }
- }
- public bool AddValue (long value)
- {
- var gap = value - min + 1;
- // Ensure the range has > 50% occupancy
- if (gap > 2 * (label_values.Count + 1) || gap <= 0)
- return false;
- max = value;
- label_values.Add (value);
- return true;
- }
- public int CompareTo (LabelsRange other)
- {
- int nLength = label_values.Count;
- int nLengthOther = other.label_values.Count;
- if (nLengthOther == nLength)
- return (int) (other.min - min);
- return nLength - nLengthOther;
- }
- }
- sealed class LabelMarker : Statement
- {
- readonly Switch s;
- readonly List<SwitchLabel> labels;
- public LabelMarker (Switch s, List<SwitchLabel> labels)
- {
- this.s = s;
- this.labels = labels;
- }
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- }
- protected override void DoEmit (EmitContext ec)
- {
- foreach (var l in labels) {
- if (l.IsDefault)
- ec.MarkLabel (s.DefaultLabel);
- else
- ec.MarkLabel (l.GetILLabel (ec));
- }
- }
- }
- public List<SwitchSection> Sections;
- public Expression Expr;
- //
- // Mapping of all labels to their SwitchLabels
- //
- Dictionary<long, SwitchLabel> labels;
- Dictionary<string, SwitchLabel> string_labels;
- /// <summary>
- /// The governing switch type
- /// </summary>
- public TypeSpec SwitchType;
- //
- // Computed
- //
- Label default_target;
- Label null_target;
- Expression new_expr;
- bool is_constant;
- SwitchSection constant_section;
- SwitchSection default_section;
- SwitchLabel null_section;
- Statement simple_stmt;
- VariableReference value;
- ExpressionStatement string_dictionary;
- FieldExpr switch_cache_field;
- ExplicitBlock block;
- //
- // Nullable Types support
- //
- Nullable.Unwrap unwrap;
- public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
- {
- Expr = e;
- this.block = block;
- Sections = sects;
- loc = l;
- }
- public ExplicitBlock Block {
- get {
- return block;
- }
- }
- public Label DefaultLabel {
- get {
- return default_target;
- }
- }
- public bool GotDefault {
- get {
- return default_section != null;
- }
- }
- public bool IsNullable {
- get {
- return unwrap != null;
- }
- }
- //
- // Determines the governing type for a switch. The returned
- // expression might be the expression from the switch, or an
- // expression that includes any potential conversions to
- //
- Expression SwitchGoverningType (ResolveContext ec, Expression expr)
- {
- switch (expr.Type.BuiltinType) {
- case BuiltinTypeSpec.Type.Byte:
- case BuiltinTypeSpec.Type.SByte:
- case BuiltinTypeSpec.Type.UShort:
- case BuiltinTypeSpec.Type.Short:
- case BuiltinTypeSpec.Type.UInt:
- case BuiltinTypeSpec.Type.Int:
- case BuiltinTypeSpec.Type.ULong:
- case BuiltinTypeSpec.Type.Long:
- case BuiltinTypeSpec.Type.Char:
- case BuiltinTypeSpec.Type.String:
- case BuiltinTypeSpec.Type.Bool:
- return expr;
- }
- if (expr.Type.IsEnum)
- return expr;
- //
- // Try to find a *user* defined implicit conversion.
- //
- // If there is no implicit conversion, or if there are multiple
- // conversions, we have to report an error
- //
- Expression converted = null;
- foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
- Expression e;
-
- e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
- if (e == null)
- continue;
- //
- // Ignore over-worked ImplicitUserConversions that do
- // an implicit conversion in addition to the user conversion.
- //
- if (!(e is UserCast))
- continue;
- if (converted != null){
- ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
- return null;
- }
- converted = e;
- }
- return converted;
- }
- public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
- {
- // LAMESPEC: For some reason it does not contain bool which looks like csc bug
- return new[] {
- types.SByte,
- types.Byte,
- types.Short,
- types.UShort,
- types.Int,
- types.UInt,
- types.Long,
- types.ULong,
- types.Char,
- types.String
- };
- }
- //
- // Performs the basic sanity checks on the switch statement
- // (looks for duplicate keys and non-constant expressions).
- //
- // It also returns a hashtable with the keys that we will later
- // use to compute the switch tables
- //
- bool CheckSwitch (ResolveContext ec)
- {
- bool error = false;
- if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
- string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
- else
- labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
-
- foreach (SwitchSection ss in Sections){
- foreach (SwitchLabel sl in ss.Labels){
- if (sl.IsDefault){
- if (default_section != null){
- sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
- error = true;
- }
- default_section = ss;
- continue;
- }
- if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
- error = true;
- continue;
- }
-
- try {
- if (string_labels != null) {
- string s = sl.Converted.GetValue () as string;
- if (s == null)
- null_section = sl;
- else
- string_labels.Add (s, sl);
- } else {
- if (sl.Converted is NullLiteral) {
- null_section = sl;
- } else {
- labels.Add (sl.Converted.GetValueAsLong (), sl);
- }
- }
- } catch (ArgumentException) {
- if (string_labels != null)
- sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
- else
- sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
- error = true;
- }
- }
- }
- return !error;
- }
-
- //
- // This method emits code for a lookup-based switch statement (non-string)
- // Basically it groups the cases into blocks that are at least half full,
- // and then spits out individual lookup opcodes for each block.
- // It emits the longest blocks first, and short blocks are just
- // handled with direct compares.
- //
- void EmitTableSwitch (EmitContext ec, Expression val)
- {
- Label lbl_default = default_target;
- if (labels != null && labels.Count > 0) {
- List<LabelsRange> ranges;
- if (string_labels != null) {
- // We have done all hard work for string already
- // setup single range only
- ranges = new List<LabelsRange> (1);
- ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
- } else {
- var element_keys = new long[labels.Count];
- labels.Keys.CopyTo (element_keys, 0);
- Array.Sort (element_keys);
- //
- // Build possible ranges of switch labes to reduce number
- // of comparisons
- //
- ranges = new List<LabelsRange> (element_keys.Length);
- var range = new LabelsRange (element_keys[0]);
- ranges.Add (range);
- for (int i = 1; i < element_keys.Length; ++i) {
- var l = element_keys[i];
- if (range.AddValue (l))
- continue;
- range = new LabelsRange (l);
- ranges.Add (range);
- }
- // sort the blocks so we can tackle the largest ones first
- ranges.Sort ();
- }
- TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
- for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
- LabelsRange kb = ranges[range_index];
- lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
- // Optimize small ranges using simple equality check
- if (kb.Range <= 2) {
- foreach (var key in kb.label_values) {
- SwitchLabel sl = labels[key];
- if (sl.Converted.IsDefaultValue) {
- val.EmitBranchable (ec, sl.GetILLabel (ec), false);
- } else {
- val.Emit (ec);
- sl.Converted.Emit (ec);
- ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
- }
- }
- } else {
- // TODO: if all the keys in the block are the same and there are
- // no gaps/defaults then just use a range-check.
- if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
- // TODO: optimize constant/I4 cases
- // check block range (could be > 2^31)
- val.Emit (ec);
- ec.EmitLong (kb.min);
- ec.Emit (OpCodes.Blt, lbl_default);
- val.Emit (ec);
- ec.EmitLong (kb.max);
- ec.Emit (OpCodes.Bgt, lbl_default);
- // normalize range
- val.Emit (ec);
- if (kb.min != 0) {
- ec.EmitLong (kb.min);
- ec.Emit (OpCodes.Sub);
- }
- ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
- } else {
- // normalize range
- val.Emit (ec);
- int first = (int) kb.min;
- if (first > 0) {
- ec.EmitInt (first);
- ec.Emit (OpCodes.Sub);
- } else if (first < 0) {
- ec.EmitInt (-first);
- ec.Emit (OpCodes.Add);
- }
- }
- // first, build the list of labels for the switch
- int iKey = 0;
- long cJumps = kb.Range;
- Label[] switch_labels = new Label[cJumps];
- for (int iJump = 0; iJump < cJumps; iJump++) {
- var key = kb.label_values[iKey];
- if (key == kb.min + iJump) {
- switch_labels[iJump] = labels[key].GetILLabel (ec);
- iKey++;
- } else {
- switch_labels[iJump] = lbl_default;
- }
- }
- // emit the switch opcode
- ec.Emit (OpCodes.Switch, switch_labels);
- }
- // mark the default for this block
- if (range_index != 0)
- ec.MarkLabel (lbl_default);
- }
- // the last default just goes to the end
- if (ranges.Count > 0)
- ec.Emit (OpCodes.Br, lbl_default);
- }
- // now emit the code for the sections
- bool found_default = false;
- foreach (SwitchSection ss in Sections) {
- foreach (SwitchLabel sl in ss.Labels) {
- if (sl.IsDefault) {
- ec.MarkLabel (lbl_default);
- found_default = true;
- if (null_section == null)
- ec.MarkLabel (null_target);
- } else if (sl.Converted.IsNull) {
- ec.MarkLabel (null_target);
- }
- ec.MarkLabel (sl.GetILLabel (ec));
- }
- ss.Block.Emit (ec);
- }
-
- if (!found_default) {
- ec.MarkLabel (lbl_default);
- if (null_section == null) {
- ec.MarkLabel (null_target);
- }
- }
- }
- SwitchLabel FindLabel (Constant value)
- {
- SwitchLabel sl = null;
- if (string_labels != null) {
- string s = value.GetValue () as string;
- if (s == null) {
- if (null_section != null)
- sl = null_section;
- else if (default_section != null)
- sl = default_section.Labels[0];
- } else {
- string_labels.TryGetValue (s, out sl);
- }
- } else {
- if (value is NullLiteral) {
- sl = null_section;
- } else {
- labels.TryGetValue (value.GetValueAsLong (), out sl);
- }
- }
- return sl;
- }
- SwitchSection FindSection (SwitchLabel label)
- {
- foreach (SwitchSection ss in Sections){
- foreach (SwitchLabel sl in ss.Labels){
- if (label == sl)
- return ss;
- }
- }
- return null;
- }
- public override bool Resolve (BlockContext ec)
- {
- Expr = Expr.Resolve (ec);
- if (Expr == null)
- return false;
- new_expr = SwitchGoverningType (ec, Expr);
- if (new_expr == null && Expr.Type.IsNullableType) {
- unwrap = Nullable.Unwrap.Create (Expr, false);
- if (unwrap == null)
- return false;
- new_expr = SwitchGoverningType (ec, unwrap);
- }
- if (new_expr == null){
- ec.Report.Error (151, loc,
- "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
- TypeManager.CSharpName (Expr.Type));
- return false;
- }
- // Validate switch.
- SwitchType = new_expr.Type;
- if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
- ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
- return false;
- }
- if (!CheckSwitch (ec))
- return false;
- Switch old_switch = ec.Switch;
- ec.Switch = this;
- ec.Switch.SwitchType = SwitchType;
- ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
- var constant = new_expr as Constant;
- if (constant != null) {
- is_constant = true;
- SwitchLabel label = FindLabel (constant);
- if (label != null)
- constant_section = FindSection (label);
- if (constant_section == null)
- constant_section = default_section;
- } else {
- //
- // Store switch expression for comparison purposes
- //
- value = new_expr as VariableReference;
- if (value == null) {
- // Create temporary variable inside switch scope
- var block = ec.CurrentBlock;
- ec.CurrentBlock = Block;
- value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
- value.Resolve (ec);
- ec.CurrentBlock = block;
- }
- }
- bool first = true;
- bool ok = true;
- foreach (SwitchSection ss in Sections){
- if (!first)
- ec.CurrentBranching.CreateSibling (
- null, FlowBranching.SiblingType.SwitchSection);
- else
- first = false;
- if (is_constant && (ss != constant_section)) {
- // If we're a constant switch, we're only emitting
- // one single section - mark all the others as
- // unreachable.
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- if (!ss.Block.ResolveUnreachable (ec, true)) {
- ok = false;
- }
- } else {
- if (!ss.Block.Resolve (ec))
- ok = false;
- }
- }
- if (default_section == null)
- ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
- ec.EndFlowBranching ();
- ec.Switch = old_switch;
- if (!ok)
- return false;
- if (!is_constant) {
- if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
- if (string_labels.Count < 7)
- ResolveSimpleSwitch (ec);
- else
- ResolveStringSwitchMap (ec);
- } else if (labels.Count < 3 && !IsNullable) {
- ResolveSimpleSwitch (ec);
- }
- }
- return true;
- }
- public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
- {
- var sl = FindLabel (value);
- if (sl == null) {
- FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
- }
- return sl;
- }
- //
- // Prepares switch using simple if/else comparison for small label count (4 + optional default)
- //
- void ResolveSimpleSwitch (BlockContext bc)
- {
- simple_stmt = default_section != null ? default_section.Block : null;
- for (int i = Sections.Count - 1; i >= 0; --i) {
- var s = Sections[i];
- if (s == default_section) {
- s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
- continue;
- }
- s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
- Expression cond = null;
- for (int ci = 0; ci < s.Labels.Count; ++ci) {
- var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted);
- if (ci > 0) {
- cond = new Binary (Binary.Operator.LogicalOr, cond, e);
- } else {
- cond = e;
- }
- }
- //
- // Compiler generated, hide from symbol file
- //
- simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
- }
- // It's null for empty switch
- if (simple_stmt != null)
- simple_stmt.Resolve (bc);
- }
- //
- // Converts string switch into string hashtable
- //
- void ResolveStringSwitchMap (ResolveContext ec)
- {
- FullNamedExpression string_dictionary_type;
- if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
- string_dictionary_type = new TypeExpression (
- ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
- new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
- loc);
- } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
- string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
- } else {
- ec.Module.PredefinedTypes.Dictionary.Resolve ();
- return;
- }
- var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
- Field field = new Field (ctype, string_dictionary_type,
- Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
- new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
- if (!field.Define ())
- return;
- ctype.AddField (field);
- var init = new List<Expression> ();
- int counter = 0;
- labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
- string value = null;
- foreach (SwitchSection section in Sections) {
- bool contains_label = false;
- foreach (SwitchLabel sl in section.Labels) {
- if (sl.IsDefault || sl.Converted.IsNull)
- continue;
- if (!contains_label) {
- labels.Add (counter, sl);
- contains_label = true;
- }
- value = (string) sl.Converted.GetValue ();
- var init_args = new List<Expression> (2);
- init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
- sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
- init_args.Add (sl.Converted);
- init.Add (new CollectionElementInitializer (init_args, loc));
- }
- //
- // Don't add empty sections
- //
- if (contains_label)
- ++counter;
- }
- Arguments args = new Arguments (1);
- args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
- Expression initializer = new NewInitialize (string_dictionary_type, args,
- new CollectionOrObjectInitializers (init, loc), loc);
- switch_cache_field = new FieldExpr (field, loc);
- string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
- }
- void DoEmitStringSwitch (EmitContext ec)
- {
- Label l_initialized = ec.DefineLabel ();
- //
- // Skip initialization when value is null
- //
- value.EmitBranchable (ec, null_target, false);
- //
- // Check if string dictionary is initialized and initialize
- //
- switch_cache_field.EmitBranchable (ec, l_initialized, true);
- using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- string_dictionary.EmitStatement (ec);
- }
- ec.MarkLabel (l_initialized);
- LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
- ResolveContext rc = new ResolveContext (ec.MemberContext);
- if (switch_cache_field.Type.IsGeneric) {
- Arguments get_value_args = new Arguments (2);
- get_value_args.Add (new Argument (value));
- get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
- Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
- if (get_item == null)
- return;
- //
- // A value was not found, go to default case
- //
- get_item.EmitBranchable (ec, default_target, false);
- } else {
- Arguments get_value_args = new Arguments (1);
- get_value_args.Add (new Argument (value));
- Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
- if (get_item == null)
- return;
- LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
- get_item_object.EmitAssign (ec, get_item, true, false);
- ec.Emit (OpCodes.Brfalse, default_target);
- ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
- new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
- get_item_int.EmitStatement (ec);
- get_item_object.Release (ec);
- }
- EmitTableSwitch (ec, string_switch_variable);
- string_switch_variable.Release (ec);
- }
- protected override void DoEmit (EmitContext ec)
- {
- // Workaround broken flow-analysis
- block.HasUnreachableClosingBrace = true;
- //
- // Needed to emit anonymous storey initialization
- // Otherwise it does not contain any statements for now
- //
- block.Emit (ec);
- default_target = ec.DefineLabel ();
- null_target = ec.DefineLabel ();
- if (IsNullable) {
- unwrap.EmitCheck (ec);
- ec.Emit (OpCodes.Brfalse, null_target);
- value.EmitAssign (ec, new_expr, false, false);
- } else if (new_expr != value && !is_constant) {
- value.EmitAssign (ec, new_expr, false, false);
- }
- //
- // Setup the codegen context
- //
- Label old_end = ec.LoopEnd;
- Switch old_switch = ec.Switch;
-
- ec.LoopEnd = ec.DefineLabel ();
- ec.Switch = this;
- // Emit Code.
- if (is_constant) {
- if (constant_section != null)
- constant_section.Block.Emit (ec);
- } else if (string_dictionary != null) {
- DoEmitStringSwitch (ec);
- } else if (simple_stmt != null) {
- simple_stmt.Emit (ec);
- } else {
- EmitTableSwitch (ec, value);
- }
- // Restore context state.
- ec.MarkLabel (ec.LoopEnd);
- //
- // Restore the previous context
- //
- 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 List<SwitchSection> ();
- foreach (SwitchSection ss in Sections){
- target.Sections.Add (ss.Clone (clonectx));
- }
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- // A place where execution can restart in an iterator
- public abstract class ResumableStatement : Statement
- {
- bool prepared;
- protected Label resume_point;
- public Label PrepareForEmit (EmitContext ec)
- {
- if (!prepared) {
- prepared = true;
- resume_point = ec.DefineLabel ();
- }
- return resume_point;
- }
- public virtual Label PrepareForDispose (EmitContext ec, Label end)
- {
- return end;
- }
- public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
- {
- }
- }
- public abstract class TryFinallyBlock : ExceptionStatement
- {
- protected Statement stmt;
- Label dispose_try_block;
- bool prepared_for_dispose, emitted_dispose;
- Method finally_host;
- protected TryFinallyBlock (Statement stmt, Location loc)
- : base (loc)
- {
- this.stmt = stmt;
- }
- #region Properties
- public Statement Statement {
- get {
- return stmt;
- }
- }
- #endregion
- protected abstract void EmitTryBody (EmitContext ec);
- public abstract void EmitFinallyBody (EmitContext ec);
- public override Label PrepareForDispose (EmitContext ec, Label end)
- {
- if (!prepared_for_dispose) {
- prepared_for_dispose = true;
- dispose_try_block = ec.DefineLabel ();
- }
- return dispose_try_block;
- }
- protected sealed override void DoEmit (EmitContext ec)
- {
- EmitTryBodyPrepare (ec);
- EmitTryBody (ec);
- ec.BeginFinallyBlock ();
- Label start_finally = ec.DefineLabel ();
- if (resume_points != null) {
- var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
- ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
- ec.Emit (OpCodes.Brfalse_S, start_finally);
- ec.Emit (OpCodes.Endfinally);
- }
- ec.MarkLabel (start_finally);
- if (finally_host != null) {
- finally_host.Define ();
- finally_host.Emit ();
- // Now it's safe to add, to close it properly and emit sequence points
- finally_host.Parent.AddMember (finally_host);
- var ce = new CallEmitter ();
- ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
- ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
- } else {
- EmitFinallyBody (ec);
- }
- ec.EndExceptionBlock ();
- }
- public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
- {
- if (emitted_dispose)
- return;
- emitted_dispose = true;
- Label end_of_try = ec.DefineLabel ();
- // Ensure that the only way we can get into this code is through a dispatcher
- if (have_dispatcher)
- ec.Emit (OpCodes.Br, end);
- ec.BeginExceptionBlock ();
- ec.MarkLabel (dispose_try_block);
- Label[] labels = null;
- for (int i = 0; i < resume_points.Count; ++i) {
- ResumableStatement s = resume_points[i];
- Label ret = s.PrepareForDispose (ec, end_of_try);
- if (ret.Equals (end_of_try) && labels == null)
- continue;
- if (labels == null) {
- labels = new Label[resume_points.Count];
- for (int j = 0; j < i; ++j)
- labels[j] = end_of_try;
- }
- labels[i] = ret;
- }
- if (labels != null) {
- int j;
- for (j = 1; j < labels.Length; ++j)
- if (!labels[0].Equals (labels[j]))
- break;
- bool emit_dispatcher = j < labels.Length;
- if (emit_dispatcher) {
- ec.Emit (OpCodes.Ldloc, pc);
- ec.EmitInt (first_resume_pc);
- ec.Emit (OpCodes.Sub);
- ec.Emit (OpCodes.Switch, labels);
- }
- foreach (ResumableStatement s in resume_points)
- s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
- }
- ec.MarkLabel (end_of_try);
- ec.BeginFinallyBlock ();
- if (finally_host != null) {
- var ce = new CallEmitter ();
- ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
- ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
- } else {
- EmitFinallyBody (ec);
- }
- ec.EndExceptionBlock ();
- }
- public override bool Resolve (BlockContext bc)
- {
- //
- // Finally block inside iterator is called from MoveNext and
- // Dispose methods that means we need to lift the block into
- // newly created host method to emit the body only once. The
- // original block then simply calls the newly generated method.
- //
- if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
- var b = stmt as Block;
- if (b != null && b.Explicit.HasYield) {
- finally_host = bc.CurrentIterator.CreateFinallyHost (this);
- }
- }
- return base.Resolve (bc);
- }
- }
- //
- // Base class for blocks using exception handling
- //
- public abstract class ExceptionStatement : ResumableStatement
- {
- #if !STATIC
- bool code_follows;
- #endif
- protected List<ResumableStatement> resume_points;
- protected int first_resume_pc;
- protected ExceptionStatement (Location loc)
- {
- this.loc = loc;
- }
- protected virtual void EmitTryBodyPrepare (EmitContext ec)
- {
- StateMachineInitializer state_machine = null;
- if (resume_points != null) {
- state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
- ec.EmitInt ((int) IteratorStorey.State.Running);
- ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
- }
- ec.BeginExceptionBlock ();
- if (resume_points != null) {
- ec.MarkLabel (resume_point);
- // For normal control flow, we want to fall-through the Switch
- // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
- ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
- ec.EmitInt (first_resume_pc);
- ec.Emit (OpCodes.Sub);
- Label[] labels = new Label[resume_points.Count];
- for (int i = 0; i < resume_points.Count; ++i)
- labels[i] = resume_points[i].PrepareForEmit (ec);
- ec.Emit (OpCodes.Switch, labels);
- }
- }
- public void SomeCodeFollows ()
- {
- #if !STATIC
- code_follows = true;
- #endif
- }
- public override bool Resolve (BlockContext ec)
- {
- #if !STATIC
- // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
- // So, ensure there's some IL code after this statement.
- if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
- ec.NeedReturnLabel ();
- #endif
- return true;
- }
- public void AddResumePoint (ResumableStatement stmt, int pc)
- {
- if (resume_points == null) {
- resume_points = new List<ResumableStatement> ();
- first_resume_pc = pc;
- }
- if (pc != first_resume_pc + resume_points.Count)
- throw new InternalErrorException ("missed an intervening AddResumePoint?");
- resume_points.Add (stmt);
- }
- }
- public class Lock : TryFinallyBlock
- {
- Expression expr;
- TemporaryVariableReference expr_copy;
- TemporaryVariableReference lock_taken;
-
- public Lock (Expression expr, Statement stmt, Location loc)
- : base (stmt, loc)
- {
- this.expr = expr;
- }
- public Expression Expr {
- get {
- return this.expr;
- }
- }
- public override bool Resolve (BlockContext ec)
- {
- expr = expr.Resolve (ec);
- if (expr == null)
- return false;
- if (!TypeSpec.IsReferenceType (expr.Type)) {
- ec.Report.Error (185, loc,
- "`{0}' is not a reference type as required by the lock statement",
- expr.Type.GetSignatureForError ());
- }
- if (expr.Type.IsGenericParameter) {
- expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
- }
- VariableReference lv = expr as VariableReference;
- bool locked;
- if (lv != null) {
- locked = lv.IsLockedByStatement;
- lv.IsLockedByStatement = true;
- } else {
- lv = null;
- locked = false;
- }
- //
- // Have to keep original lock value around to unlock same location
- // in the case of original value has changed or is null
- //
- expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
- expr_copy.Resolve (ec);
- //
- // Ensure Monitor methods are available
- //
- if (ResolvePredefinedMethods (ec) > 1) {
- lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
- lock_taken.Resolve (ec);
- }
- using (ec.Set (ResolveContext.Options.LockScope)) {
- ec.StartFlowBranching (this);
- Statement.Resolve (ec);
- ec.EndFlowBranching ();
- }
- if (lv != null) {
- lv.IsLockedByStatement = locked;
- }
- base.Resolve (ec);
- return true;
- }
-
- protected override void EmitTryBodyPrepare (EmitContext ec)
- {
- expr_copy.EmitAssign (ec, expr);
- if (lock_taken != null) {
- //
- // Initialize ref variable
- //
- lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
- } else {
- //
- // Monitor.Enter (expr_copy)
- //
- expr_copy.Emit (ec);
- ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
- }
- base.EmitTryBodyPrepare (ec);
- }
- protected override void EmitTryBody (EmitContext ec)
- {
- //
- // Monitor.Enter (expr_copy, ref lock_taken)
- //
- if (lock_taken != null) {
- expr_copy.Emit (ec);
- lock_taken.LocalInfo.CreateBuilder (ec);
- lock_taken.AddressOf (ec, AddressOp.Load);
- ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
- }
- Statement.Emit (ec);
- }
- public override void EmitFinallyBody (EmitContext ec)
- {
- //
- // if (lock_taken) Monitor.Exit (expr_copy)
- //
- Label skip = ec.DefineLabel ();
- if (lock_taken != null) {
- lock_taken.Emit (ec);
- ec.Emit (OpCodes.Brfalse_S, skip);
- }
- expr_copy.Emit (ec);
- var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
- if (m != null)
- ec.Emit (OpCodes.Call, m);
- ec.MarkLabel (skip);
- }
- int ResolvePredefinedMethods (ResolveContext rc)
- {
- // Try 4.0 Monitor.Enter (object, ref bool) overload first
- var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
- if (m != null)
- return 4;
- m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
- if (m != null)
- return 1;
- rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
- return 0;
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Lock target = (Lock) t;
- target.expr = expr.Clone (clonectx);
- target.stmt = Statement.Clone (clonectx);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class Unchecked : Statement {
- public Block Block;
-
- public Unchecked (Block b, Location loc)
- {
- Block = b;
- b.Unchecked = true;
- this.loc = loc;
- }
- public override bool Resolve (BlockContext ec)
- {
- using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
- return Block.Resolve (ec);
- }
-
- protected override void DoEmit (EmitContext ec)
- {
- using (ec.With (EmitContext.Options.CheckedScope, false))
- Block.Emit (ec);
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Unchecked target = (Unchecked) t;
- target.Block = clonectx.LookupBlock (Block);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class Checked : Statement {
- public Block Block;
-
- public Checked (Block b, Location loc)
- {
- Block = b;
- b.Unchecked = false;
- this.loc = loc;
- }
- public override bool Resolve (BlockContext ec)
- {
- using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
- return Block.Resolve (ec);
- }
- protected override void DoEmit (EmitContext ec)
- {
- using (ec.With (EmitContext.Options.CheckedScope, true))
- Block.Emit (ec);
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Checked target = (Checked) t;
- target.Block = clonectx.LookupBlock (Block);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class Unsafe : Statement {
- public Block Block;
- public Unsafe (Block b, Location loc)
- {
- Block = b;
- Block.Unsafe = true;
- this.loc = loc;
- }
- public override bool Resolve (BlockContext ec)
- {
- if (ec.CurrentIterator != null)
- ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
- using (ec.Set (ResolveContext.Options.UnsafeScope))
- return Block.Resolve (ec);
- }
-
- protected override void DoEmit (EmitContext ec)
- {
- Block.Emit (ec);
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Unsafe target = (Unsafe) t;
- target.Block = clonectx.LookupBlock (Block);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- //
- // Fixed statement
- //
- public class Fixed : Statement
- {
- abstract class Emitter : ShimExpression
- {
- protected LocalVariable vi;
- protected Emitter (Expression expr, LocalVariable li)
- : base (expr)
- {
- vi = li;
- }
- public abstract void EmitExit (EmitContext ec);
- }
- class ExpressionEmitter : Emitter {
- public ExpressionEmitter (Expression converted, LocalVariable li) :
- base (converted, li)
- {
- }
- protected override Expression DoResolve (ResolveContext rc)
- {
- throw new NotImplementedException ();
- }
- public override void Emit (EmitContext ec) {
- //
- // Store pointer in pinned location
- //
- expr.Emit (ec);
- vi.EmitAssign (ec);
- }
- public override void EmitExit (EmitContext ec)
- {
- ec.EmitInt (0);
- ec.Emit (OpCodes.Conv_U);
- vi.EmitAssign (ec);
- }
- }
- class StringEmitter : Emitter
- {
- LocalVariable pinned_string;
- public StringEmitter (Expression expr, LocalVariable li, Location loc)
- : base (expr, li)
- {
- }
- protected override Expression DoResolve (ResolveContext rc)
- {
- pinned_string = new LocalVariable (vi.Block, "$pinned",
- LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
- vi.Location);
- pinned_string.Type = rc.BuiltinTypes.String;
- eclass = ExprClass.Variable;
- type = rc.BuiltinTypes.Int;
- return this;
- }
- public override void Emit (EmitContext ec)
- {
- pinned_string.CreateBuilder (ec);
- expr.Emit (ec);
- pinned_string.EmitAssign (ec);
- // TODO: Should use Binary::Add
- pinned_string.Emit (ec);
- ec.Emit (OpCodes.Conv_I);
- var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
- if (m == null)
- return;
- PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
- //pe.InstanceExpression = pinned_string;
- pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
- ec.Emit (OpCodes.Add);
- vi.EmitAssign (ec);
- }
- public override void EmitExit (EmitContext ec)
- {
- ec.EmitNull ();
- pinned_string.EmitAssign (ec);
- }
- }
- public class VariableDeclaration : BlockVariableDeclaration
- {
- public VariableDeclaration (FullNamedExpression type, LocalVariable li)
- : base (type, li)
- {
- }
- protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
- {
- if (!Variable.Type.IsPointer && li == Variable) {
- bc.Report.Error (209, TypeExpression.Location,
- "The type of locals declared in a fixed statement must be a pointer type");
- return null;
- }
- //
- // The rules for the possible declarators are pretty wise,
- // but the production on the grammar is more concise.
- //
- // So we have to enforce these rules here.
- //
- // We do not resolve before doing the case 1 test,
- // because the grammar is explicit in that the token &
- // is present, so we need to test for this particular case.
- //
- if (initializer is Cast) {
- bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
- return null;
- }
- initializer = initializer.Resolve (bc);
- if (initializer == null)
- return null;
- //
- // Case 1: Array
- //
- if (initializer.Type.IsArray) {
- TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
- //
- // Provided that array_type is unmanaged,
- //
- if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
- return null;
- //
- // and T* is implicitly convertible to the
- // pointer type given in the fixed statement.
- //
- ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
- Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
- if (converted == null)
- return null;
- //
- // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
- //
- converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
- new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
- new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
- new NullLiteral (loc),
- converted, loc);
- converted = converted.Resolve (bc);
- return new ExpressionEmitter (converted, li);
- }
- //
- // Case 2: string
- //
- if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
- return new StringEmitter (initializer, li, loc).Resolve (bc);
- }
- // Case 3: fixed buffer
- if (initializer is FixedBufferPtr) {
- return new ExpressionEmitter (initializer, li);
- }
- //
- // Case 4: & object.
- //
- bool already_fixed = true;
- Unary u = initializer as Unary;
- if (u != null && u.Oper == Unary.Operator.AddressOf) {
- IVariableReference vr = u.Expr as IVariableReference;
- if (vr == null || !vr.IsFixed) {
- already_fixed = false;
- }
- }
- if (already_fixed) {
- bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
- }
- initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
- return new ExpressionEmitter (initializer, li);
- }
- }
- VariableDeclaration decl;
- Statement statement;
- bool has_ret;
- public Fixed (VariableDeclaration decl, Statement stmt, Location l)
- {
- this.decl = decl;
- statement = stmt;
- loc = l;
- }
- #region Properties
- public Statement Statement {
- get {
- return statement;
- }
- }
- public BlockVariableDeclaration Variables {
- get {
- return decl;
- }
- }
- #endregion
- public override bool Resolve (BlockContext ec)
- {
- using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
- if (!decl.Resolve (ec))
- return false;
- }
- ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
- bool ok = statement.Resolve (ec);
- bool flow_unreachable = ec.EndFlowBranching ();
- has_ret = flow_unreachable;
- return ok;
- }
-
- protected override void DoEmit (EmitContext ec)
- {
- decl.Variable.CreateBuilder (ec);
- decl.Initializer.Emit (ec);
- if (decl.Declarators != null) {
- foreach (var d in decl.Declarators) {
- d.Variable.CreateBuilder (ec);
- d.Initializer.Emit (ec);
- }
- }
- statement.Emit (ec);
- if (has_ret)
- return;
- //
- // Clear the pinned variable
- //
- ((Emitter) decl.Initializer).EmitExit (ec);
- if (decl.Declarators != null) {
- foreach (var d in decl.Declarators) {
- ((Emitter)d.Initializer).EmitExit (ec);
- }
- }
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Fixed target = (Fixed) t;
- target.decl = (VariableDeclaration) decl.Clone (clonectx);
- target.statement = statement.Clone (clonectx);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class Catch : Statement
- {
- Block block;
- LocalVariable li;
- FullNamedExpression type_expr;
- CompilerAssign assign;
- TypeSpec type;
-
- public Catch (Block block, Location loc)
- {
- this.block = block;
- this.loc = loc;
- }
- #region Properties
- public Block Block {
- get {
- return block;
- }
- }
- public TypeSpec CatchType {
- get {
- return type;
- }
- }
- public bool IsGeneral {
- get {
- return type_expr == null;
- }
- }
- public FullNamedExpression TypeExpression {
- get {
- return type_expr;
- }
- set {
- type_expr = value;
- }
- }
- public LocalVariable Variable {
- get {
- return li;
- }
- set {
- li = value;
- }
- }
- #endregion
- protected override void DoEmit (EmitContext ec)
- {
- if (IsGeneral)
- ec.BeginCatchBlock (ec.BuiltinTypes.Object);
- else
- ec.BeginCatchBlock (CatchType);
- if (li != null) {
- li.CreateBuilder (ec);
- //
- // Special case hoisted catch variable, we have to use a temporary variable
- // to pass via anonymous storey initialization with the value still on top
- // of the stack
- //
- if (li.HoistedVariant != null) {
- LocalTemporary lt = new LocalTemporary (li.Type);
- lt.Store (ec);
- // switch to assigning from the temporary variable and not from top of the stack
- assign.UpdateSource (lt);
- }
- } else {
- ec.Emit (OpCodes.Pop);
- }
- Block.Emit (ec);
- }
- public override bool Resolve (BlockContext ec)
- {
- using (ec.With (ResolveContext.Options.CatchScope, true)) {
- if (type_expr != null) {
- type = type_expr.ResolveAsType (ec);
- if (type == null)
- return false;
- if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
- ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
- } else if (li != null) {
- li.Type = type;
- li.PrepareForFlowAnalysis (ec);
- // source variable is at the top of the stack
- Expression source = new EmptyExpression (li.Type);
- if (li.Type.IsGenericParameter)
- source = new UnboxCast (source, li.Type);
- //
- // Uses Location.Null to hide from symbol file
- //
- assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
- Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
- }
- }
- return Block.Resolve (ec);
- }
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Catch target = (Catch) t;
- if (type_expr != null)
- target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
- target.block = clonectx.LookupBlock (block);
- }
- }
- public class TryFinally : TryFinallyBlock
- {
- Block fini;
- public TryFinally (Statement stmt, Block fini, Location loc)
- : base (stmt, loc)
- {
- this.fini = fini;
- }
- public Block Finallyblock {
- get {
- return fini;
- }
- }
- public override bool Resolve (BlockContext ec)
- {
- bool ok = true;
- ec.StartFlowBranching (this);
- if (!stmt.Resolve (ec))
- ok = false;
- if (ok)
- ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
- using (ec.With (ResolveContext.Options.FinallyScope, true)) {
- if (!fini.Resolve (ec))
- ok = false;
- }
- ec.EndFlowBranching ();
- ok &= base.Resolve (ec);
- return ok;
- }
- protected override void EmitTryBody (EmitContext ec)
- {
- stmt.Emit (ec);
- }
- public override void EmitFinallyBody (EmitContext ec)
- {
- fini.Emit (ec);
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- TryFinally target = (TryFinally) t;
- target.stmt = (Statement) stmt.Clone (clonectx);
- if (fini != null)
- target.fini = clonectx.LookupBlock (fini);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class TryCatch : ExceptionStatement
- {
- public Block Block;
- List<Catch> clauses;
- readonly bool inside_try_finally;
- public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
- : base (l)
- {
- this.Block = block;
- this.clauses = catch_clauses;
- this.inside_try_finally = inside_try_finally;
- }
- public List<Catch> Clauses {
- get {
- return clauses;
- }
- }
- public bool IsTryCatchFinally {
- get {
- return inside_try_finally;
- }
- }
- public override bool Resolve (BlockContext ec)
- {
- bool ok = true;
- ec.StartFlowBranching (this);
- if (!Block.Resolve (ec))
- ok = false;
- for (int i = 0; i < clauses.Count; ++i) {
- var c = clauses[i];
- ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
- if (!c.Resolve (ec)) {
- ok = false;
- continue;
- }
- TypeSpec resolved_type = c.CatchType;
- for (int ii = 0; ii < clauses.Count; ++ii) {
- if (ii == i)
- continue;
- if (clauses[ii].IsGeneral) {
- if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
- continue;
- if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
- continue;
- if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
- continue;
- ec.Report.Warning (1058, 1, c.loc,
- "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
- continue;
- }
- if (ii >= i)
- continue;
- var ct = clauses[ii].CatchType;
- if (ct == null)
- continue;
- if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
- ec.Report.Error (160, c.loc,
- "A previous catch clause already catches all exceptions of this or a super type `{0}'",
- ct.GetSignatureForError ());
- ok = false;
- }
- }
- }
- ec.EndFlowBranching ();
- return base.Resolve (ec) && ok;
- }
- protected sealed override void DoEmit (EmitContext ec)
- {
- if (!inside_try_finally)
- EmitTryBodyPrepare (ec);
- Block.Emit (ec);
- foreach (Catch c in clauses)
- c.Emit (ec);
- if (!inside_try_finally)
- ec.EndExceptionBlock ();
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- TryCatch target = (TryCatch) t;
- target.Block = clonectx.LookupBlock (Block);
- if (clauses != null){
- target.clauses = new List<Catch> ();
- foreach (Catch c in clauses)
- target.clauses.Add ((Catch) c.Clone (clonectx));
- }
- }
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- public class Using : TryFinallyBlock
- {
- public class VariableDeclaration : BlockVariableDeclaration
- {
- Statement dispose_call;
- public VariableDeclaration (FullNamedExpression type, LocalVariable li)
- : base (type, li)
- {
- }
- public VariableDeclaration (LocalVariable li, Location loc)
- : base (li)
- {
- this.loc = loc;
- }
- public VariableDeclaration (Expression expr)
- : base (null)
- {
- loc = expr.Location;
- Initializer = expr;
- }
- #region Properties
- public bool IsNested { get; private set; }
- #endregion
- public void EmitDispose (EmitContext ec)
- {
- dispose_call.Emit (ec);
- }
- public override bool Resolve (BlockContext bc)
- {
- if (IsNested)
- return true;
- return base.Resolve (bc, false);
- }
- public Expression ResolveExpression (BlockContext bc)
- {
- var e = Initializer.Resolve (bc);
- if (e == null)
- return null;
- li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
- Initializer = ResolveInitializer (bc, Variable, e);
- return e;
- }
- protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
- {
- if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
- initializer = initializer.Resolve (bc);
- if (initializer == null)
- return null;
- // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
- Arguments args = new Arguments (1);
- args.Add (new Argument (initializer));
- initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
- if (initializer == null)
- return null;
- var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
- dispose_call = CreateDisposeCall (bc, var);
- dispose_call.Resolve (bc);
- return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
- }
- if (li == Variable) {
- CheckIDiposableConversion (bc, li, initializer);
- dispose_call = CreateDisposeCall (bc, li);
- dispose_call.Resolve (bc);
- }
- return base.ResolveInitializer (bc, li, initializer);
- }
- protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
- {
- var type = li.Type;
- if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
- if (type.IsNullableType) {
- // it's handled in CreateDisposeCall
- return;
- }
- bc.Report.SymbolRelatedToPreviousError (type);
- var loc = type_expr == null ? initializer.Location : type_expr.Location;
- bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
- type.GetSignatureForError ());
- return;
- }
- }
- protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
- {
- var lvr = lv.CreateReferenceExpression (bc, lv.Location);
- var type = lv.Type;
- var loc = lv.Location;
- var idt = bc.BuiltinTypes.IDisposable;
- var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
- var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
- dispose_mg.InstanceExpression = type.IsNullableType ?
- new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
- lvr;
- //
- // Hide it from symbol file via null location
- //
- Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
- // Add conditional call when disposing possible null variable
- if (!type.IsStruct || type.IsNullableType)
- dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
- return dispose;
- }
- public void ResolveDeclaratorInitializer (BlockContext bc)
- {
- Initializer = base.ResolveInitializer (bc, Variable, Initializer);
- }
- public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
- {
- for (int i = declarators.Count - 1; i >= 0; --i) {
- var d = declarators [i];
- var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
- vd.Initializer = d.Initializer;
- vd.IsNested = true;
- vd.dispose_call = CreateDisposeCall (bc, d.Variable);
- vd.dispose_call.Resolve (bc);
- stmt = new Using (vd, stmt, d.Variable.Location);
- }
- declarators = null;
- return stmt;
- }
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- VariableDeclaration decl;
- public Using (VariableDeclaration decl, Statement stmt, Location loc)
- : base (stmt, loc)
- {
- this.decl = decl;
- }
- public Using (Expression expr, Statement stmt, Location loc)
- : base (stmt, loc)
- {
- this.decl = new VariableDeclaration (expr);
- }
- #region Properties
- public Expression Expr {
- get {
- return decl.Variable == null ? decl.Initializer : null;
- }
- }
- public BlockVariableDeclaration Variables {
- get {
- return decl;
- }
- }
- #endregion
- public override void Emit (EmitContext ec)
- {
- //
- // Don't emit sequence point it will be set on variable declaration
- //
- DoEmit (ec);
- }
- protected override void EmitTryBodyPrepare (EmitContext ec)
- {
- decl.Emit (ec);
- base.EmitTryBodyPrepare (ec);
- }
- protected override void EmitTryBody (EmitContext ec)
- {
- stmt.Emit (ec);
- }
- public override void EmitFinallyBody (EmitContext ec)
- {
- decl.EmitDispose (ec);
- }
- public override bool Resolve (BlockContext ec)
- {
- VariableReference vr;
- bool vr_locked = false;
- using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
- if (decl.Variable == null) {
- vr = decl.ResolveExpression (ec) as VariableReference;
- if (vr != null) {
- vr_locked = vr.IsLockedByStatement;
- vr.IsLockedByStatement = true;
- }
- } else {
- if (decl.IsNested) {
- decl.ResolveDeclaratorInitializer (ec);
- } else {
- if (!decl.Resolve (ec))
- return false;
- if (decl.Declarators != null) {
- stmt = decl.RewriteUsingDeclarators (ec, stmt);
- }
- }
- vr = null;
- }
- }
- ec.StartFlowBranching (this);
- stmt.Resolve (ec);
- ec.EndFlowBranching ();
- if (vr != null)
- vr.IsLockedByStatement = vr_locked;
- base.Resolve (ec);
- return true;
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Using target = (Using) t;
- target.decl = (VariableDeclaration) decl.Clone (clonectx);
- target.stmt = stmt.Clone (clonectx);
- }
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- /// <summary>
- /// Implementation of the foreach C# statement
- /// </summary>
- public class Foreach : Statement
- {
- abstract class IteratorStatement : Statement
- {
- protected readonly Foreach for_each;
- protected IteratorStatement (Foreach @foreach)
- {
- this.for_each = @foreach;
- this.loc = @foreach.expr.Location;
- }
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- throw new NotImplementedException ();
- }
- public override void Emit (EmitContext ec)
- {
- if (ec.EmitAccurateDebugInfo) {
- ec.Emit (OpCodes.Nop);
- }
- base.Emit (ec);
- }
- }
- sealed class ArrayForeach : IteratorStatement
- {
- TemporaryVariableReference[] lengths;
- Expression [] length_exprs;
- StatementExpression[] counter;
- TemporaryVariableReference[] variables;
- TemporaryVariableReference copy;
- public ArrayForeach (Foreach @foreach, int rank)
- : base (@foreach)
- {
- counter = new StatementExpression[rank];
- variables = new TemporaryVariableReference[rank];
- length_exprs = new Expression [rank];
- //
- // Only use temporary length variables when dealing with
- // multi-dimensional arrays
- //
- if (rank > 1)
- lengths = new TemporaryVariableReference [rank];
- }
- public override bool Resolve (BlockContext ec)
- {
- Block variables_block = for_each.variable.Block;
- copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
- copy.Resolve (ec);
- int rank = length_exprs.Length;
- Arguments list = new Arguments (rank);
- for (int i = 0; i < rank; i++) {
- var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
- variables[i] = v;
- counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
- counter[i].Resolve (ec);
- if (rank == 1) {
- length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
- } else {
- lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
- lengths[i].Resolve (ec);
- Arguments args = new Arguments (1);
- args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
- length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
- }
- list.Add (new Argument (v));
- }
- var access = new ElementAccess (copy, list, loc).Resolve (ec);
- if (access == null)
- return false;
- TypeSpec var_type;
- if (for_each.type is VarExpr) {
- // Infer implicitly typed local variable from foreach array type
- var_type = access.Type;
- } else {
- var_type = for_each.type.ResolveAsType (ec);
- if (var_type == null)
- return false;
- access = Convert.ExplicitConversion (ec, access, var_type, loc);
- if (access == null)
- return false;
- }
- for_each.variable.Type = var_type;
- var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
- if (variable_ref == null)
- return false;
- for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
- bool ok = true;
- ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
- ec.CurrentBranching.CreateSibling ();
- ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
- if (!for_each.body.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)
- {
- copy.EmitAssign (ec, for_each.expr);
- int rank = length_exprs.Length;
- Label[] test = new Label [rank];
- Label[] loop = new Label [rank];
- for (int i = 0; i < rank; i++) {
- test [i] = ec.DefineLabel ();
- loop [i] = ec.DefineLabel ();
- if (lengths != null)
- lengths [i].EmitAssign (ec, length_exprs [i]);
- }
- IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
- for (int i = 0; i < rank; i++) {
- variables [i].EmitAssign (ec, zero);
- ec.Emit (OpCodes.Br, test [i]);
- ec.MarkLabel (loop [i]);
- }
- for_each.body.Emit (ec);
- ec.MarkLabel (ec.LoopBegin);
- ec.Mark (for_each.expr.Location);
- for (int i = rank - 1; i >= 0; i--){
- counter [i].Emit (ec);
- ec.MarkLabel (test [i]);
- variables [i].Emit (ec);
- if (lengths != null)
- lengths [i].Emit (ec);
- else
- length_exprs [i].Emit (ec);
- ec.Emit (OpCodes.Blt, loop [i]);
- }
- ec.MarkLabel (ec.LoopEnd);
- }
- }
- sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
- {
- class RuntimeDispose : Using.VariableDeclaration
- {
- public RuntimeDispose (LocalVariable lv, Location loc)
- : base (lv, loc)
- {
- }
- protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
- {
- // Defered to runtime check
- }
- protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
- {
- var idt = bc.BuiltinTypes.IDisposable;
- //
- // Fabricates code like
- //
- // if ((temp = vr as IDisposable) != null) temp.Dispose ();
- //
- var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
- var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
- dispose_variable.CreateReferenceExpression (bc, loc),
- new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
- loc), new NullLiteral (loc));
- var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
- var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
- dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
- Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
- return new If (idisaposable_test, dispose, loc);
- }
- }
- LocalVariable variable;
- Expression expr;
- Statement statement;
- ExpressionStatement init;
- TemporaryVariableReference enumerator_variable;
- bool ambiguous_getenumerator_name;
- public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
- : base (@foreach)
- {
- this.variable = var;
- this.expr = expr;
- }
- void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
- {
- rc.Report.SymbolRelatedToPreviousError (enumerator);
- rc.Report.Error (202, loc,
- "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
- enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
- }
- MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
- {
- //
- // Option 1: Try to match by name GetEnumerator first
- //
- var mexpr = Expression.MemberLookup (rc, false, expr.Type,
- "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
- var mg = mexpr as MethodGroupExpr;
- if (mg != null) {
- mg.InstanceExpression = expr;
- Arguments args = new Arguments (0);
- mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
- // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
- if (ambiguous_getenumerator_name)
- mg = null;
- if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
- return mg;
- }
- }
- //
- // Option 2: Try to match using IEnumerable interfaces with preference of generic version
- //
- var t = expr.Type;
- PredefinedMember<MethodSpec> iface_candidate = null;
- var ptypes = rc.Module.PredefinedTypes;
- var gen_ienumerable = ptypes.IEnumerableGeneric;
- if (!gen_ienumerable.Define ())
- gen_ienumerable = null;
- var ifaces = t.Interfaces;
- if (ifaces != null) {
- foreach (var iface in ifaces) {
- if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
- if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
- rc.Report.SymbolRelatedToPreviousError (expr.Type);
- rc.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",
- expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
- return null;
- }
- // TODO: Cache this somehow
- iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
- MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
- continue;
- }
- if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
- iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
- }
- }
- }
- if (iface_candidate == null) {
- if (expr.Type != InternalType.ErrorType) {
- rc.Report.Error (1579, loc,
- "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
- expr.Type.GetSignatureForError (), "GetEnumerator");
- }
- return null;
- }
- var method = iface_candidate.Resolve (loc);
- if (method == null)
- return null;
- mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
- mg.InstanceExpression = expr;
- return mg;
- }
- MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
- {
- var ms = MemberCache.FindMember (enumerator.ReturnType,
- MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
- BindingRestriction.InstanceOnly) as MethodSpec;
- if (ms == null || !ms.IsPublic) {
- Error_WrongEnumerator (rc, enumerator);
- return null;
- }
- return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
- }
- PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
- {
- var ps = MemberCache.FindMember (enumerator.ReturnType,
- MemberFilter.Property ("Current", null),
- BindingRestriction.InstanceOnly) as PropertySpec;
- if (ps == null || !ps.IsPublic) {
- Error_WrongEnumerator (rc, enumerator);
- return null;
- }
- return ps;
- }
- public override bool Resolve (BlockContext ec)
- {
- bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
- if (is_dynamic) {
- expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
- } else if (expr.Type.IsNullableType) {
- expr = new Nullable.UnwrapCall (expr).Resolve (ec);
- }
- var get_enumerator_mg = ResolveGetEnumerator (ec);
- if (get_enumerator_mg == null) {
- return false;
- }
- var get_enumerator = get_enumerator_mg.BestCandidate;
- enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
- enumerator_variable.Resolve (ec);
- // Prepare bool MoveNext ()
- var move_next_mg = ResolveMoveNext (ec, get_enumerator);
- if (move_next_mg == null) {
- return false;
- }
- move_next_mg.InstanceExpression = enumerator_variable;
- // Prepare ~T~ Current { get; }
- var current_prop = ResolveCurrent (ec, get_enumerator);
- if (current_prop == null) {
- return false;
- }
- var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
- if (current_pe == null)
- return false;
- VarExpr ve = for_each.type as VarExpr;
- if (ve != null) {
- if (is_dynamic) {
- // Source type is dynamic, set element type to dynamic too
- variable.Type = ec.BuiltinTypes.Dynamic;
- } else {
- // Infer implicitly typed local variable from foreach enumerable type
- variable.Type = current_pe.Type;
- }
- } else {
- if (is_dynamic) {
- // Explicit cast of dynamic collection elements has to be done at runtime
- current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
- }
- variable.Type = for_each.type.ResolveAsType (ec);
- if (variable.Type == null)
- return false;
- current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
- if (current_pe == null)
- return false;
- }
- var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
- if (variable_ref == null)
- return false;
- for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
- var init = new Invocation (get_enumerator_mg, null);
- statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
- for_each.body, Location.Null);
- var enum_type = enumerator_variable.Type;
- //
- // Add Dispose method call when enumerator can be IDisposable
- //
- if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
- if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
- //
- // Runtime Dispose check
- //
- var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
- vd.Initializer = init;
- statement = new Using (vd, statement, Location.Null);
- } else {
- //
- // No Dispose call needed
- //
- this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
- this.init.Resolve (ec);
- }
- } else {
- //
- // Static Dispose check
- //
- var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
- vd.Initializer = init;
- statement = new Using (vd, statement, Location.Null);
- }
- return statement.Resolve (ec);
- }
- protected override void DoEmit (EmitContext ec)
- {
- enumerator_variable.LocalInfo.CreateBuilder (ec);
- if (init != null)
- init.EmitStatement (ec);
- statement.Emit (ec);
- }
- #region IErrorHandler Members
- bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
- {
- ec.Report.SymbolRelatedToPreviousError (best);
- ec.Report.Warning (278, 2, expr.Location,
- "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
- expr.Type.GetSignatureForError (), "enumerable",
- best.GetSignatureForError (), ambiguous.GetSignatureForError ());
- ambiguous_getenumerator_name = true;
- return true;
- }
- bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
- {
- return false;
- }
- bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
- {
- return false;
- }
- bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
- {
- return false;
- }
- #endregion
- }
- Expression type;
- LocalVariable variable;
- Expression expr;
- Statement statement;
- Block body;
- public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
- {
- this.type = type;
- this.variable = var;
- this.expr = expr;
- this.statement = stmt;
- this.body = body;
- loc = l;
- }
- public Expression Expr {
- get { return expr; }
- }
- public Statement Statement {
- get { return statement; }
- }
- public Expression TypeExpression {
- get { return type; }
- }
- public LocalVariable Variable {
- get { return variable; }
- }
- public override bool Resolve (BlockContext ec)
- {
- expr = expr.Resolve (ec);
- if (expr == null)
- return false;
- if (expr.IsNull) {
- ec.Report.Error (186, loc, "Use of null is not valid in this context");
- return false;
- }
- body.AddStatement (statement);
- if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
- statement = new ArrayForeach (this, 1);
- } else if (expr.Type is ArrayContainer) {
- statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
- } else {
- if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
- ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
- expr.ExprClassName);
- return false;
- }
- statement = new CollectionForeach (this, variable, expr);
- }
- return statement.Resolve (ec);
- }
- protected override void DoEmit (EmitContext ec)
- {
- variable.CreateBuilder (ec);
- Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
- ec.LoopBegin = ec.DefineLabel ();
- ec.LoopEnd = ec.DefineLabel ();
- statement.Emit (ec);
- ec.LoopBegin = old_begin;
- ec.LoopEnd = old_end;
- }
- protected override void CloneTo (CloneContext clonectx, Statement t)
- {
- Foreach target = (Foreach) t;
- target.type = type.Clone (clonectx);
- target.expr = expr.Clone (clonectx);
- target.body = (Block) body.Clone (clonectx);
- target.statement = statement.Clone (clonectx);
- }
-
- public override object Accept (StructuralVisitor visitor)
- {
- return visitor.Visit (this);
- }
- }
- }