2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
53 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
55 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
56 bool ok = Resolve (ec);
57 ec.KillFlowBranching ();
63 /// Return value indicates whether all code paths emitted return.
65 protected abstract void DoEmit (EmitContext ec);
67 public virtual void Emit (EmitContext ec)
72 if (ec.StatementEpilogue != null) {
78 // This routine must be overrided in derived classes and make copies
79 // of all the data that might be modified if resolved
81 protected abstract void CloneTo (CloneContext clonectx, Statement target);
83 public Statement Clone (CloneContext clonectx)
85 Statement s = (Statement) this.MemberwiseClone ();
86 CloneTo (clonectx, s);
90 public virtual Expression CreateExpressionTree (ResolveContext ec)
92 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
96 public virtual object Accept (StructuralVisitor visitor)
98 return visitor.Visit (this);
102 public sealed class EmptyStatement : Statement
104 public EmptyStatement (Location loc)
109 public override bool Resolve (BlockContext ec)
114 public override bool ResolveUnreachable (BlockContext ec, bool warn)
119 public override void Emit (EmitContext ec)
123 protected override void DoEmit (EmitContext ec)
125 throw new NotSupportedException ();
128 protected override void CloneTo (CloneContext clonectx, Statement target)
133 public override object Accept (StructuralVisitor visitor)
135 return visitor.Visit (this);
139 public class If : Statement {
141 public Statement TrueStatement;
142 public Statement FalseStatement;
146 public If (Expression bool_expr, Statement true_statement, Location l)
147 : this (bool_expr, true_statement, null, l)
151 public If (Expression bool_expr,
152 Statement true_statement,
153 Statement false_statement,
156 this.expr = bool_expr;
157 TrueStatement = true_statement;
158 FalseStatement = false_statement;
162 public Expression Expr {
168 public override bool Resolve (BlockContext ec)
172 expr = expr.Resolve (ec);
177 // Dead code elimination
179 if (expr is Constant) {
180 bool take = !((Constant) expr).IsDefaultValue;
183 if (!TrueStatement.Resolve (ec))
186 if ((FalseStatement != null) &&
187 !FalseStatement.ResolveUnreachable (ec, true))
189 FalseStatement = null;
191 if (!TrueStatement.ResolveUnreachable (ec, true))
193 TrueStatement = null;
195 if ((FalseStatement != null) &&
196 !FalseStatement.Resolve (ec))
204 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
206 ok &= TrueStatement.Resolve (ec);
208 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
210 ec.CurrentBranching.CreateSibling ();
212 if (FalseStatement != null)
213 ok &= FalseStatement.Resolve (ec);
215 ec.EndFlowBranching ();
220 protected override void DoEmit (EmitContext ec)
222 Label false_target = ec.DefineLabel ();
226 // If we're a boolean constant, Resolve() already
227 // eliminated dead code for us.
229 Constant c = expr as Constant;
231 c.EmitSideEffect (ec);
233 if (!c.IsDefaultValue)
234 TrueStatement.Emit (ec);
235 else if (FalseStatement != null)
236 FalseStatement.Emit (ec);
241 expr.EmitBranchable (ec, false_target, false);
243 TrueStatement.Emit (ec);
245 if (FalseStatement != null){
246 bool branch_emitted = false;
248 end = ec.DefineLabel ();
250 ec.Emit (OpCodes.Br, end);
251 branch_emitted = true;
254 ec.MarkLabel (false_target);
255 FalseStatement.Emit (ec);
260 ec.MarkLabel (false_target);
264 protected override void CloneTo (CloneContext clonectx, Statement t)
268 target.expr = expr.Clone (clonectx);
269 target.TrueStatement = TrueStatement.Clone (clonectx);
270 if (FalseStatement != null)
271 target.FalseStatement = FalseStatement.Clone (clonectx);
274 public override object Accept (StructuralVisitor visitor)
276 return visitor.Visit (this);
280 public class Do : Statement {
281 public Expression expr;
282 public Statement EmbeddedStatement;
284 public Do (Statement statement, BooleanExpression bool_expr, Location l)
287 EmbeddedStatement = statement;
291 public override bool Resolve (BlockContext ec)
295 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
297 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
299 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
300 if (!EmbeddedStatement.Resolve (ec))
302 ec.EndFlowBranching ();
304 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
305 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
307 expr = expr.Resolve (ec);
310 else if (expr is Constant){
311 bool infinite = !((Constant) expr).IsDefaultValue;
313 ec.CurrentBranching.CurrentUsageVector.Goto ();
316 ec.EndFlowBranching ();
321 protected override void DoEmit (EmitContext ec)
323 Label loop = ec.DefineLabel ();
324 Label old_begin = ec.LoopBegin;
325 Label old_end = ec.LoopEnd;
327 ec.LoopBegin = ec.DefineLabel ();
328 ec.LoopEnd = ec.DefineLabel ();
331 EmbeddedStatement.Emit (ec);
332 ec.MarkLabel (ec.LoopBegin);
334 // Mark start of while condition
335 ec.Mark (expr.Location);
338 // Dead code elimination
340 if (expr is Constant) {
341 bool res = !((Constant) expr).IsDefaultValue;
343 expr.EmitSideEffect (ec);
345 ec.Emit (OpCodes.Br, loop);
347 expr.EmitBranchable (ec, loop, true);
350 ec.MarkLabel (ec.LoopEnd);
352 ec.LoopBegin = old_begin;
353 ec.LoopEnd = old_end;
356 protected override void CloneTo (CloneContext clonectx, Statement t)
360 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
361 target.expr = expr.Clone (clonectx);
364 public override object Accept (StructuralVisitor visitor)
366 return visitor.Visit (this);
370 public class While : Statement {
371 public Expression expr;
372 public Statement Statement;
373 bool infinite, empty;
375 public While (BooleanExpression bool_expr, Statement statement, Location l)
377 this.expr = bool_expr;
378 Statement = statement;
382 public override bool Resolve (BlockContext ec)
386 expr = expr.Resolve (ec);
391 // Inform whether we are infinite or not
393 if (expr is Constant){
394 bool value = !((Constant) expr).IsDefaultValue;
397 if (!Statement.ResolveUnreachable (ec, true))
405 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
407 ec.CurrentBranching.CreateSibling ();
409 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
410 if (!Statement.Resolve (ec))
412 ec.EndFlowBranching ();
414 // There's no direct control flow from the end of the embedded statement to the end of the loop
415 ec.CurrentBranching.CurrentUsageVector.Goto ();
417 ec.EndFlowBranching ();
422 protected override void DoEmit (EmitContext ec)
425 expr.EmitSideEffect (ec);
429 Label old_begin = ec.LoopBegin;
430 Label old_end = ec.LoopEnd;
432 ec.LoopBegin = ec.DefineLabel ();
433 ec.LoopEnd = ec.DefineLabel ();
436 // Inform whether we are infinite or not
438 if (expr is Constant) {
439 // expr is 'true', since the 'empty' case above handles the 'false' case
440 ec.MarkLabel (ec.LoopBegin);
442 if (ec.EmitAccurateDebugInfo)
443 ec.Emit (OpCodes.Nop);
445 expr.EmitSideEffect (ec);
447 ec.Emit (OpCodes.Br, ec.LoopBegin);
450 // Inform that we are infinite (ie, `we return'), only
451 // if we do not `break' inside the code.
453 ec.MarkLabel (ec.LoopEnd);
455 Label while_loop = ec.DefineLabel ();
457 ec.Emit (OpCodes.Br, ec.LoopBegin);
458 ec.MarkLabel (while_loop);
462 ec.MarkLabel (ec.LoopBegin);
464 ec.Mark (expr.Location);
465 expr.EmitBranchable (ec, while_loop, true);
467 ec.MarkLabel (ec.LoopEnd);
470 ec.LoopBegin = old_begin;
471 ec.LoopEnd = old_end;
474 protected override void CloneTo (CloneContext clonectx, Statement t)
476 While target = (While) t;
478 target.expr = expr.Clone (clonectx);
479 target.Statement = Statement.Clone (clonectx);
482 public override object Accept (StructuralVisitor visitor)
484 return visitor.Visit (this);
488 public class For : Statement
490 bool infinite, empty;
492 public For (Location l)
497 public Statement Initializer {
501 public Expression Condition {
505 public Statement Iterator {
509 public Statement Statement {
513 public override bool Resolve (BlockContext ec)
517 if (Initializer != null) {
518 if (!Initializer.Resolve (ec))
522 if (Condition != null) {
523 Condition = Condition.Resolve (ec);
524 if (Condition == null)
526 else if (Condition is Constant) {
527 bool value = !((Constant) Condition).IsDefaultValue;
530 if (!Statement.ResolveUnreachable (ec, true))
532 if ((Iterator != null) &&
533 !Iterator.ResolveUnreachable (ec, false))
543 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
545 ec.CurrentBranching.CreateSibling ();
547 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
549 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
550 if (!Statement.Resolve (ec))
552 ec.EndFlowBranching ();
554 if (Iterator != null){
555 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
556 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
559 if (!Iterator.Resolve (ec))
564 // There's no direct control flow from the end of the embedded statement to the end of the loop
565 ec.CurrentBranching.CurrentUsageVector.Goto ();
567 ec.EndFlowBranching ();
572 protected override void DoEmit (EmitContext ec)
574 if (Initializer != null)
575 Initializer.Emit (ec);
578 Condition.EmitSideEffect (ec);
582 Label old_begin = ec.LoopBegin;
583 Label old_end = ec.LoopEnd;
584 Label loop = ec.DefineLabel ();
585 Label test = ec.DefineLabel ();
587 ec.LoopBegin = ec.DefineLabel ();
588 ec.LoopEnd = ec.DefineLabel ();
590 ec.Emit (OpCodes.Br, test);
594 ec.MarkLabel (ec.LoopBegin);
599 // If test is null, there is no test, and we are just
602 if (Condition != null) {
603 ec.Mark (Condition.Location);
606 // The Resolve code already catches the case for
607 // Test == Constant (false) so we know that
610 if (Condition is Constant) {
611 Condition.EmitSideEffect (ec);
612 ec.Emit (OpCodes.Br, loop);
614 Condition.EmitBranchable (ec, loop, true);
618 ec.Emit (OpCodes.Br, loop);
619 ec.MarkLabel (ec.LoopEnd);
621 ec.LoopBegin = old_begin;
622 ec.LoopEnd = old_end;
625 protected override void CloneTo (CloneContext clonectx, Statement t)
627 For target = (For) t;
629 if (Initializer != null)
630 target.Initializer = Initializer.Clone (clonectx);
631 if (Condition != null)
632 target.Condition = Condition.Clone (clonectx);
633 if (Iterator != null)
634 target.Iterator = Iterator.Clone (clonectx);
635 target.Statement = Statement.Clone (clonectx);
638 public override object Accept (StructuralVisitor visitor)
640 return visitor.Visit (this);
644 public class StatementExpression : Statement
646 ExpressionStatement expr;
648 public StatementExpression (ExpressionStatement expr)
654 public StatementExpression (ExpressionStatement expr, Location loc)
660 public ExpressionStatement Expr {
666 protected override void CloneTo (CloneContext clonectx, Statement t)
668 StatementExpression target = (StatementExpression) t;
669 target.expr = (ExpressionStatement) expr.Clone (clonectx);
672 protected override void DoEmit (EmitContext ec)
674 expr.EmitStatement (ec);
677 public override bool Resolve (BlockContext ec)
679 expr = expr.ResolveStatement (ec);
683 public override object Accept (StructuralVisitor visitor)
685 return visitor.Visit (this);
689 public class StatementErrorExpression : Statement
691 readonly Expression expr;
693 public StatementErrorExpression (Expression expr)
698 public Expression Expr {
704 protected override void DoEmit (EmitContext ec)
706 throw new NotSupportedException ();
709 protected override void CloneTo (CloneContext clonectx, Statement target)
711 throw new NotImplementedException ();
714 public override object Accept (StructuralVisitor visitor)
716 return visitor.Visit (this);
721 // Simple version of statement list not requiring a block
723 public class StatementList : Statement
725 List<Statement> statements;
727 public StatementList (Statement first, Statement second)
729 statements = new List<Statement> () { first, second };
733 public IList<Statement> Statements {
740 public void Add (Statement statement)
742 statements.Add (statement);
745 public override bool Resolve (BlockContext ec)
747 foreach (var s in statements)
753 protected override void DoEmit (EmitContext ec)
755 foreach (var s in statements)
759 protected override void CloneTo (CloneContext clonectx, Statement target)
761 StatementList t = (StatementList) target;
763 t.statements = new List<Statement> (statements.Count);
764 foreach (Statement s in statements)
765 t.statements.Add (s.Clone (clonectx));
768 public override object Accept (StructuralVisitor visitor)
770 return visitor.Visit (this);
774 // A 'return' or a 'yield break'
775 public abstract class ExitStatement : Statement
777 protected bool unwind_protect;
778 protected abstract bool DoResolve (BlockContext ec);
780 public virtual void Error_FinallyClause (Report Report)
782 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
785 public sealed override bool Resolve (BlockContext ec)
787 var res = DoResolve (ec);
788 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
789 ec.CurrentBranching.CurrentUsageVector.Goto ();
795 /// Implements the return statement
797 public class Return : ExitStatement
801 public Return (Expression expr, Location l)
809 public Expression Expr {
820 protected override bool DoResolve (BlockContext ec)
823 if (ec.ReturnType.Kind == MemberKind.Void)
827 // Return must not be followed by an expression when
828 // the method return type is Task
830 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
831 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
832 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
834 // Extra trick not to emit ret/leave inside awaiter body
836 expr = EmptyExpression.Null;
841 if (ec.CurrentIterator != null) {
842 Error_ReturnFromIterator (ec);
844 ec.Report.Error (126, loc,
845 "An object of a type convertible to `{0}' is required for the return statement",
846 ec.ReturnType.GetSignatureForError ());
852 expr = expr.Resolve (ec);
853 TypeSpec block_return_type = ec.ReturnType;
855 AnonymousExpression am = ec.CurrentAnonymousMethod;
857 if (block_return_type.Kind == MemberKind.Void) {
858 ec.Report.Error (127, loc,
859 "`{0}': A return keyword must not be followed by any expression when method returns void",
860 ec.GetSignatureForError ());
866 Error_ReturnFromIterator (ec);
870 var async_block = am as AsyncInitializer;
871 if (async_block != null) {
873 var storey = (AsyncTaskStorey) am.Storey;
874 var async_type = storey.ReturnType;
876 if (async_type == null && async_block.ReturnTypeInference != null) {
877 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
881 // TODO: Better error message
882 if (async_type.Kind == MemberKind.Void) {
883 ec.Report.Error (127, loc,
884 "`{0}': A return keyword must not be followed by any expression when method returns void",
885 ec.GetSignatureForError ());
890 if (!async_type.IsGenericTask) {
891 if (this is ContextualReturn)
894 ec.Report.Error (1997, loc,
895 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
896 ec.GetSignatureForError ());
901 // The return type is actually Task<T> type argument
903 if (expr.Type == async_type) {
904 ec.Report.Error (4016, loc,
905 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
906 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
908 block_return_type = async_type.TypeArguments[0];
912 var l = am as AnonymousMethodBody;
913 if (l != null && l.ReturnTypeInference != null && expr != null) {
914 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
923 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
924 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
927 if (am != null && block_return_type == ec.ReturnType) {
928 ec.Report.Error (1662, loc,
929 "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",
930 am.ContainerType, am.GetSignatureForError ());
939 protected override void DoEmit (EmitContext ec)
944 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
945 if (async_body != null) {
946 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
948 // It's null for await without async
949 if (async_return != null) {
950 async_return.EmitAssign (ec);
955 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
961 if (unwind_protect || ec.EmitAccurateDebugInfo)
962 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
965 if (unwind_protect) {
966 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
967 } else if (ec.EmitAccurateDebugInfo) {
968 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
970 ec.Emit (OpCodes.Ret);
974 void Error_ReturnFromIterator (ResolveContext rc)
976 rc.Report.Error (1622, loc,
977 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
980 protected override void CloneTo (CloneContext clonectx, Statement t)
982 Return target = (Return) t;
983 // It's null for simple return;
985 target.expr = expr.Clone (clonectx);
988 public override object Accept (StructuralVisitor visitor)
990 return visitor.Visit (this);
994 public class Goto : Statement {
996 LabeledStatement label;
999 public override bool Resolve (BlockContext ec)
1001 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1002 ec.CurrentBranching.CurrentUsageVector.Goto ();
1006 public Goto (string label, Location l)
1012 public string Target {
1013 get { return target; }
1016 public void SetResolvedTarget (LabeledStatement label)
1019 label.AddReference ();
1022 protected override void CloneTo (CloneContext clonectx, Statement target)
1027 protected override void DoEmit (EmitContext ec)
1030 throw new InternalErrorException ("goto emitted before target resolved");
1031 Label l = label.LabelTarget (ec);
1032 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1035 public override object Accept (StructuralVisitor visitor)
1037 return visitor.Visit (this);
1041 public class LabeledStatement : Statement {
1048 FlowBranching.UsageVector vectors;
1050 public LabeledStatement (string name, Block block, Location l)
1057 public Label LabelTarget (EmitContext ec)
1062 label = ec.DefineLabel ();
1067 public Block Block {
1073 public string Name {
1074 get { return name; }
1077 public bool IsDefined {
1078 get { return defined; }
1081 public bool HasBeenReferenced {
1082 get { return referenced; }
1085 public FlowBranching.UsageVector JumpOrigins {
1086 get { return vectors; }
1089 public void AddUsageVector (FlowBranching.UsageVector vector)
1091 vector = vector.Clone ();
1092 vector.Next = vectors;
1096 protected override void CloneTo (CloneContext clonectx, Statement target)
1101 public override bool Resolve (BlockContext ec)
1103 // this flow-branching will be terminated when the surrounding block ends
1104 ec.StartFlowBranching (this);
1108 protected override void DoEmit (EmitContext ec)
1110 if (!HasBeenReferenced)
1111 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1114 ec.MarkLabel (label);
1117 public void AddReference ()
1122 public override object Accept (StructuralVisitor visitor)
1124 return visitor.Visit (this);
1130 /// `goto default' statement
1132 public class GotoDefault : Statement {
1134 public GotoDefault (Location l)
1139 protected override void CloneTo (CloneContext clonectx, Statement target)
1144 public override bool Resolve (BlockContext ec)
1146 ec.CurrentBranching.CurrentUsageVector.Goto ();
1148 if (ec.Switch == null) {
1149 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1153 if (!ec.Switch.GotDefault) {
1154 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1161 protected override void DoEmit (EmitContext ec)
1163 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1166 public override object Accept (StructuralVisitor visitor)
1168 return visitor.Visit (this);
1173 /// `goto case' statement
1175 public class GotoCase : Statement {
1179 public GotoCase (Expression e, Location l)
1185 public Expression Expr {
1191 public override bool Resolve (BlockContext ec)
1193 if (ec.Switch == null){
1194 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1198 ec.CurrentBranching.CurrentUsageVector.Goto ();
1200 expr = expr.Resolve (ec);
1204 Constant c = expr as Constant;
1206 ec.Report.Error (150, expr.Location, "A constant value is expected");
1211 if (ec.Switch.IsNullable && c is NullLiteral) {
1214 TypeSpec type = ec.Switch.SwitchType;
1215 res = c.TryReduce (ec, type);
1217 c.Error_ValueCannotBeConverted (ec, type, true);
1221 if (!Convert.ImplicitStandardConversionExists (c, type))
1222 ec.Report.Warning (469, 2, loc,
1223 "The `goto case' value is not implicitly convertible to type `{0}'",
1224 TypeManager.CSharpName (type));
1228 sl = ec.Switch.ResolveGotoCase (ec, res);
1232 protected override void DoEmit (EmitContext ec)
1234 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1237 protected override void CloneTo (CloneContext clonectx, Statement t)
1239 GotoCase target = (GotoCase) t;
1241 target.expr = expr.Clone (clonectx);
1244 public override object Accept (StructuralVisitor visitor)
1246 return visitor.Visit (this);
1250 public class Throw : Statement {
1253 public Throw (Expression expr, Location l)
1259 public Expression Expr {
1265 public override bool Resolve (BlockContext ec)
1268 ec.CurrentBranching.CurrentUsageVector.Goto ();
1269 return ec.CurrentBranching.CheckRethrow (loc);
1272 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1273 ec.CurrentBranching.CurrentUsageVector.Goto ();
1278 var et = ec.BuiltinTypes.Exception;
1279 if (Convert.ImplicitConversionExists (ec, expr, et))
1280 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1282 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1287 protected override void DoEmit (EmitContext ec)
1290 ec.Emit (OpCodes.Rethrow);
1294 ec.Emit (OpCodes.Throw);
1298 protected override void CloneTo (CloneContext clonectx, Statement t)
1300 Throw target = (Throw) t;
1303 target.expr = expr.Clone (clonectx);
1306 public override object Accept (StructuralVisitor visitor)
1308 return visitor.Visit (this);
1312 public class Break : Statement {
1314 public Break (Location l)
1319 bool unwind_protect;
1321 public override bool Resolve (BlockContext ec)
1323 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1324 ec.CurrentBranching.CurrentUsageVector.Goto ();
1328 protected override void DoEmit (EmitContext ec)
1330 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1333 protected override void CloneTo (CloneContext clonectx, Statement t)
1338 public override object Accept (StructuralVisitor visitor)
1340 return visitor.Visit (this);
1344 public class Continue : Statement {
1346 public Continue (Location l)
1351 bool unwind_protect;
1353 public override bool Resolve (BlockContext ec)
1355 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1356 ec.CurrentBranching.CurrentUsageVector.Goto ();
1360 protected override void DoEmit (EmitContext ec)
1362 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1365 protected override void CloneTo (CloneContext clonectx, Statement t)
1370 public override object Accept (StructuralVisitor visitor)
1372 return visitor.Visit (this);
1376 public interface ILocalVariable
1378 void Emit (EmitContext ec);
1379 void EmitAssign (EmitContext ec);
1380 void EmitAddressOf (EmitContext ec);
1383 public interface INamedBlockVariable
1385 Block Block { get; }
1386 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1387 bool IsDeclared { get; }
1388 bool IsParameter { get; }
1389 Location Location { get; }
1392 public class BlockVariableDeclaration : Statement
1394 public class Declarator
1397 Expression initializer;
1399 public Declarator (LocalVariable li, Expression initializer)
1401 if (li.Type != null)
1402 throw new ArgumentException ("Expected null variable type");
1405 this.initializer = initializer;
1408 public Declarator (Declarator clone, Expression initializer)
1411 this.initializer = initializer;
1416 public LocalVariable Variable {
1422 public Expression Initializer {
1427 initializer = value;
1434 Expression initializer;
1435 protected FullNamedExpression type_expr;
1436 protected LocalVariable li;
1437 protected List<Declarator> declarators;
1440 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1442 this.type_expr = type;
1444 this.loc = type_expr.Location;
1447 protected BlockVariableDeclaration (LocalVariable li)
1454 public List<Declarator> Declarators {
1460 public Expression Initializer {
1465 initializer = value;
1469 public FullNamedExpression TypeExpression {
1475 public LocalVariable Variable {
1483 public void AddDeclarator (Declarator decl)
1485 if (declarators == null)
1486 declarators = new List<Declarator> ();
1488 declarators.Add (decl);
1491 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1493 if (bc.Report.Errors != 0)
1496 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1498 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1499 new MemberName (li.Name, li.Location), null);
1501 container.AddField (f);
1504 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1508 public override bool Resolve (BlockContext bc)
1510 return Resolve (bc, true);
1513 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1515 if (type == null && !li.IsCompilerGenerated) {
1516 var vexpr = type_expr as VarExpr;
1519 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1520 // same name exists or as a keyword when no type was found
1522 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1523 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1524 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1527 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1531 if (li.IsConstant) {
1532 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1536 if (Initializer == null) {
1537 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1541 if (declarators != null) {
1542 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1546 Initializer = Initializer.Resolve (bc);
1547 if (Initializer != null) {
1548 ((VarExpr) type_expr).InferType (bc, Initializer);
1549 type = type_expr.Type;
1551 // Set error type to indicate the var was placed correctly but could
1554 // var a = missing ();
1556 type = InternalType.ErrorType;
1561 type = type_expr.ResolveAsType (bc);
1565 if (li.IsConstant && !type.IsConstantCompatible) {
1566 Const.Error_InvalidConstantType (type, loc, bc.Report);
1571 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1576 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1578 CreateEvaluatorVariable (bc, li);
1580 li.PrepareForFlowAnalysis (bc);
1583 if (initializer != null) {
1584 initializer = ResolveInitializer (bc, li, initializer);
1585 // li.Variable.DefinitelyAssigned
1588 if (declarators != null) {
1589 foreach (var d in declarators) {
1590 d.Variable.Type = li.Type;
1592 CreateEvaluatorVariable (bc, d.Variable);
1594 d.Variable.PrepareForFlowAnalysis (bc);
1597 if (d.Initializer != null && resolveDeclaratorInitializers) {
1598 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1599 // d.Variable.DefinitelyAssigned
1607 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1609 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1610 return a.ResolveStatement (bc);
1613 protected override void DoEmit (EmitContext ec)
1615 li.CreateBuilder (ec);
1617 if (Initializer != null)
1618 ((ExpressionStatement) Initializer).EmitStatement (ec);
1620 if (declarators != null) {
1621 foreach (var d in declarators) {
1622 d.Variable.CreateBuilder (ec);
1623 if (d.Initializer != null)
1624 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1629 protected override void CloneTo (CloneContext clonectx, Statement target)
1631 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1633 if (type_expr != null)
1634 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1636 if (initializer != null)
1637 t.initializer = initializer.Clone (clonectx);
1639 if (declarators != null) {
1640 t.declarators = null;
1641 foreach (var d in declarators)
1642 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1646 public override object Accept (StructuralVisitor visitor)
1648 return visitor.Visit (this);
1652 public class BlockConstantDeclaration : BlockVariableDeclaration
1654 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1659 public override void Emit (EmitContext ec)
1661 // Nothing to emit, not even sequence point
1664 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1666 initializer = initializer.Resolve (bc);
1667 if (initializer == null)
1670 var c = initializer as Constant;
1672 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1676 c = c.ConvertImplicitly (li.Type);
1678 if (TypeSpec.IsReferenceType (li.Type))
1679 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1681 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1686 li.ConstantValue = c;
1690 public override object Accept (StructuralVisitor visitor)
1692 return visitor.Visit (this);
1697 // The information about a user-perceived local variable
1699 public class LocalVariable : INamedBlockVariable, ILocalVariable
1706 AddressTaken = 1 << 2,
1707 CompilerGenerated = 1 << 3,
1709 ForeachVariable = 1 << 5,
1710 FixedVariable = 1 << 6,
1711 UsingVariable = 1 << 7,
1712 // DefinitelyAssigned = 1 << 8,
1715 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1719 readonly string name;
1720 readonly Location loc;
1721 readonly Block block;
1723 Constant const_value;
1725 public VariableInfo VariableInfo;
1726 HoistedVariable hoisted_variant;
1728 LocalBuilder builder;
1730 public LocalVariable (Block block, string name, Location loc)
1737 public LocalVariable (Block block, string name, Flags flags, Location loc)
1738 : this (block, name, loc)
1744 // Used by variable declarators
1746 public LocalVariable (LocalVariable li, string name, Location loc)
1747 : this (li.block, name, li.flags, loc)
1753 public bool AddressTaken {
1755 return (flags & Flags.AddressTaken) != 0;
1759 public Block Block {
1765 public Constant ConstantValue {
1770 const_value = value;
1775 // Hoisted local variable variant
1777 public HoistedVariable HoistedVariant {
1779 return hoisted_variant;
1782 hoisted_variant = value;
1786 public bool IsDeclared {
1788 return type != null;
1792 public bool IsCompilerGenerated {
1794 return (flags & Flags.CompilerGenerated) != 0;
1798 public bool IsConstant {
1800 return (flags & Flags.Constant) != 0;
1804 public bool IsLocked {
1806 return (flags & Flags.IsLocked) != 0;
1809 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1813 public bool IsThis {
1815 return (flags & Flags.IsThis) != 0;
1819 public bool IsFixed {
1821 return (flags & Flags.FixedVariable) != 0;
1825 bool INamedBlockVariable.IsParameter {
1831 public bool IsReadonly {
1833 return (flags & Flags.ReadonlyMask) != 0;
1837 public Location Location {
1843 public string Name {
1849 public TypeSpec Type {
1860 public void CreateBuilder (EmitContext ec)
1862 if ((flags & Flags.Used) == 0) {
1863 if (VariableInfo == null) {
1864 // Missing flow analysis or wrong variable flags
1865 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1868 if (VariableInfo.IsEverAssigned)
1869 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1871 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1874 if (HoistedVariant != null)
1877 if (builder != null) {
1878 if ((flags & Flags.CompilerGenerated) != 0)
1881 // To avoid Used warning duplicates
1882 throw new InternalErrorException ("Already created variable `{0}'", name);
1886 // All fixed variabled are pinned, a slot has to be alocated
1888 builder = ec.DeclareLocal (Type, IsFixed);
1889 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1890 ec.DefineLocalVariable (name, builder);
1893 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1895 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1900 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1902 if (IsConstant && const_value != null)
1903 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1905 return new LocalVariableReference (this, loc);
1908 public void Emit (EmitContext ec)
1910 // TODO: Need something better for temporary variables
1911 if ((flags & Flags.CompilerGenerated) != 0)
1914 ec.Emit (OpCodes.Ldloc, builder);
1917 public void EmitAssign (EmitContext ec)
1919 // TODO: Need something better for temporary variables
1920 if ((flags & Flags.CompilerGenerated) != 0)
1923 ec.Emit (OpCodes.Stloc, builder);
1926 public void EmitAddressOf (EmitContext ec)
1928 ec.Emit (OpCodes.Ldloca, builder);
1931 public static string GetCompilerGeneratedName (Block block)
1933 // HACK: Debugger depends on the name semantics
1934 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1937 public string GetReadOnlyContext ()
1939 switch (flags & Flags.ReadonlyMask) {
1940 case Flags.FixedVariable:
1941 return "fixed variable";
1942 case Flags.ForeachVariable:
1943 return "foreach iteration variable";
1944 case Flags.UsingVariable:
1945 return "using variable";
1948 throw new InternalErrorException ("Variable is not readonly");
1951 public bool IsThisAssigned (BlockContext ec, Block block)
1953 if (VariableInfo == null)
1954 throw new Exception ();
1956 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1959 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1962 public bool IsAssigned (BlockContext ec)
1964 if (VariableInfo == null)
1965 throw new Exception ();
1967 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1970 public void PrepareForFlowAnalysis (BlockContext bc)
1973 // No need for definitely assigned check for these guys
1975 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1978 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1979 bc.FlowOffset += VariableInfo.Length;
1983 // Mark the variables as referenced in the user code
1985 public void SetIsUsed ()
1987 flags |= Flags.Used;
1990 public void SetHasAddressTaken ()
1992 flags |= (Flags.AddressTaken | Flags.Used);
1995 public override string ToString ()
1997 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2002 /// Block represents a C# block.
2006 /// This class is used in a number of places: either to represent
2007 /// explicit blocks that the programmer places or implicit blocks.
2009 /// Implicit blocks are used as labels or to introduce variable
2012 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2013 /// they contain extra information that is not necessary on normal blocks.
2015 public class Block : Statement {
2022 HasCapturedVariable = 64,
2023 HasCapturedThis = 1 << 7,
2024 IsExpressionTree = 1 << 8,
2025 CompilerGenerated = 1 << 9,
2026 HasAsyncModifier = 1 << 10,
2028 YieldBlock = 1 << 12,
2029 AwaitBlock = 1 << 13
2032 public Block Parent;
2033 public Location StartLocation;
2034 public Location EndLocation;
2036 public ExplicitBlock Explicit;
2037 public ParametersBlock ParametersBlock;
2039 protected Flags flags;
2042 // The statements in this block
2044 protected List<Statement> statements;
2046 protected List<Statement> scope_initializers;
2048 int? resolving_init_idx;
2054 public int ID = id++;
2056 static int clone_id_counter;
2060 // int assignable_slots;
2061 bool unreachable_shown;
2064 public Block (Block parent, Location start, Location end)
2065 : this (parent, 0, start, end)
2069 public Block (Block parent, Flags flags, Location start, Location end)
2071 if (parent != null) {
2072 // the appropriate constructors will fixup these fields
2073 ParametersBlock = parent.ParametersBlock;
2074 Explicit = parent.Explicit;
2077 this.Parent = parent;
2079 this.StartLocation = start;
2080 this.EndLocation = end;
2082 statements = new List<Statement> (4);
2084 this.original = this;
2089 public bool HasUnreachableClosingBrace {
2091 return (flags & Flags.HasRet) != 0;
2094 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2098 public Block Original {
2107 public bool IsCompilerGenerated {
2108 get { return (flags & Flags.CompilerGenerated) != 0; }
2109 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2112 public bool Unchecked {
2113 get { return (flags & Flags.Unchecked) != 0; }
2114 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2117 public bool Unsafe {
2118 get { return (flags & Flags.Unsafe) != 0; }
2119 set { flags |= Flags.Unsafe; }
2122 public List<Statement> Statements {
2123 get { return statements; }
2128 public Block CreateSwitchBlock (Location start)
2130 // FIXME: Only explicit block should be created
2131 var new_block = new Block (this, start, start);
2132 new_block.IsCompilerGenerated = true;
2136 public void SetEndLocation (Location loc)
2141 public void AddLabel (LabeledStatement target)
2143 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2146 public void AddLocalName (LocalVariable li)
2148 AddLocalName (li.Name, li);
2151 public void AddLocalName (string name, INamedBlockVariable li)
2153 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2156 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2158 if (reason == null) {
2159 Error_AlreadyDeclared (name, variable);
2163 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2164 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2165 "to `{0}', which is already used in a `{1}' scope to denote something else",
2169 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2171 var pi = variable as ParametersBlock.ParameterInfo;
2173 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2175 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2176 "A local variable named `{0}' is already defined in this scope", name);
2180 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2182 ParametersBlock.TopBlock.Report.Error (412, loc,
2183 "The type parameter name `{0}' is the same as local variable or parameter name",
2188 // It should be used by expressions which require to
2189 // register a statement during resolve process.
2191 public void AddScopeStatement (Statement s)
2193 if (scope_initializers == null)
2194 scope_initializers = new List<Statement> ();
2197 // Simple recursive helper, when resolve scope initializer another
2198 // new scope initializer can be added, this ensures it's initialized
2199 // before existing one. For now this can happen with expression trees
2200 // in base ctor initializer only
2202 if (resolving_init_idx.HasValue) {
2203 scope_initializers.Insert (resolving_init_idx.Value, s);
2204 ++resolving_init_idx;
2206 scope_initializers.Add (s);
2210 public void AddStatement (Statement s)
2215 public int AssignableSlots {
2217 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2219 // return assignable_slots;
2223 public LabeledStatement LookupLabel (string name)
2225 return ParametersBlock.TopBlock.GetLabel (name, this);
2228 public override bool Resolve (BlockContext ec)
2230 if ((flags & Flags.Resolved) != 0)
2233 Block prev_block = ec.CurrentBlock;
2236 ec.CurrentBlock = this;
2237 ec.StartFlowBranching (this);
2240 // Compiler generated scope statements
2242 if (scope_initializers != null) {
2243 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2244 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2247 resolving_init_idx = null;
2251 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2252 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2253 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2254 // responsible for handling the situation.
2256 int statement_count = statements.Count;
2257 for (int ix = 0; ix < statement_count; ix++){
2258 Statement s = statements [ix];
2261 // Warn if we detect unreachable code.
2264 if (s is EmptyStatement)
2267 if (!unreachable_shown && !(s is LabeledStatement)) {
2268 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2269 unreachable_shown = true;
2272 Block c_block = s as Block;
2273 if (c_block != null)
2274 c_block.unreachable = c_block.unreachable_shown = true;
2278 // Note that we're not using ResolveUnreachable() for unreachable
2279 // statements here. ResolveUnreachable() creates a temporary
2280 // flow branching and kills it afterwards. This leads to problems
2281 // if you have two unreachable statements where the first one
2282 // assigns a variable and the second one tries to access it.
2285 if (!s.Resolve (ec)) {
2287 if (ec.IsInProbingMode)
2290 statements [ix] = new EmptyStatement (s.loc);
2294 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2295 statements [ix] = new EmptyStatement (s.loc);
2297 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2298 if (unreachable && s is LabeledStatement)
2299 throw new InternalErrorException ("should not happen");
2302 while (ec.CurrentBranching is FlowBranchingLabeled)
2303 ec.EndFlowBranching ();
2305 bool flow_unreachable = ec.EndFlowBranching ();
2307 ec.CurrentBlock = prev_block;
2309 if (flow_unreachable)
2310 flags |= Flags.HasRet;
2312 // If we're a non-static `struct' constructor which doesn't have an
2313 // initializer, then we must initialize all of the struct's fields.
2314 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2317 flags |= Flags.Resolved;
2321 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2323 unreachable_shown = true;
2327 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2329 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2330 fb.CurrentUsageVector.IsUnreachable = true;
2331 bool ok = Resolve (ec);
2332 ec.KillFlowBranching ();
2337 protected override void DoEmit (EmitContext ec)
2339 for (int ix = 0; ix < statements.Count; ix++){
2340 statements [ix].Emit (ec);
2344 public override void Emit (EmitContext ec)
2346 if (scope_initializers != null)
2347 EmitScopeInitializers (ec);
2352 protected void EmitScopeInitializers (EmitContext ec)
2354 foreach (Statement s in scope_initializers)
2359 public override string ToString ()
2361 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2365 protected override void CloneTo (CloneContext clonectx, Statement t)
2367 Block target = (Block) t;
2369 target.clone_id = clone_id_counter++;
2372 clonectx.AddBlockMap (this, target);
2373 if (original != this)
2374 clonectx.AddBlockMap (original, target);
2376 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2377 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2380 target.Parent = clonectx.RemapBlockCopy (Parent);
2382 target.statements = new List<Statement> (statements.Count);
2383 foreach (Statement s in statements)
2384 target.statements.Add (s.Clone (clonectx));
2387 public override object Accept (StructuralVisitor visitor)
2389 return visitor.Visit (this);
2393 public class ExplicitBlock : Block
2395 protected AnonymousMethodStorey am_storey;
2397 public ExplicitBlock (Block parent, Location start, Location end)
2398 : this (parent, (Flags) 0, start, end)
2402 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2403 : base (parent, flags, start, end)
2405 this.Explicit = this;
2410 public AnonymousMethodStorey AnonymousMethodStorey {
2416 public bool HasAwait {
2418 return (flags & Flags.AwaitBlock) != 0;
2422 public bool HasCapturedThis {
2424 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2427 return (flags & Flags.HasCapturedThis) != 0;
2432 // Used to indicate that the block has reference to parent
2433 // block and cannot be made static when defining anonymous method
2435 public bool HasCapturedVariable {
2437 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2440 return (flags & Flags.HasCapturedVariable) != 0;
2444 public bool HasYield {
2446 return (flags & Flags.YieldBlock) != 0;
2453 // Creates anonymous method storey in current block
2455 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2458 // Return same story for iterator and async blocks unless we are
2459 // in nested anonymous method
2461 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2462 return ec.CurrentAnonymousMethod.Storey;
2464 if (am_storey == null) {
2465 MemberBase mc = ec.MemberContext as MemberBase;
2468 // Creates anonymous method storey for this block
2470 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2476 public override void Emit (EmitContext ec)
2478 if (am_storey != null) {
2479 DefineStoreyContainer (ec, am_storey);
2480 am_storey.EmitStoreyInstantiation (ec, this);
2483 if (scope_initializers != null)
2484 EmitScopeInitializers (ec);
2486 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2487 ec.Emit (OpCodes.Nop);
2498 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2499 ec.Emit (OpCodes.Nop);
2503 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2505 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2506 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2507 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2511 // Creates anonymous method storey
2513 storey.CreateContainer ();
2514 storey.DefineContainer ();
2516 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2519 // Only first storey in path will hold this reference. All children blocks will
2520 // reference it indirectly using $ref field
2522 for (Block b = Original.Explicit.Parent; b != null; b = b.Parent) {
2523 var s = b.Explicit.AnonymousMethodStorey;
2525 storey.HoistedThis = s.HoistedThis;
2531 // We are the first storey on path and this has to be hoisted
2533 if (storey.HoistedThis == null) {
2534 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2536 // ThisReferencesFromChildrenBlock holds all reference even if they
2537 // are not on this path. It saves some memory otherwise it'd have to
2538 // be in every explicit block. We run this check to see if the reference
2539 // is valid for this storey
2541 Block block_on_path = ref_block;
2542 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2544 if (block_on_path == null)
2547 if (storey.HoistedThis == null)
2548 storey.AddCapturedThisField (ec);
2550 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2551 if (b.AnonymousMethodStorey != null) {
2552 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2553 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2556 // Stop propagation inside same top block
2558 if (b.ParametersBlock == ParametersBlock.Original)
2561 b = b.ParametersBlock;
2564 var pb = b as ParametersBlock;
2565 if (pb != null && pb.StateMachine != null) {
2566 if (pb.StateMachine == storey)
2569 pb.StateMachine.AddParentStoreyReference (ec, storey);
2572 b.HasCapturedVariable = true;
2578 var ref_blocks = storey.ReferencesFromChildrenBlock;
2579 if (ref_blocks != null) {
2580 foreach (ExplicitBlock ref_block in ref_blocks) {
2581 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2582 if (b.AnonymousMethodStorey != null) {
2583 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2586 // Stop propagation inside same top block
2588 if (b.ParametersBlock == ParametersBlock.Original)
2591 b = b.ParametersBlock;
2594 var pb = b as ParametersBlock;
2595 if (pb != null && pb.StateMachine != null) {
2596 if (pb.StateMachine == storey)
2599 pb.StateMachine.AddParentStoreyReference (ec, storey);
2602 b.HasCapturedVariable = true;
2608 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2611 public void RegisterAsyncAwait ()
2614 while ((block.flags & Flags.AwaitBlock) == 0) {
2615 block.flags |= Flags.AwaitBlock;
2617 if (block is ParametersBlock)
2620 block = block.Parent.Explicit;
2624 public void RegisterIteratorYield ()
2627 while ((block.flags & Flags.YieldBlock) == 0) {
2628 block.flags |= Flags.YieldBlock;
2630 if (block.Parent == null)
2633 block = block.Parent.Explicit;
2637 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2639 tryBlock.statements = statements;
2640 statements = new List<Statement> (1);
2641 statements.Add (tf);
2646 // ParametersBlock was introduced to support anonymous methods
2647 // and lambda expressions
2649 public class ParametersBlock : ExplicitBlock
2651 public class ParameterInfo : INamedBlockVariable
2653 readonly ParametersBlock block;
2655 public VariableInfo VariableInfo;
2658 public ParameterInfo (ParametersBlock block, int index)
2666 public ParametersBlock Block {
2672 Block INamedBlockVariable.Block {
2678 public bool IsDeclared {
2684 public bool IsParameter {
2690 public bool IsLocked {
2699 public Location Location {
2701 return Parameter.Location;
2705 public Parameter Parameter {
2707 return block.Parameters [index];
2711 public TypeSpec ParameterType {
2713 return Parameter.Type;
2719 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2721 return new ParameterReference (this, loc);
2726 // Block is converted into an expression
2728 sealed class BlockScopeExpression : Expression
2731 readonly ParametersBlock block;
2733 public BlockScopeExpression (Expression child, ParametersBlock block)
2739 public override bool ContainsEmitWithAwait ()
2741 return child.ContainsEmitWithAwait ();
2744 public override Expression CreateExpressionTree (ResolveContext ec)
2746 throw new NotSupportedException ();
2749 protected override Expression DoResolve (ResolveContext ec)
2754 child = child.Resolve (ec);
2758 eclass = child.eclass;
2763 public override void Emit (EmitContext ec)
2765 block.EmitScopeInitializers (ec);
2770 protected ParametersCompiled parameters;
2771 protected ParameterInfo[] parameter_info;
2773 protected bool unreachable;
2774 protected ToplevelBlock top_block;
2775 protected StateMachine state_machine;
2777 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2778 : base (parent, 0, start, start)
2780 if (parameters == null)
2781 throw new ArgumentNullException ("parameters");
2783 this.parameters = parameters;
2784 ParametersBlock = this;
2786 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2788 this.top_block = parent.ParametersBlock.top_block;
2789 ProcessParameters ();
2792 protected ParametersBlock (ParametersCompiled parameters, Location start)
2793 : base (null, 0, start, start)
2795 if (parameters == null)
2796 throw new ArgumentNullException ("parameters");
2798 this.parameters = parameters;
2799 ParametersBlock = this;
2803 // It's supposed to be used by method body implementation of anonymous methods
2805 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2806 : base (null, 0, source.StartLocation, source.EndLocation)
2808 this.parameters = parameters;
2809 this.statements = source.statements;
2810 this.scope_initializers = source.scope_initializers;
2812 this.resolved = true;
2813 this.unreachable = source.unreachable;
2814 this.am_storey = source.am_storey;
2815 this.state_machine = source.state_machine;
2817 ParametersBlock = this;
2820 // Overwrite original for comparison purposes when linking cross references
2821 // between anonymous methods
2828 public bool IsAsync {
2830 return (flags & Flags.HasAsyncModifier) != 0;
2833 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2838 // Block has been converted to expression tree
2840 public bool IsExpressionTree {
2842 return (flags & Flags.IsExpressionTree) != 0;
2847 // The parameters for the block.
2849 public ParametersCompiled Parameters {
2855 public StateMachine StateMachine {
2857 return state_machine;
2861 public ToplevelBlock TopBlock {
2867 public bool Resolved {
2869 return (flags & Flags.Resolved) != 0;
2873 public int TemporaryLocalsCount { get; set; }
2878 // Check whether all `out' parameters have been assigned.
2880 public void CheckOutParameters (FlowBranching.UsageVector vector)
2882 if (vector.IsUnreachable)
2885 int n = parameter_info == null ? 0 : parameter_info.Length;
2887 for (int i = 0; i < n; i++) {
2888 VariableInfo var = parameter_info[i].VariableInfo;
2893 if (vector.IsAssigned (var, false))
2896 var p = parameter_info[i].Parameter;
2897 TopBlock.Report.Error (177, p.Location,
2898 "The out parameter `{0}' must be assigned to before control leaves the current method",
2903 public override Expression CreateExpressionTree (ResolveContext ec)
2905 if (statements.Count == 1) {
2906 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2907 if (scope_initializers != null)
2908 expr = new BlockScopeExpression (expr, this);
2913 return base.CreateExpressionTree (ec);
2916 public override void Emit (EmitContext ec)
2918 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2919 DefineStoreyContainer (ec, state_machine);
2920 state_machine.EmitStoreyInstantiation (ec, this);
2926 public void EmitEmbedded (EmitContext ec)
2928 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2929 DefineStoreyContainer (ec, state_machine);
2930 state_machine.EmitStoreyInstantiation (ec, this);
2936 public ParameterInfo GetParameterInfo (Parameter p)
2938 for (int i = 0; i < parameters.Count; ++i) {
2939 if (parameters[i] == p)
2940 return parameter_info[i];
2943 throw new ArgumentException ("Invalid parameter");
2946 public ParameterReference GetParameterReference (int index, Location loc)
2948 return new ParameterReference (parameter_info[index], loc);
2951 public Statement PerformClone ()
2953 CloneContext clonectx = new CloneContext ();
2954 return Clone (clonectx);
2957 protected void ProcessParameters ()
2959 if (parameters.Count == 0)
2962 parameter_info = new ParameterInfo[parameters.Count];
2963 for (int i = 0; i < parameter_info.Length; ++i) {
2964 var p = parameters.FixedParameters[i];
2968 // TODO: Should use Parameter only and more block there
2969 parameter_info[i] = new ParameterInfo (this, i);
2971 AddLocalName (p.Name, parameter_info[i]);
2975 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2982 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2983 flags |= Flags.IsExpressionTree;
2988 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2989 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2994 unreachable = top_level.End ();
2996 } catch (Exception e) {
2997 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3000 if (rc.CurrentBlock != null) {
3001 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3003 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3006 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3010 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3011 if (rc.CurrentAnonymousMethod == null) {
3012 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3013 if (md is StateMachineMethod) {
3016 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3021 // If an asynchronous body of F is either an expression classified as nothing, or a
3022 // statement block where no return statements have expressions, the inferred return type is Task
3025 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3026 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3027 am.ReturnTypeInference = null;
3028 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3033 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3034 rc.CurrentAnonymousMethod.GetSignatureForError ());
3042 void ResolveMeta (BlockContext ec)
3044 int orig_count = parameters.Count;
3046 for (int i = 0; i < orig_count; ++i) {
3047 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3049 if ((mod & Parameter.Modifier.OUT) == 0)
3052 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3053 parameter_info[i].VariableInfo = vi;
3054 ec.FlowOffset += vi.Length;
3058 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3060 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3061 var stateMachine = new IteratorStorey (iterator);
3063 state_machine = stateMachine;
3064 iterator.SetStateMachine (stateMachine);
3066 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3067 tlb.Original = this;
3068 tlb.IsCompilerGenerated = true;
3069 tlb.state_machine = stateMachine;
3070 tlb.AddStatement (new Return (iterator, iterator.Location));
3074 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3076 for (int i = 0; i < parameters.Count; i++) {
3077 Parameter p = parameters[i];
3078 Parameter.Modifier mod = p.ModFlags;
3079 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3080 host.Compiler.Report.Error (1988, p.Location,
3081 "Async methods cannot have ref or out parameters");
3085 if (p is ArglistParameter) {
3086 host.Compiler.Report.Error (4006, p.Location,
3087 "__arglist is not allowed in parameter list of async methods");
3091 if (parameters.Types[i].IsPointer) {
3092 host.Compiler.Report.Error (4005, p.Location,
3093 "Async methods cannot have unsafe parameters");
3099 host.Compiler.Report.Warning (1998, 1, loc,
3100 "Async block lacks `await' operator and will run synchronously");
3103 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3104 var initializer = new AsyncInitializer (this, host, block_type);
3105 initializer.Type = block_type;
3107 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3109 state_machine = stateMachine;
3110 initializer.SetStateMachine (stateMachine);
3112 var b = this is ToplevelBlock ?
3113 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3114 new ParametersBlock (Parent, parameters, Location.Null) {
3119 b.IsCompilerGenerated = true;
3120 b.state_machine = stateMachine;
3121 b.AddStatement (new StatementExpression (initializer));
3129 public class ToplevelBlock : ParametersBlock
3131 LocalVariable this_variable;
3132 CompilerContext compiler;
3133 Dictionary<string, object> names;
3134 Dictionary<string, object> labels;
3136 List<ExplicitBlock> this_references;
3138 public ToplevelBlock (CompilerContext ctx, Location loc)
3139 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3143 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3144 : base (parameters, start)
3146 this.compiler = ctx;
3148 flags |= Flags.HasRet;
3150 ProcessParameters ();
3154 // Recreates a top level block from parameters block. Used for
3155 // compiler generated methods where the original block comes from
3156 // explicit child block. This works for already resolved blocks
3157 // only to ensure we resolve them in the correct flow order
3159 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3160 : base (source, parameters)
3162 this.compiler = source.TopBlock.compiler;
3164 flags |= Flags.HasRet;
3167 public bool IsIterator {
3173 public Report Report {
3175 return compiler.Report;
3180 // Used by anonymous blocks to track references of `this' variable
3182 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3184 return this_references;
3189 // Returns the "this" instance variable of this block.
3190 // See AddThisVariable() for more information.
3192 public LocalVariable ThisVariable {
3194 return this_variable;
3198 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3201 names = new Dictionary<string, object> ();
3204 if (!names.TryGetValue (name, out value)) {
3205 names.Add (name, li);
3209 INamedBlockVariable existing = value as INamedBlockVariable;
3210 List<INamedBlockVariable> existing_list;
3211 if (existing != null) {
3212 existing_list = new List<INamedBlockVariable> ();
3213 existing_list.Add (existing);
3214 names[name] = existing_list;
3216 existing_list = (List<INamedBlockVariable>) value;
3220 // A collision checking between local names
3222 for (int i = 0; i < existing_list.Count; ++i) {
3223 existing = existing_list[i];
3224 Block b = existing.Block.Explicit;
3226 // Collision at same level
3227 if (li.Block.Explicit == b) {
3228 li.Block.Error_AlreadyDeclared (name, li);
3232 // Collision with parent
3233 Block parent = li.Block.Explicit;
3234 while ((parent = parent.Parent) != null) {
3236 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3237 i = existing_list.Count;
3242 if (!ignoreChildrenBlocks) {
3243 // Collision with children
3244 while ((b = b.Parent) != null) {
3245 if (li.Block.Explicit == b) {
3246 li.Block.Error_AlreadyDeclared (name, li, "child");
3247 i = existing_list.Count;
3254 existing_list.Add (li);
3257 public void AddLabel (string name, LabeledStatement label)
3260 labels = new Dictionary<string, object> ();
3263 if (!labels.TryGetValue (name, out value)) {
3264 labels.Add (name, label);
3268 LabeledStatement existing = value as LabeledStatement;
3269 List<LabeledStatement> existing_list;
3270 if (existing != null) {
3271 existing_list = new List<LabeledStatement> ();
3272 existing_list.Add (existing);
3273 labels[name] = existing_list;
3275 existing_list = (List<LabeledStatement>) value;
3279 // A collision checking between labels
3281 for (int i = 0; i < existing_list.Count; ++i) {
3282 existing = existing_list[i];
3283 Block b = existing.Block;
3285 // Collision at same level
3286 if (label.Block == b) {
3287 Report.SymbolRelatedToPreviousError (existing.loc, name);
3288 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3292 // Collision with parent
3294 while ((b = b.Parent) != null) {
3295 if (existing.Block == b) {
3296 Report.Error (158, label.loc,
3297 "The label `{0}' shadows another label by the same name in a contained scope", name);
3298 i = existing_list.Count;
3303 // Collision with with children
3305 while ((b = b.Parent) != null) {
3306 if (label.Block == b) {
3307 Report.Error (158, label.loc,
3308 "The label `{0}' shadows another label by the same name in a contained scope", name);
3309 i = existing_list.Count;
3315 existing_list.Add (label);
3318 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3320 if (this_references == null)
3321 this_references = new List<ExplicitBlock> ();
3323 if (!this_references.Contains (block))
3324 this_references.Add (block);
3327 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3329 this_references.Remove (block);
3333 // Creates an arguments set from all parameters, useful for method proxy calls
3335 public Arguments GetAllParametersArguments ()
3337 int count = parameters.Count;
3338 Arguments args = new Arguments (count);
3339 for (int i = 0; i < count; ++i) {
3340 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3341 args.Add (new Argument (arg_expr));
3348 // Lookup inside a block, the returned value can represent 3 states
3350 // true+variable: A local name was found and it's valid
3351 // false+variable: A local name was found in a child block only
3352 // false+null: No local name was found
3354 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3360 if (!names.TryGetValue (name, out value))
3363 variable = value as INamedBlockVariable;
3365 if (variable != null) {
3367 if (variable.Block == b.Original)
3371 } while (b != null);
3379 } while (b != null);
3381 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3382 for (int i = 0; i < list.Count; ++i) {
3385 if (variable.Block == b.Original)
3389 } while (b != null);
3397 } while (b != null);
3407 public LabeledStatement GetLabel (string name, Block block)
3413 if (!labels.TryGetValue (name, out value)) {
3417 var label = value as LabeledStatement;
3419 if (label != null) {
3420 if (label.Block == b.Original)
3423 // TODO: Temporary workaround for the switch block implicit label block
3424 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3427 List<LabeledStatement> list = (List<LabeledStatement>) value;
3428 for (int i = 0; i < list.Count; ++i) {
3430 if (label.Block == b.Original)
3433 // TODO: Temporary workaround for the switch block implicit label block
3434 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3443 // This is used by non-static `struct' constructors which do not have an
3444 // initializer - in this case, the constructor must initialize all of the
3445 // struct's fields. To do this, we add a "this" variable and use the flow
3446 // analysis code to ensure that it's been fully initialized before control
3447 // leaves the constructor.
3449 public void AddThisVariable (BlockContext bc)
3451 if (this_variable != null)
3452 throw new InternalErrorException (StartLocation.ToString ());
3454 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3455 this_variable.Type = bc.CurrentType;
3456 this_variable.PrepareForFlowAnalysis (bc);
3459 public bool IsThisAssigned (BlockContext ec)
3461 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3464 public override void Emit (EmitContext ec)
3466 if (Report.Errors > 0)
3472 if (IsCompilerGenerated) {
3473 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3481 // If `HasReturnLabel' is set, then we already emitted a
3482 // jump to the end of the method, so we must emit a `ret'
3485 // Unfortunately, System.Reflection.Emit automatically emits
3486 // a leave to the end of a finally block. This is a problem
3487 // if no code is following the try/finally block since we may
3488 // jump to a point after the end of the method.
3489 // As a workaround, we're always creating a return label in
3492 if (ec.HasReturnLabel || !unreachable) {
3493 if (ec.HasReturnLabel)
3494 ec.MarkLabel (ec.ReturnLabel);
3496 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3497 ec.Mark (EndLocation);
3499 if (ec.ReturnType.Kind != MemberKind.Void)
3500 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3502 ec.Emit (OpCodes.Ret);
3506 } catch (Exception e){
3507 Console.WriteLine ("Exception caught by the compiler while emitting:");
3508 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3510 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3517 public class SwitchLabel {
3520 readonly Location loc;
3525 // if expr == null, then it is the default case.
3527 public SwitchLabel (Expression expr, Location l)
3533 public bool IsDefault {
3535 return label == null;
3539 public Expression Label {
3545 public Location Location {
3551 public Constant Converted {
3560 public Label GetILLabel (EmitContext ec)
3562 if (il_label == null){
3563 il_label = ec.DefineLabel ();
3566 return il_label.Value;
3570 // Resolves the expression, reduces it to a literal if possible
3571 // and then converts it to the requested type.
3573 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3575 Expression e = label.Resolve (ec);
3580 Constant c = e as Constant;
3582 ec.Report.Error (150, loc, "A constant value is expected");
3586 if (allow_nullable && c is NullLiteral) {
3591 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3592 return converted != null;
3595 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3598 if (converted == null)
3601 label = converted.GetValueAsLiteral ();
3603 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3604 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3607 public SwitchLabel Clone (CloneContext clonectx)
3612 return new SwitchLabel (label.Clone (clonectx), loc);
3616 public class SwitchSection {
3617 public readonly List<SwitchLabel> Labels;
3618 public readonly Block Block;
3620 public SwitchSection (List<SwitchLabel> labels, Block block)
3626 public SwitchSection Clone (CloneContext clonectx)
3628 var cloned_labels = new List<SwitchLabel> ();
3630 foreach (SwitchLabel sl in Labels)
3631 cloned_labels.Add (sl.Clone (clonectx));
3633 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3637 public class Switch : Statement
3639 // structure used to hold blocks of keys while calculating table switch
3640 sealed class LabelsRange : IComparable<LabelsRange>
3642 public readonly long min;
3644 public readonly List<long> label_values;
3646 public LabelsRange (long value)
3649 label_values = new List<long> ();
3650 label_values.Add (value);
3653 public LabelsRange (long min, long max, ICollection<long> values)
3657 this.label_values = new List<long> (values);
3662 return max - min + 1;
3666 public bool AddValue (long value)
3668 var gap = value - min + 1;
3669 // Ensure the range has > 50% occupancy
3670 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3674 label_values.Add (value);
3678 public int CompareTo (LabelsRange other)
3680 int nLength = label_values.Count;
3681 int nLengthOther = other.label_values.Count;
3682 if (nLengthOther == nLength)
3683 return (int) (other.min - min);
3685 return nLength - nLengthOther;
3689 sealed class LabelMarker : Statement
3692 readonly List<SwitchLabel> labels;
3694 public LabelMarker (Switch s, List<SwitchLabel> labels)
3697 this.labels = labels;
3700 protected override void CloneTo (CloneContext clonectx, Statement target)
3704 protected override void DoEmit (EmitContext ec)
3706 foreach (var l in labels) {
3708 ec.MarkLabel (s.DefaultLabel);
3710 ec.MarkLabel (l.GetILLabel (ec));
3715 public List<SwitchSection> Sections;
3716 public Expression Expr;
3719 // Mapping of all labels to their SwitchLabels
3721 Dictionary<long, SwitchLabel> labels;
3722 Dictionary<string, SwitchLabel> string_labels;
3725 /// The governing switch type
3727 public TypeSpec SwitchType;
3732 Label default_target;
3734 Expression new_expr;
3737 SwitchSection constant_section;
3738 SwitchSection default_section;
3739 SwitchLabel null_section;
3741 Statement simple_stmt;
3742 VariableReference value;
3743 ExpressionStatement string_dictionary;
3744 FieldExpr switch_cache_field;
3745 ExplicitBlock block;
3748 // Nullable Types support
3750 Nullable.Unwrap unwrap;
3752 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3760 public ExplicitBlock Block {
3766 public Label DefaultLabel {
3768 return default_target;
3772 public bool GotDefault {
3774 return default_section != null;
3778 public bool IsNullable {
3780 return unwrap != null;
3785 // Determines the governing type for a switch. The returned
3786 // expression might be the expression from the switch, or an
3787 // expression that includes any potential conversions to
3789 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3791 switch (expr.Type.BuiltinType) {
3792 case BuiltinTypeSpec.Type.Byte:
3793 case BuiltinTypeSpec.Type.SByte:
3794 case BuiltinTypeSpec.Type.UShort:
3795 case BuiltinTypeSpec.Type.Short:
3796 case BuiltinTypeSpec.Type.UInt:
3797 case BuiltinTypeSpec.Type.Int:
3798 case BuiltinTypeSpec.Type.ULong:
3799 case BuiltinTypeSpec.Type.Long:
3800 case BuiltinTypeSpec.Type.Char:
3801 case BuiltinTypeSpec.Type.String:
3802 case BuiltinTypeSpec.Type.Bool:
3806 if (expr.Type.IsEnum)
3810 // Try to find a *user* defined implicit conversion.
3812 // If there is no implicit conversion, or if there are multiple
3813 // conversions, we have to report an error
3815 Expression converted = null;
3816 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3819 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3824 // Ignore over-worked ImplicitUserConversions that do
3825 // an implicit conversion in addition to the user conversion.
3827 if (!(e is UserCast))
3830 if (converted != null){
3831 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3840 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3842 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3858 // Performs the basic sanity checks on the switch statement
3859 // (looks for duplicate keys and non-constant expressions).
3861 // It also returns a hashtable with the keys that we will later
3862 // use to compute the switch tables
3864 bool CheckSwitch (ResolveContext ec)
3867 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3868 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3870 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3872 foreach (SwitchSection ss in Sections){
3873 foreach (SwitchLabel sl in ss.Labels){
3875 if (default_section != null){
3876 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3879 default_section = ss;
3883 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3889 if (string_labels != null) {
3890 string s = sl.Converted.GetValue () as string;
3894 string_labels.Add (s, sl);
3896 if (sl.Converted is NullLiteral) {
3899 labels.Add (sl.Converted.GetValueAsLong (), sl);
3902 } catch (ArgumentException) {
3903 if (string_labels != null)
3904 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3906 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3916 // This method emits code for a lookup-based switch statement (non-string)
3917 // Basically it groups the cases into blocks that are at least half full,
3918 // and then spits out individual lookup opcodes for each block.
3919 // It emits the longest blocks first, and short blocks are just
3920 // handled with direct compares.
3922 void EmitTableSwitch (EmitContext ec, Expression val)
3924 Label lbl_default = default_target;
3926 if (labels != null && labels.Count > 0) {
3927 List<LabelsRange> ranges;
3928 if (string_labels != null) {
3929 // We have done all hard work for string already
3930 // setup single range only
3931 ranges = new List<LabelsRange> (1);
3932 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3934 var element_keys = new long[labels.Count];
3935 labels.Keys.CopyTo (element_keys, 0);
3936 Array.Sort (element_keys);
3939 // Build possible ranges of switch labes to reduce number
3942 ranges = new List<LabelsRange> (element_keys.Length);
3943 var range = new LabelsRange (element_keys[0]);
3945 for (int i = 1; i < element_keys.Length; ++i) {
3946 var l = element_keys[i];
3947 if (range.AddValue (l))
3950 range = new LabelsRange (l);
3954 // sort the blocks so we can tackle the largest ones first
3958 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3960 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3961 LabelsRange kb = ranges[range_index];
3962 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3964 // Optimize small ranges using simple equality check
3965 if (kb.Range <= 2) {
3966 foreach (var key in kb.label_values) {
3967 SwitchLabel sl = labels[key];
3968 if (sl.Converted.IsDefaultValue) {
3969 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3972 sl.Converted.Emit (ec);
3973 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3977 // TODO: if all the keys in the block are the same and there are
3978 // no gaps/defaults then just use a range-check.
3979 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3980 // TODO: optimize constant/I4 cases
3982 // check block range (could be > 2^31)
3984 ec.EmitLong (kb.min);
3985 ec.Emit (OpCodes.Blt, lbl_default);
3988 ec.EmitLong (kb.max);
3989 ec.Emit (OpCodes.Bgt, lbl_default);
3994 ec.EmitLong (kb.min);
3995 ec.Emit (OpCodes.Sub);
3998 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4002 int first = (int) kb.min;
4005 ec.Emit (OpCodes.Sub);
4006 } else if (first < 0) {
4007 ec.EmitInt (-first);
4008 ec.Emit (OpCodes.Add);
4012 // first, build the list of labels for the switch
4014 long cJumps = kb.Range;
4015 Label[] switch_labels = new Label[cJumps];
4016 for (int iJump = 0; iJump < cJumps; iJump++) {
4017 var key = kb.label_values[iKey];
4018 if (key == kb.min + iJump) {
4019 switch_labels[iJump] = labels[key].GetILLabel (ec);
4022 switch_labels[iJump] = lbl_default;
4026 // emit the switch opcode
4027 ec.Emit (OpCodes.Switch, switch_labels);
4030 // mark the default for this block
4031 if (range_index != 0)
4032 ec.MarkLabel (lbl_default);
4035 // the last default just goes to the end
4036 if (ranges.Count > 0)
4037 ec.Emit (OpCodes.Br, lbl_default);
4040 // now emit the code for the sections
4041 bool found_default = false;
4043 foreach (SwitchSection ss in Sections) {
4044 foreach (SwitchLabel sl in ss.Labels) {
4046 ec.MarkLabel (lbl_default);
4047 found_default = true;
4048 if (null_section == null)
4049 ec.MarkLabel (null_target);
4050 } else if (sl.Converted.IsNull) {
4051 ec.MarkLabel (null_target);
4054 ec.MarkLabel (sl.GetILLabel (ec));
4060 if (!found_default) {
4061 ec.MarkLabel (lbl_default);
4062 if (null_section == null) {
4063 ec.MarkLabel (null_target);
4068 SwitchLabel FindLabel (Constant value)
4070 SwitchLabel sl = null;
4072 if (string_labels != null) {
4073 string s = value.GetValue () as string;
4075 if (null_section != null)
4077 else if (default_section != null)
4078 sl = default_section.Labels[0];
4080 string_labels.TryGetValue (s, out sl);
4083 if (value is NullLiteral) {
4086 labels.TryGetValue (value.GetValueAsLong (), out sl);
4093 SwitchSection FindSection (SwitchLabel label)
4095 foreach (SwitchSection ss in Sections){
4096 foreach (SwitchLabel sl in ss.Labels){
4105 public override bool Resolve (BlockContext ec)
4107 Expr = Expr.Resolve (ec);
4111 new_expr = SwitchGoverningType (ec, Expr);
4113 if (new_expr == null && Expr.Type.IsNullableType) {
4114 unwrap = Nullable.Unwrap.Create (Expr, false);
4118 new_expr = SwitchGoverningType (ec, unwrap);
4121 if (new_expr == null){
4122 ec.Report.Error (151, loc,
4123 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4124 TypeManager.CSharpName (Expr.Type));
4129 SwitchType = new_expr.Type;
4131 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4132 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4136 if (!CheckSwitch (ec))
4139 Switch old_switch = ec.Switch;
4141 ec.Switch.SwitchType = SwitchType;
4143 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4145 var constant = new_expr as Constant;
4146 if (constant != null) {
4148 SwitchLabel label = FindLabel (constant);
4150 constant_section = FindSection (label);
4152 if (constant_section == null)
4153 constant_section = default_section;
4156 // Store switch expression for comparission purposes
4158 value = new_expr as VariableReference;
4160 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4165 foreach (SwitchSection ss in Sections){
4167 ec.CurrentBranching.CreateSibling (
4168 null, FlowBranching.SiblingType.SwitchSection);
4172 if (is_constant && (ss != constant_section)) {
4173 // If we're a constant switch, we're only emitting
4174 // one single section - mark all the others as
4176 ec.CurrentBranching.CurrentUsageVector.Goto ();
4177 if (!ss.Block.ResolveUnreachable (ec, true)) {
4181 if (!ss.Block.Resolve (ec))
4186 if (default_section == null)
4187 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4189 ec.EndFlowBranching ();
4190 ec.Switch = old_switch;
4196 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4197 if (string_labels.Count < 7)
4198 ResolveSimpleSwitch (ec);
4200 ResolveStringSwitchMap (ec);
4201 } else if (labels.Count < 3 && !IsNullable) {
4202 ResolveSimpleSwitch (ec);
4209 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4211 var sl = FindLabel (value);
4214 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4221 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4223 void ResolveSimpleSwitch (BlockContext bc)
4225 simple_stmt = default_section != null ? default_section.Block : null;
4227 for (int i = Sections.Count - 1; i >= 0; --i) {
4228 var s = Sections[i];
4230 if (s == default_section) {
4231 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4235 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4237 Expression cond = null;
4238 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4239 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4242 cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4249 // Compiler generated, hide from symbol file
4251 simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4254 // It's null for empty switch
4255 if (simple_stmt != null)
4256 simple_stmt.Resolve (bc);
4260 // Converts string switch into string hashtable
4262 void ResolveStringSwitchMap (ResolveContext ec)
4264 FullNamedExpression string_dictionary_type;
4265 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4266 string_dictionary_type = new TypeExpression (
4267 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4268 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4270 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4271 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4273 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4277 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4278 Field field = new Field (ctype, string_dictionary_type,
4279 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4280 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4281 if (!field.Define ())
4283 ctype.AddField (field);
4285 var init = new List<Expression> ();
4287 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4288 string value = null;
4289 foreach (SwitchSection section in Sections) {
4290 bool contains_label = false;
4291 foreach (SwitchLabel sl in section.Labels) {
4292 if (sl.IsDefault || sl.Converted.IsNull)
4295 if (!contains_label) {
4296 labels.Add (counter, sl);
4297 contains_label = true;
4300 value = (string) sl.Converted.GetValue ();
4301 var init_args = new List<Expression> (2);
4302 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4304 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4305 init_args.Add (sl.Converted);
4307 init.Add (new CollectionElementInitializer (init_args, loc));
4311 // Don't add empty sections
4317 Arguments args = new Arguments (1);
4318 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4319 Expression initializer = new NewInitialize (string_dictionary_type, args,
4320 new CollectionOrObjectInitializers (init, loc), loc);
4322 switch_cache_field = new FieldExpr (field, loc);
4323 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4326 void DoEmitStringSwitch (EmitContext ec)
4328 Label l_initialized = ec.DefineLabel ();
4331 // Skip initialization when value is null
4333 value.EmitBranchable (ec, null_target, false);
4336 // Check if string dictionary is initialized and initialize
4338 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4339 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4340 string_dictionary.EmitStatement (ec);
4342 ec.MarkLabel (l_initialized);
4344 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4346 ResolveContext rc = new ResolveContext (ec.MemberContext);
4348 if (switch_cache_field.Type.IsGeneric) {
4349 Arguments get_value_args = new Arguments (2);
4350 get_value_args.Add (new Argument (value));
4351 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4352 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4353 if (get_item == null)
4357 // A value was not found, go to default case
4359 get_item.EmitBranchable (ec, default_target, false);
4361 Arguments get_value_args = new Arguments (1);
4362 get_value_args.Add (new Argument (value));
4364 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4365 if (get_item == null)
4368 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4369 get_item_object.EmitAssign (ec, get_item, true, false);
4370 ec.Emit (OpCodes.Brfalse, default_target);
4372 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4373 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4375 get_item_int.EmitStatement (ec);
4376 get_item_object.Release (ec);
4379 EmitTableSwitch (ec, string_switch_variable);
4380 string_switch_variable.Release (ec);
4383 protected override void DoEmit (EmitContext ec)
4385 // Workaround broken flow-analysis
4386 block.HasUnreachableClosingBrace = true;
4389 // Needed to emit anonymous storey initialization
4390 // Otherwise it does not contain any statements for now
4394 default_target = ec.DefineLabel ();
4395 null_target = ec.DefineLabel ();
4398 unwrap.EmitCheck (ec);
4399 ec.Emit (OpCodes.Brfalse, null_target);
4400 value.EmitAssign (ec, new_expr, false, false);
4401 } else if (new_expr != value && !is_constant) {
4402 value.EmitAssign (ec, new_expr, false, false);
4406 // Setup the codegen context
4408 Label old_end = ec.LoopEnd;
4409 Switch old_switch = ec.Switch;
4411 ec.LoopEnd = ec.DefineLabel ();
4416 if (constant_section != null)
4417 constant_section.Block.Emit (ec);
4418 } else if (string_dictionary != null) {
4419 DoEmitStringSwitch (ec);
4420 } else if (simple_stmt != null) {
4421 simple_stmt.Emit (ec);
4423 EmitTableSwitch (ec, value);
4426 // Restore context state.
4427 ec.MarkLabel (ec.LoopEnd);
4430 // Restore the previous context
4432 ec.LoopEnd = old_end;
4433 ec.Switch = old_switch;
4436 protected override void CloneTo (CloneContext clonectx, Statement t)
4438 Switch target = (Switch) t;
4440 target.Expr = Expr.Clone (clonectx);
4441 target.Sections = new List<SwitchSection> ();
4442 foreach (SwitchSection ss in Sections){
4443 target.Sections.Add (ss.Clone (clonectx));
4447 public override object Accept (StructuralVisitor visitor)
4449 return visitor.Visit (this);
4453 // A place where execution can restart in an iterator
4454 public abstract class ResumableStatement : Statement
4457 protected Label resume_point;
4459 public Label PrepareForEmit (EmitContext ec)
4463 resume_point = ec.DefineLabel ();
4465 return resume_point;
4468 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4473 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4478 public abstract class TryFinallyBlock : ExceptionStatement
4480 protected Statement stmt;
4481 Label dispose_try_block;
4482 bool prepared_for_dispose, emitted_dispose;
4483 Method finally_host;
4485 protected TryFinallyBlock (Statement stmt, Location loc)
4493 public Statement Statement {
4501 protected abstract void EmitTryBody (EmitContext ec);
4502 public abstract void EmitFinallyBody (EmitContext ec);
4504 public override Label PrepareForDispose (EmitContext ec, Label end)
4506 if (!prepared_for_dispose) {
4507 prepared_for_dispose = true;
4508 dispose_try_block = ec.DefineLabel ();
4510 return dispose_try_block;
4513 protected sealed override void DoEmit (EmitContext ec)
4515 EmitTryBodyPrepare (ec);
4518 ec.BeginFinallyBlock ();
4520 Label start_finally = ec.DefineLabel ();
4521 if (resume_points != null) {
4522 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4524 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4525 ec.Emit (OpCodes.Brfalse_S, start_finally);
4526 ec.Emit (OpCodes.Endfinally);
4529 ec.MarkLabel (start_finally);
4531 if (finally_host != null) {
4532 var ce = new CallEmitter ();
4533 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4534 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4536 EmitFinallyBody (ec);
4539 ec.EndExceptionBlock ();
4542 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4544 if (emitted_dispose)
4547 emitted_dispose = true;
4549 Label end_of_try = ec.DefineLabel ();
4551 // Ensure that the only way we can get into this code is through a dispatcher
4552 if (have_dispatcher)
4553 ec.Emit (OpCodes.Br, end);
4555 ec.BeginExceptionBlock ();
4557 ec.MarkLabel (dispose_try_block);
4559 Label[] labels = null;
4560 for (int i = 0; i < resume_points.Count; ++i) {
4561 ResumableStatement s = resume_points[i];
4562 Label ret = s.PrepareForDispose (ec, end_of_try);
4563 if (ret.Equals (end_of_try) && labels == null)
4565 if (labels == null) {
4566 labels = new Label[resume_points.Count];
4567 for (int j = 0; j < i; ++j)
4568 labels[j] = end_of_try;
4573 if (labels != null) {
4575 for (j = 1; j < labels.Length; ++j)
4576 if (!labels[0].Equals (labels[j]))
4578 bool emit_dispatcher = j < labels.Length;
4580 if (emit_dispatcher) {
4581 ec.Emit (OpCodes.Ldloc, pc);
4582 ec.EmitInt (first_resume_pc);
4583 ec.Emit (OpCodes.Sub);
4584 ec.Emit (OpCodes.Switch, labels);
4587 foreach (ResumableStatement s in resume_points)
4588 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4591 ec.MarkLabel (end_of_try);
4593 ec.BeginFinallyBlock ();
4595 if (finally_host != null) {
4596 var ce = new CallEmitter ();
4597 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4598 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4600 EmitFinallyBody (ec);
4603 ec.EndExceptionBlock ();
4606 public override bool Resolve (BlockContext bc)
4609 // Finally block inside iterator is called from MoveNext and
4610 // Dispose methods that means we need to lift the block into
4611 // newly created host method to emit the body only once. The
4612 // original block then simply calls the newly generated method.
4614 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4615 var b = stmt as Block;
4616 if (b != null && b.Explicit.HasYield) {
4617 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4621 return base.Resolve (bc);
4626 // Base class for blocks using exception handling
4628 public abstract class ExceptionStatement : ResumableStatement
4633 protected List<ResumableStatement> resume_points;
4634 protected int first_resume_pc;
4636 protected ExceptionStatement (Location loc)
4641 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4643 StateMachineInitializer state_machine = null;
4644 if (resume_points != null) {
4645 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4647 ec.EmitInt ((int) IteratorStorey.State.Running);
4648 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4651 ec.BeginExceptionBlock ();
4653 if (resume_points != null) {
4654 ec.MarkLabel (resume_point);
4656 // For normal control flow, we want to fall-through the Switch
4657 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4658 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4659 ec.EmitInt (first_resume_pc);
4660 ec.Emit (OpCodes.Sub);
4662 Label[] labels = new Label[resume_points.Count];
4663 for (int i = 0; i < resume_points.Count; ++i)
4664 labels[i] = resume_points[i].PrepareForEmit (ec);
4665 ec.Emit (OpCodes.Switch, labels);
4669 public void SomeCodeFollows ()
4672 code_follows = true;
4676 public override bool Resolve (BlockContext ec)
4679 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4680 // So, ensure there's some IL code after this statement.
4681 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4682 ec.NeedReturnLabel ();
4687 public void AddResumePoint (ResumableStatement stmt, int pc)
4689 if (resume_points == null) {
4690 resume_points = new List<ResumableStatement> ();
4691 first_resume_pc = pc;
4694 if (pc != first_resume_pc + resume_points.Count)
4695 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4697 resume_points.Add (stmt);
4702 public class Lock : TryFinallyBlock
4705 TemporaryVariableReference expr_copy;
4706 TemporaryVariableReference lock_taken;
4708 public Lock (Expression expr, Statement stmt, Location loc)
4714 public Expression Expr {
4720 public override bool Resolve (BlockContext ec)
4722 expr = expr.Resolve (ec);
4726 if (!TypeSpec.IsReferenceType (expr.Type)) {
4727 ec.Report.Error (185, loc,
4728 "`{0}' is not a reference type as required by the lock statement",
4729 expr.Type.GetSignatureForError ());
4732 if (expr.Type.IsGenericParameter) {
4733 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4736 VariableReference lv = expr as VariableReference;
4739 locked = lv.IsLockedByStatement;
4740 lv.IsLockedByStatement = true;
4746 using (ec.Set (ResolveContext.Options.LockScope)) {
4747 ec.StartFlowBranching (this);
4748 Statement.Resolve (ec);
4749 ec.EndFlowBranching ();
4753 lv.IsLockedByStatement = locked;
4759 // Have to keep original lock value around to unlock same location
4760 // in the case the original has changed or is null
4762 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4763 expr_copy.Resolve (ec);
4766 // Ensure Monitor methods are available
4768 if (ResolvePredefinedMethods (ec) > 1) {
4769 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4770 lock_taken.Resolve (ec);
4776 protected override void EmitTryBodyPrepare (EmitContext ec)
4778 expr_copy.EmitAssign (ec, expr);
4780 if (lock_taken != null) {
4782 // Initialize ref variable
4784 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4787 // Monitor.Enter (expr_copy)
4789 expr_copy.Emit (ec);
4790 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4793 base.EmitTryBodyPrepare (ec);
4796 protected override void EmitTryBody (EmitContext ec)
4799 // Monitor.Enter (expr_copy, ref lock_taken)
4801 if (lock_taken != null) {
4802 expr_copy.Emit (ec);
4803 lock_taken.LocalInfo.CreateBuilder (ec);
4804 lock_taken.AddressOf (ec, AddressOp.Load);
4805 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4808 Statement.Emit (ec);
4811 public override void EmitFinallyBody (EmitContext ec)
4814 // if (lock_taken) Monitor.Exit (expr_copy)
4816 Label skip = ec.DefineLabel ();
4818 if (lock_taken != null) {
4819 lock_taken.Emit (ec);
4820 ec.Emit (OpCodes.Brfalse_S, skip);
4823 expr_copy.Emit (ec);
4824 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4826 ec.Emit (OpCodes.Call, m);
4828 ec.MarkLabel (skip);
4831 int ResolvePredefinedMethods (ResolveContext rc)
4833 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4834 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4838 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4842 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4846 protected override void CloneTo (CloneContext clonectx, Statement t)
4848 Lock target = (Lock) t;
4850 target.expr = expr.Clone (clonectx);
4851 target.stmt = Statement.Clone (clonectx);
4854 public override object Accept (StructuralVisitor visitor)
4856 return visitor.Visit (this);
4861 public class Unchecked : Statement {
4864 public Unchecked (Block b, Location loc)
4871 public override bool Resolve (BlockContext ec)
4873 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4874 return Block.Resolve (ec);
4877 protected override void DoEmit (EmitContext ec)
4879 using (ec.With (EmitContext.Options.CheckedScope, false))
4883 protected override void CloneTo (CloneContext clonectx, Statement t)
4885 Unchecked target = (Unchecked) t;
4887 target.Block = clonectx.LookupBlock (Block);
4890 public override object Accept (StructuralVisitor visitor)
4892 return visitor.Visit (this);
4896 public class Checked : Statement {
4899 public Checked (Block b, Location loc)
4902 b.Unchecked = false;
4906 public override bool Resolve (BlockContext ec)
4908 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4909 return Block.Resolve (ec);
4912 protected override void DoEmit (EmitContext ec)
4914 using (ec.With (EmitContext.Options.CheckedScope, true))
4918 protected override void CloneTo (CloneContext clonectx, Statement t)
4920 Checked target = (Checked) t;
4922 target.Block = clonectx.LookupBlock (Block);
4925 public override object Accept (StructuralVisitor visitor)
4927 return visitor.Visit (this);
4931 public class Unsafe : Statement {
4934 public Unsafe (Block b, Location loc)
4937 Block.Unsafe = true;
4941 public override bool Resolve (BlockContext ec)
4943 if (ec.CurrentIterator != null)
4944 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4946 using (ec.Set (ResolveContext.Options.UnsafeScope))
4947 return Block.Resolve (ec);
4950 protected override void DoEmit (EmitContext ec)
4955 protected override void CloneTo (CloneContext clonectx, Statement t)
4957 Unsafe target = (Unsafe) t;
4959 target.Block = clonectx.LookupBlock (Block);
4962 public override object Accept (StructuralVisitor visitor)
4964 return visitor.Visit (this);
4971 public class Fixed : Statement
4973 abstract class Emitter : ShimExpression
4975 protected LocalVariable vi;
4977 protected Emitter (Expression expr, LocalVariable li)
4983 public abstract void EmitExit (EmitContext ec);
4986 class ExpressionEmitter : Emitter {
4987 public ExpressionEmitter (Expression converted, LocalVariable li) :
4988 base (converted, li)
4992 protected override Expression DoResolve (ResolveContext rc)
4994 throw new NotImplementedException ();
4997 public override void Emit (EmitContext ec) {
4999 // Store pointer in pinned location
5005 public override void EmitExit (EmitContext ec)
5008 ec.Emit (OpCodes.Conv_U);
5013 class StringEmitter : Emitter
5015 LocalVariable pinned_string;
5017 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5022 protected override Expression DoResolve (ResolveContext rc)
5024 pinned_string = new LocalVariable (vi.Block, "$pinned",
5025 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5027 pinned_string.Type = rc.BuiltinTypes.String;
5029 eclass = ExprClass.Variable;
5030 type = rc.BuiltinTypes.Int;
5034 public override void Emit (EmitContext ec)
5036 pinned_string.CreateBuilder (ec);
5039 pinned_string.EmitAssign (ec);
5041 // TODO: Should use Binary::Add
5042 pinned_string.Emit (ec);
5043 ec.Emit (OpCodes.Conv_I);
5045 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5049 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5050 //pe.InstanceExpression = pinned_string;
5051 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5053 ec.Emit (OpCodes.Add);
5057 public override void EmitExit (EmitContext ec)
5060 pinned_string.EmitAssign (ec);
5064 public class VariableDeclaration : BlockVariableDeclaration
5066 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5071 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5073 if (!Variable.Type.IsPointer && li == Variable) {
5074 bc.Report.Error (209, TypeExpression.Location,
5075 "The type of locals declared in a fixed statement must be a pointer type");
5080 // The rules for the possible declarators are pretty wise,
5081 // but the production on the grammar is more concise.
5083 // So we have to enforce these rules here.
5085 // We do not resolve before doing the case 1 test,
5086 // because the grammar is explicit in that the token &
5087 // is present, so we need to test for this particular case.
5090 if (initializer is Cast) {
5091 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5095 initializer = initializer.Resolve (bc);
5097 if (initializer == null)
5103 if (initializer.Type.IsArray) {
5104 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5107 // Provided that array_type is unmanaged,
5109 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5113 // and T* is implicitly convertible to the
5114 // pointer type given in the fixed statement.
5116 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5118 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5119 if (converted == null)
5123 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5125 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5126 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
5127 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
5128 new NullLiteral (loc),
5131 converted = converted.Resolve (bc);
5133 return new ExpressionEmitter (converted, li);
5139 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5140 return new StringEmitter (initializer, li, loc).Resolve (bc);
5143 // Case 3: fixed buffer
5144 if (initializer is FixedBufferPtr) {
5145 return new ExpressionEmitter (initializer, li);
5149 // Case 4: & object.
5151 bool already_fixed = true;
5152 Unary u = initializer as Unary;
5153 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5154 IVariableReference vr = u.Expr as IVariableReference;
5155 if (vr == null || !vr.IsFixed) {
5156 already_fixed = false;
5160 if (already_fixed) {
5161 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5164 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5165 return new ExpressionEmitter (initializer, li);
5170 VariableDeclaration decl;
5171 Statement statement;
5174 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5183 public Statement Statement {
5189 public BlockVariableDeclaration Variables {
5197 public override bool Resolve (BlockContext ec)
5199 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5200 if (!decl.Resolve (ec))
5204 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5205 bool ok = statement.Resolve (ec);
5206 bool flow_unreachable = ec.EndFlowBranching ();
5207 has_ret = flow_unreachable;
5212 protected override void DoEmit (EmitContext ec)
5214 decl.Variable.CreateBuilder (ec);
5215 decl.Initializer.Emit (ec);
5216 if (decl.Declarators != null) {
5217 foreach (var d in decl.Declarators) {
5218 d.Variable.CreateBuilder (ec);
5219 d.Initializer.Emit (ec);
5223 statement.Emit (ec);
5229 // Clear the pinned variable
5231 ((Emitter) decl.Initializer).EmitExit (ec);
5232 if (decl.Declarators != null) {
5233 foreach (var d in decl.Declarators) {
5234 ((Emitter)d.Initializer).EmitExit (ec);
5239 protected override void CloneTo (CloneContext clonectx, Statement t)
5241 Fixed target = (Fixed) t;
5243 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5244 target.statement = statement.Clone (clonectx);
5247 public override object Accept (StructuralVisitor visitor)
5249 return visitor.Visit (this);
5253 public class Catch : Statement
5257 FullNamedExpression type_expr;
5258 CompilerAssign assign;
5261 public Catch (Block block, Location loc)
5269 public Block Block {
5275 public TypeSpec CatchType {
5281 public bool IsGeneral {
5283 return type_expr == null;
5287 public FullNamedExpression TypeExpression {
5296 public LocalVariable Variable {
5307 protected override void DoEmit (EmitContext ec)
5310 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5312 ec.BeginCatchBlock (CatchType);
5315 li.CreateBuilder (ec);
5318 // Special case hoisted catch variable, we have to use a temporary variable
5319 // to pass via anonymous storey initialization with the value still on top
5322 if (li.HoistedVariant != null) {
5323 LocalTemporary lt = new LocalTemporary (li.Type);
5326 // switch to assigning from the temporary variable and not from top of the stack
5327 assign.UpdateSource (lt);
5330 ec.Emit (OpCodes.Pop);
5336 public override bool Resolve (BlockContext ec)
5338 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5339 if (type_expr != null) {
5340 type = type_expr.ResolveAsType (ec);
5344 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5345 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5346 } else if (li != null) {
5348 li.PrepareForFlowAnalysis (ec);
5350 // source variable is at the top of the stack
5351 Expression source = new EmptyExpression (li.Type);
5352 if (li.Type.IsGenericParameter)
5353 source = new UnboxCast (source, li.Type);
5356 // Uses Location.Null to hide from symbol file
5358 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5359 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5363 return Block.Resolve (ec);
5367 protected override void CloneTo (CloneContext clonectx, Statement t)
5369 Catch target = (Catch) t;
5371 if (type_expr != null)
5372 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5374 target.block = clonectx.LookupBlock (block);
5378 public class TryFinally : TryFinallyBlock
5382 public TryFinally (Statement stmt, Block fini, Location loc)
5388 public Block Finallyblock {
5394 public override bool Resolve (BlockContext ec)
5398 ec.StartFlowBranching (this);
5400 if (!stmt.Resolve (ec))
5404 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5406 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5407 if (!fini.Resolve (ec))
5411 ec.EndFlowBranching ();
5413 ok &= base.Resolve (ec);
5418 protected override void EmitTryBody (EmitContext ec)
5423 public override void EmitFinallyBody (EmitContext ec)
5428 protected override void CloneTo (CloneContext clonectx, Statement t)
5430 TryFinally target = (TryFinally) t;
5432 target.stmt = (Statement) stmt.Clone (clonectx);
5434 target.fini = clonectx.LookupBlock (fini);
5437 public override object Accept (StructuralVisitor visitor)
5439 return visitor.Visit (this);
5443 public class TryCatch : ExceptionStatement
5446 List<Catch> clauses;
5447 readonly bool inside_try_finally;
5449 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5453 this.clauses = catch_clauses;
5454 this.inside_try_finally = inside_try_finally;
5457 public List<Catch> Clauses {
5463 public bool IsTryCatchFinally {
5465 return inside_try_finally;
5469 public override bool Resolve (BlockContext ec)
5473 ec.StartFlowBranching (this);
5475 if (!Block.Resolve (ec))
5478 for (int i = 0; i < clauses.Count; ++i) {
5480 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5482 if (!c.Resolve (ec)) {
5487 TypeSpec resolved_type = c.CatchType;
5488 for (int ii = 0; ii < clauses.Count; ++ii) {
5492 if (clauses[ii].IsGeneral) {
5493 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5496 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5499 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5502 ec.Report.Warning (1058, 1, c.loc,
5503 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5511 var ct = clauses[ii].CatchType;
5515 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5516 ec.Report.Error (160, c.loc,
5517 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5518 ct.GetSignatureForError ());
5524 ec.EndFlowBranching ();
5526 return base.Resolve (ec) && ok;
5529 protected sealed override void DoEmit (EmitContext ec)
5531 if (!inside_try_finally)
5532 EmitTryBodyPrepare (ec);
5536 foreach (Catch c in clauses)
5539 if (!inside_try_finally)
5540 ec.EndExceptionBlock ();
5543 protected override void CloneTo (CloneContext clonectx, Statement t)
5545 TryCatch target = (TryCatch) t;
5547 target.Block = clonectx.LookupBlock (Block);
5548 if (clauses != null){
5549 target.clauses = new List<Catch> ();
5550 foreach (Catch c in clauses)
5551 target.clauses.Add ((Catch) c.Clone (clonectx));
5555 public override object Accept (StructuralVisitor visitor)
5557 return visitor.Visit (this);
5561 public class Using : TryFinallyBlock
5563 public class VariableDeclaration : BlockVariableDeclaration
5565 Statement dispose_call;
5567 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5572 public VariableDeclaration (LocalVariable li, Location loc)
5578 public VariableDeclaration (Expression expr)
5581 loc = expr.Location;
5587 public bool IsNested { get; private set; }
5591 public void EmitDispose (EmitContext ec)
5593 dispose_call.Emit (ec);
5596 public override bool Resolve (BlockContext bc)
5601 return base.Resolve (bc, false);
5604 public Expression ResolveExpression (BlockContext bc)
5606 var e = Initializer.Resolve (bc);
5610 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5611 Initializer = ResolveInitializer (bc, Variable, e);
5615 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5617 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5618 initializer = initializer.Resolve (bc);
5619 if (initializer == null)
5622 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5623 Arguments args = new Arguments (1);
5624 args.Add (new Argument (initializer));
5625 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5626 if (initializer == null)
5629 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5630 dispose_call = CreateDisposeCall (bc, var);
5631 dispose_call.Resolve (bc);
5633 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5636 if (li == Variable) {
5637 CheckIDiposableConversion (bc, li, initializer);
5638 dispose_call = CreateDisposeCall (bc, li);
5639 dispose_call.Resolve (bc);
5642 return base.ResolveInitializer (bc, li, initializer);
5645 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5649 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5650 if (type.IsNullableType) {
5651 // it's handled in CreateDisposeCall
5655 bc.Report.SymbolRelatedToPreviousError (type);
5656 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5657 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5658 type.GetSignatureForError ());
5664 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5666 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5668 var loc = lv.Location;
5670 var idt = bc.BuiltinTypes.IDisposable;
5671 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5673 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5674 dispose_mg.InstanceExpression = type.IsNullableType ?
5675 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5679 // Hide it from symbol file via null location
5681 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5683 // Add conditional call when disposing possible null variable
5684 if (!type.IsStruct || type.IsNullableType)
5685 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, dispose.loc);
5690 public void ResolveDeclaratorInitializer (BlockContext bc)
5692 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5695 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5697 for (int i = declarators.Count - 1; i >= 0; --i) {
5698 var d = declarators [i];
5699 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5700 vd.Initializer = d.Initializer;
5702 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5703 vd.dispose_call.Resolve (bc);
5705 stmt = new Using (vd, stmt, d.Variable.Location);
5712 public override object Accept (StructuralVisitor visitor)
5714 return visitor.Visit (this);
5718 VariableDeclaration decl;
5720 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5726 public Using (Expression expr, Statement stmt, Location loc)
5729 this.decl = new VariableDeclaration (expr);
5734 public Expression Expr {
5736 return decl.Variable == null ? decl.Initializer : null;
5740 public BlockVariableDeclaration Variables {
5748 public override void Emit (EmitContext ec)
5751 // Don't emit sequence point it will be set on variable declaration
5756 protected override void EmitTryBodyPrepare (EmitContext ec)
5759 base.EmitTryBodyPrepare (ec);
5762 protected override void EmitTryBody (EmitContext ec)
5767 public override void EmitFinallyBody (EmitContext ec)
5769 decl.EmitDispose (ec);
5772 public override bool Resolve (BlockContext ec)
5774 VariableReference vr;
5775 bool vr_locked = false;
5777 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5778 if (decl.Variable == null) {
5779 vr = decl.ResolveExpression (ec) as VariableReference;
5781 vr_locked = vr.IsLockedByStatement;
5782 vr.IsLockedByStatement = true;
5785 if (decl.IsNested) {
5786 decl.ResolveDeclaratorInitializer (ec);
5788 if (!decl.Resolve (ec))
5791 if (decl.Declarators != null) {
5792 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5800 ec.StartFlowBranching (this);
5804 ec.EndFlowBranching ();
5807 vr.IsLockedByStatement = vr_locked;
5814 protected override void CloneTo (CloneContext clonectx, Statement t)
5816 Using target = (Using) t;
5818 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5819 target.stmt = stmt.Clone (clonectx);
5822 public override object Accept (StructuralVisitor visitor)
5824 return visitor.Visit (this);
5829 /// Implementation of the foreach C# statement
5831 public class Foreach : Statement
5833 abstract class IteratorStatement : Statement
5835 protected readonly Foreach for_each;
5837 protected IteratorStatement (Foreach @foreach)
5839 this.for_each = @foreach;
5840 this.loc = @foreach.expr.Location;
5843 protected override void CloneTo (CloneContext clonectx, Statement target)
5845 throw new NotImplementedException ();
5848 public override void Emit (EmitContext ec)
5850 if (ec.EmitAccurateDebugInfo) {
5851 ec.Emit (OpCodes.Nop);
5858 sealed class ArrayForeach : IteratorStatement
5860 TemporaryVariableReference[] lengths;
5861 Expression [] length_exprs;
5862 StatementExpression[] counter;
5863 TemporaryVariableReference[] variables;
5865 TemporaryVariableReference copy;
5867 public ArrayForeach (Foreach @foreach, int rank)
5870 counter = new StatementExpression[rank];
5871 variables = new TemporaryVariableReference[rank];
5872 length_exprs = new Expression [rank];
5875 // Only use temporary length variables when dealing with
5876 // multi-dimensional arrays
5879 lengths = new TemporaryVariableReference [rank];
5882 public override bool Resolve (BlockContext ec)
5884 Block variables_block = for_each.variable.Block;
5885 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5888 int rank = length_exprs.Length;
5889 Arguments list = new Arguments (rank);
5890 for (int i = 0; i < rank; i++) {
5891 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5893 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5894 counter[i].Resolve (ec);
5897 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5899 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5900 lengths[i].Resolve (ec);
5902 Arguments args = new Arguments (1);
5903 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5904 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5907 list.Add (new Argument (v));
5910 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5915 if (for_each.type is VarExpr) {
5916 // Infer implicitly typed local variable from foreach array type
5917 var_type = access.Type;
5919 var_type = for_each.type.ResolveAsType (ec);
5921 if (var_type == null)
5924 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5929 for_each.variable.Type = var_type;
5931 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5932 if (variable_ref == null)
5935 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.variable.Location));
5939 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5940 ec.CurrentBranching.CreateSibling ();
5942 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5943 if (!for_each.body.Resolve (ec))
5945 ec.EndFlowBranching ();
5947 // There's no direct control flow from the end of the embedded statement to the end of the loop
5948 ec.CurrentBranching.CurrentUsageVector.Goto ();
5950 ec.EndFlowBranching ();
5955 protected override void DoEmit (EmitContext ec)
5957 copy.EmitAssign (ec, for_each.expr);
5959 int rank = length_exprs.Length;
5960 Label[] test = new Label [rank];
5961 Label[] loop = new Label [rank];
5963 for (int i = 0; i < rank; i++) {
5964 test [i] = ec.DefineLabel ();
5965 loop [i] = ec.DefineLabel ();
5967 if (lengths != null)
5968 lengths [i].EmitAssign (ec, length_exprs [i]);
5971 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5972 for (int i = 0; i < rank; i++) {
5973 variables [i].EmitAssign (ec, zero);
5975 ec.Emit (OpCodes.Br, test [i]);
5976 ec.MarkLabel (loop [i]);
5979 for_each.body.Emit (ec);
5981 ec.MarkLabel (ec.LoopBegin);
5982 ec.Mark (for_each.expr.Location);
5984 for (int i = rank - 1; i >= 0; i--){
5985 counter [i].Emit (ec);
5987 ec.MarkLabel (test [i]);
5988 variables [i].Emit (ec);
5990 if (lengths != null)
5991 lengths [i].Emit (ec);
5993 length_exprs [i].Emit (ec);
5995 ec.Emit (OpCodes.Blt, loop [i]);
5998 ec.MarkLabel (ec.LoopEnd);
6002 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6004 class RuntimeDispose : Using.VariableDeclaration
6006 public RuntimeDispose (LocalVariable lv, Location loc)
6011 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6013 // Defered to runtime check
6016 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6018 var idt = bc.BuiltinTypes.IDisposable;
6021 // Fabricates code like
6023 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6026 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6028 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6029 dispose_variable.CreateReferenceExpression (bc, loc),
6030 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6031 loc), new NullLiteral (loc), loc);
6033 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6035 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6036 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6038 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6039 return new If (idisaposable_test, dispose, loc);
6043 LocalVariable variable;
6045 Statement statement;
6046 ExpressionStatement init;
6047 TemporaryVariableReference enumerator_variable;
6048 bool ambiguous_getenumerator_name;
6050 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6053 this.variable = var;
6057 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6059 rc.Report.SymbolRelatedToPreviousError (enumerator);
6060 rc.Report.Error (202, loc,
6061 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6062 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6065 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6068 // Option 1: Try to match by name GetEnumerator first
6070 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6071 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6073 var mg = mexpr as MethodGroupExpr;
6075 mg.InstanceExpression = expr;
6076 Arguments args = new Arguments (0);
6077 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
6079 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6080 if (ambiguous_getenumerator_name)
6083 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6089 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6092 PredefinedMember<MethodSpec> iface_candidate = null;
6093 var ptypes = rc.Module.PredefinedTypes;
6094 var gen_ienumerable = ptypes.IEnumerableGeneric;
6095 if (!gen_ienumerable.Define ())
6096 gen_ienumerable = null;
6099 var ifaces = t.Interfaces;
6100 if (ifaces != null) {
6101 foreach (var iface in ifaces) {
6102 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6103 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6104 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6105 rc.Report.Error (1640, loc,
6106 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6107 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6112 // TODO: Cache this somehow
6113 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6114 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6119 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6120 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6125 if (t.IsGenericParameter)
6130 } while (t != null);
6132 if (iface_candidate == null) {
6133 if (expr.Type != InternalType.ErrorType) {
6134 rc.Report.Error (1579, loc,
6135 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6136 expr.Type.GetSignatureForError (), "GetEnumerator");
6142 var method = iface_candidate.Resolve (loc);
6146 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6147 mg.InstanceExpression = expr;
6151 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6153 var ms = MemberCache.FindMember (enumerator.ReturnType,
6154 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6155 BindingRestriction.InstanceOnly) as MethodSpec;
6157 if (ms == null || !ms.IsPublic) {
6158 Error_WrongEnumerator (rc, enumerator);
6162 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6165 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6167 var ps = MemberCache.FindMember (enumerator.ReturnType,
6168 MemberFilter.Property ("Current", null),
6169 BindingRestriction.InstanceOnly) as PropertySpec;
6171 if (ps == null || !ps.IsPublic) {
6172 Error_WrongEnumerator (rc, enumerator);
6179 public override bool Resolve (BlockContext ec)
6181 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6184 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6185 } else if (expr.Type.IsNullableType) {
6186 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6189 var get_enumerator_mg = ResolveGetEnumerator (ec);
6190 if (get_enumerator_mg == null) {
6194 var get_enumerator = get_enumerator_mg.BestCandidate;
6195 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6196 enumerator_variable.Resolve (ec);
6198 // Prepare bool MoveNext ()
6199 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6200 if (move_next_mg == null) {
6204 move_next_mg.InstanceExpression = enumerator_variable;
6206 // Prepare ~T~ Current { get; }
6207 var current_prop = ResolveCurrent (ec, get_enumerator);
6208 if (current_prop == null) {
6212 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6213 if (current_pe == null)
6216 VarExpr ve = for_each.type as VarExpr;
6220 // Source type is dynamic, set element type to dynamic too
6221 variable.Type = ec.BuiltinTypes.Dynamic;
6223 // Infer implicitly typed local variable from foreach enumerable type
6224 variable.Type = current_pe.Type;
6228 // Explicit cast of dynamic collection elements has to be done at runtime
6229 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6232 variable.Type = for_each.type.ResolveAsType (ec);
6234 if (variable.Type == null)
6237 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6238 if (current_pe == null)
6242 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6243 if (variable_ref == null)
6246 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), variable.Location));
6248 var init = new Invocation (get_enumerator_mg, null);
6250 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6251 for_each.body, Location.Null);
6253 var enum_type = enumerator_variable.Type;
6256 // Add Dispose method call when enumerator can be IDisposable
6258 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6259 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6261 // Runtime Dispose check
6263 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6264 vd.Initializer = init;
6265 statement = new Using (vd, statement, Location.Null);
6268 // No Dispose call needed
6270 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6271 this.init.Resolve (ec);
6275 // Static Dispose check
6277 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6278 vd.Initializer = init;
6279 statement = new Using (vd, statement, Location.Null);
6282 return statement.Resolve (ec);
6285 protected override void DoEmit (EmitContext ec)
6287 enumerator_variable.LocalInfo.CreateBuilder (ec);
6290 init.EmitStatement (ec);
6292 statement.Emit (ec);
6295 #region IErrorHandler Members
6297 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6299 ec.Report.SymbolRelatedToPreviousError (best);
6300 ec.Report.Warning (278, 2, expr.Location,
6301 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6302 expr.Type.GetSignatureForError (), "enumerable",
6303 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6305 ambiguous_getenumerator_name = true;
6309 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6314 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6319 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6328 LocalVariable variable;
6330 Statement statement;
6333 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6336 this.variable = var;
6338 this.statement = stmt;
6343 public Expression Expr {
6344 get { return expr; }
6347 public Statement Statement {
6348 get { return statement; }
6351 public Expression TypeExpression {
6352 get { return type; }
6355 public LocalVariable Variable {
6356 get { return variable; }
6359 public override bool Resolve (BlockContext ec)
6361 expr = expr.Resolve (ec);
6366 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6370 body.AddStatement (statement);
6372 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6373 statement = new ArrayForeach (this, 1);
6374 } else if (expr.Type is ArrayContainer) {
6375 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6377 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6378 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6379 expr.ExprClassName);
6383 statement = new CollectionForeach (this, variable, expr);
6386 return statement.Resolve (ec);
6389 protected override void DoEmit (EmitContext ec)
6391 variable.CreateBuilder (ec);
6393 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6394 ec.LoopBegin = ec.DefineLabel ();
6395 ec.LoopEnd = ec.DefineLabel ();
6397 statement.Emit (ec);
6399 ec.LoopBegin = old_begin;
6400 ec.LoopEnd = old_end;
6403 protected override void CloneTo (CloneContext clonectx, Statement t)
6405 Foreach target = (Foreach) t;
6407 target.type = type.Clone (clonectx);
6408 target.expr = expr.Clone (clonectx);
6409 target.body = (Block) body.Clone (clonectx);
6410 target.statement = statement.Clone (clonectx);
6413 public override object Accept (StructuralVisitor visitor)
6415 return visitor.Visit (this);