[LoongArch64] Part-5:add loongarch support in some files for LoongArch64. (#21769)
[mono-project.git] / mcs / mcs / statement.cs
blobc8b77c1adc11d47aabcf6bb0564ca12f3461f35f
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Authors:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
14 using System;
15 using System.Collections.Generic;
17 #if STATIC
18 using IKVM.Reflection.Emit;
19 #else
20 using System.Reflection.Emit;
21 #endif
23 namespace Mono.CSharp {
25 public abstract class Statement {
26 public Location loc;
27 protected bool reachable;
29 public bool IsUnreachable {
30 get {
31 return !reachable;
35 /// <summary>
36 /// Resolves the statement, true means that all sub-statements
37 /// did resolve ok.
38 /// </summary>
39 public virtual bool Resolve (BlockContext bc)
41 return true;
44 /// <summary>
45 /// Return value indicates whether all code paths emitted return.
46 /// </summary>
47 protected abstract void DoEmit (EmitContext ec);
49 public virtual void Emit (EmitContext ec)
51 ec.Mark (loc);
52 DoEmit (ec);
54 if (ec.StatementEpilogue != null) {
55 ec.EmitEpilogue ();
60 // This routine must be overrided in derived classes and make copies
61 // of all the data that might be modified if resolved
62 //
63 protected abstract void CloneTo (CloneContext clonectx, Statement target);
65 public Statement Clone (CloneContext clonectx)
67 Statement s = (Statement) this.MemberwiseClone ();
68 CloneTo (clonectx, s);
69 return s;
72 public virtual Expression CreateExpressionTree (ResolveContext ec)
74 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
75 return null;
78 public virtual object Accept (StructuralVisitor visitor)
80 return visitor.Visit (this);
84 // Return value indicates whether statement has unreachable end
86 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
88 public bool FlowAnalysis (FlowAnalysisContext fc)
90 if (reachable) {
91 fc.UnreachableReported = false;
92 var res = DoFlowAnalysis (fc);
93 return res;
97 // Special handling cases
99 if (this is Block) {
100 return DoFlowAnalysis (fc);
103 if (this is EmptyStatement || loc.IsNull)
104 return true;
106 if (fc.UnreachableReported)
107 return true;
109 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
110 fc.UnreachableReported = true;
111 return true;
114 public virtual Reachability MarkReachable (Reachability rc)
116 if (!rc.IsUnreachable)
117 reachable = true;
119 return rc;
122 protected void CheckExitBoundaries (BlockContext bc, Block scope)
124 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
125 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
126 return;
129 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
130 if (b.IsFinallyBlock) {
131 Error_FinallyClauseExit (bc);
132 break;
137 protected void Error_FinallyClauseExit (BlockContext bc)
139 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
143 public sealed class EmptyStatement : Statement
145 public EmptyStatement (Location loc)
147 this.loc = loc;
150 public override bool Resolve (BlockContext ec)
152 return true;
155 public override void Emit (EmitContext ec)
159 protected override void DoEmit (EmitContext ec)
161 throw new NotSupportedException ();
164 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
166 return false;
169 protected override void CloneTo (CloneContext clonectx, Statement target)
171 // nothing needed.
174 public override object Accept (StructuralVisitor visitor)
176 return visitor.Visit (this);
180 public class If : Statement {
181 Expression expr;
182 public Statement TrueStatement;
183 public Statement FalseStatement;
185 bool true_returns, false_returns;
187 public If (Expression bool_expr, Statement true_statement, Location l)
188 : this (bool_expr, true_statement, null, l)
192 public If (Expression bool_expr,
193 Statement true_statement,
194 Statement false_statement,
195 Location l)
197 this.expr = bool_expr;
198 TrueStatement = true_statement;
199 FalseStatement = false_statement;
200 loc = l;
203 public Expression Expr {
204 get {
205 return this.expr;
209 public override bool Resolve (BlockContext ec)
211 expr = expr.Resolve (ec);
213 var ok = TrueStatement.Resolve (ec);
215 if (FalseStatement != null) {
216 ok &= FalseStatement.Resolve (ec);
219 return ok;
222 protected override void DoEmit (EmitContext ec)
224 Label false_target = ec.DefineLabel ();
225 Label end;
228 // If we're a boolean constant, Resolve() already
229 // eliminated dead code for us.
231 Constant c = expr as Constant;
232 if (c != null){
233 c.EmitSideEffect (ec);
235 if (!c.IsDefaultValue)
236 TrueStatement.Emit (ec);
237 else if (FalseStatement != null)
238 FalseStatement.Emit (ec);
240 return;
243 expr.EmitBranchable (ec, false_target, false);
245 TrueStatement.Emit (ec);
247 if (FalseStatement != null){
248 bool branch_emitted = false;
250 end = ec.DefineLabel ();
251 if (!true_returns){
252 ec.Emit (OpCodes.Br, end);
253 branch_emitted = true;
256 ec.MarkLabel (false_target);
257 FalseStatement.Emit (ec);
259 if (branch_emitted)
260 ec.MarkLabel (end);
261 } else {
262 ec.MarkLabel (false_target);
266 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
268 expr.FlowAnalysisConditional (fc);
270 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
272 fc.BranchDefiniteAssignment (fc.DefiniteAssignmentOnTrue);
273 var labels = fc.CopyLabelStack ();
275 var res = TrueStatement.FlowAnalysis (fc);
277 fc.SetLabelStack (labels);
279 if (FalseStatement == null) {
281 var c = expr as Constant;
282 if (c != null && !c.IsDefaultValue)
283 return true_returns;
285 if (true_returns)
286 fc.DefiniteAssignment = da_false;
287 else
288 fc.DefiniteAssignment &= da_false;
290 return false;
293 if (true_returns) {
294 fc.DefiniteAssignment = da_false;
296 res = FalseStatement.FlowAnalysis (fc);
297 fc.SetLabelStack (labels);
298 return res;
301 var da_true = fc.DefiniteAssignment;
303 fc.DefiniteAssignment = da_false;
305 res &= FalseStatement.FlowAnalysis (fc);
307 fc.SetLabelStack (labels);
309 if (!TrueStatement.IsUnreachable) {
310 if (false_returns || FalseStatement.IsUnreachable)
311 fc.DefiniteAssignment = da_true;
312 else
313 fc.DefiniteAssignment &= da_true;
316 return res;
319 public override Reachability MarkReachable (Reachability rc)
321 if (rc.IsUnreachable)
322 return rc;
324 base.MarkReachable (rc);
326 var c = expr as Constant;
327 if (c != null) {
328 bool take = !c.IsDefaultValue;
329 if (take) {
330 rc = TrueStatement.MarkReachable (rc);
331 } else {
332 if (FalseStatement != null)
333 rc = FalseStatement.MarkReachable (rc);
336 return rc;
339 var true_rc = TrueStatement.MarkReachable (rc);
340 true_returns = true_rc.IsUnreachable;
342 if (FalseStatement == null)
343 return rc;
345 var false_rc = FalseStatement.MarkReachable (rc);
346 false_returns = false_rc.IsUnreachable;
348 return true_rc & false_rc;
351 protected override void CloneTo (CloneContext clonectx, Statement t)
353 If target = (If) t;
355 target.expr = expr.Clone (clonectx);
356 target.TrueStatement = TrueStatement.Clone (clonectx);
357 if (FalseStatement != null)
358 target.FalseStatement = FalseStatement.Clone (clonectx);
361 public override object Accept (StructuralVisitor visitor)
363 return visitor.Visit (this);
367 public class Do : LoopStatement
369 public Expression expr;
370 bool iterator_reachable, end_reachable;
372 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
373 : base (statement)
375 expr = bool_expr;
376 loc = doLocation;
377 WhileLocation = whileLocation;
380 public Location WhileLocation {
381 get; private set;
384 public override bool Resolve (BlockContext bc)
386 var ok = base.Resolve (bc);
388 expr = expr.Resolve (bc);
390 return ok;
393 protected override void DoEmit (EmitContext ec)
395 Label loop = ec.DefineLabel ();
396 Label old_begin = ec.LoopBegin;
397 Label old_end = ec.LoopEnd;
399 ec.LoopBegin = ec.DefineLabel ();
400 ec.LoopEnd = ec.DefineLabel ();
402 ec.MarkLabel (loop);
403 Statement.Emit (ec);
404 ec.MarkLabel (ec.LoopBegin);
406 // Mark start of while condition
407 ec.Mark (WhileLocation);
410 // Dead code elimination
412 if (expr is Constant) {
413 bool res = !((Constant) expr).IsDefaultValue;
415 expr.EmitSideEffect (ec);
416 if (res)
417 ec.Emit (OpCodes.Br, loop);
418 } else {
419 expr.EmitBranchable (ec, loop, true);
422 ec.MarkLabel (ec.LoopEnd);
424 ec.LoopBegin = old_begin;
425 ec.LoopEnd = old_end;
428 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
430 var res = Statement.FlowAnalysis (fc);
432 expr.FlowAnalysisConditional (fc);
434 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
436 if (res && !iterator_reachable)
437 return !end_reachable;
439 if (!end_reachable) {
440 var c = expr as Constant;
441 if (c != null && !c.IsDefaultValue)
442 return true;
445 return false;
448 public override Reachability MarkReachable (Reachability rc)
450 base.MarkReachable (rc);
452 var body_rc = Statement.MarkReachable (rc);
454 if (body_rc.IsUnreachable && !iterator_reachable) {
455 expr = new UnreachableExpression (expr);
456 return end_reachable ? rc : Reachability.CreateUnreachable ();
459 if (!end_reachable) {
460 var c = expr as Constant;
461 if (c != null && !c.IsDefaultValue)
462 return Reachability.CreateUnreachable ();
465 return rc;
468 protected override void CloneTo (CloneContext clonectx, Statement t)
470 Do target = (Do) t;
472 target.Statement = Statement.Clone (clonectx);
473 target.expr = expr.Clone (clonectx);
476 public override object Accept (StructuralVisitor visitor)
478 return visitor.Visit (this);
481 public override void SetEndReachable ()
483 end_reachable = true;
486 public override void SetIteratorReachable ()
488 iterator_reachable = true;
492 public class While : LoopStatement
494 public Expression expr;
495 bool empty, infinite, end_reachable;
496 List<DefiniteAssignmentBitSet> end_reachable_das;
498 public While (BooleanExpression bool_expr, Statement statement, Location l)
499 : base (statement)
501 this.expr = bool_expr;
502 loc = l;
505 public override bool Resolve (BlockContext bc)
507 bool ok = true;
509 expr = expr.Resolve (bc);
510 if (expr == null)
511 ok = false;
513 var c = expr as Constant;
514 if (c != null) {
515 empty = c.IsDefaultValue;
516 infinite = !empty;
519 ok &= base.Resolve (bc);
520 return ok;
523 protected override void DoEmit (EmitContext ec)
525 if (empty) {
526 expr.EmitSideEffect (ec);
527 return;
530 Label old_begin = ec.LoopBegin;
531 Label old_end = ec.LoopEnd;
533 ec.LoopBegin = ec.DefineLabel ();
534 ec.LoopEnd = ec.DefineLabel ();
537 // Inform whether we are infinite or not
539 if (expr is Constant) {
540 // expr is 'true', since the 'empty' case above handles the 'false' case
541 ec.MarkLabel (ec.LoopBegin);
543 if (ec.EmitAccurateDebugInfo)
544 ec.Emit (OpCodes.Nop);
546 expr.EmitSideEffect (ec);
547 Statement.Emit (ec);
548 ec.Emit (OpCodes.Br, ec.LoopBegin);
551 // Inform that we are infinite (ie, `we return'), only
552 // if we do not `break' inside the code.
554 ec.MarkLabel (ec.LoopEnd);
555 } else {
556 Label while_loop = ec.DefineLabel ();
558 ec.Emit (OpCodes.Br, ec.LoopBegin);
559 ec.MarkLabel (while_loop);
561 expr.EmitPrepare (ec);
563 Statement.Emit (ec);
565 ec.MarkLabel (ec.LoopBegin);
567 ec.Mark (loc);
568 expr.EmitBranchable (ec, while_loop, true);
570 ec.MarkLabel (ec.LoopEnd);
573 ec.LoopBegin = old_begin;
574 ec.LoopEnd = old_end;
577 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
579 expr.FlowAnalysisConditional (fc);
581 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
583 fc.BranchDefiniteAssignment (fc.DefiniteAssignmentOnTrue);
585 Statement.FlowAnalysis (fc);
588 // Special case infinite while with breaks
590 if (end_reachable_das != null) {
591 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
592 end_reachable_das = null;
595 fc.DefiniteAssignment = da_false;
597 if (infinite && !end_reachable)
598 return true;
600 return false;
603 public override Reachability MarkReachable (Reachability rc)
605 if (rc.IsUnreachable)
606 return rc;
608 base.MarkReachable (rc);
611 // Special case unreachable while body
613 if (empty) {
614 Statement.MarkReachable (Reachability.CreateUnreachable ());
615 return rc;
618 Statement.MarkReachable (rc);
621 // When infinite while end is unreachable via break anything what follows is unreachable too
623 if (infinite && !end_reachable)
624 return Reachability.CreateUnreachable ();
626 return rc;
629 protected override void CloneTo (CloneContext clonectx, Statement t)
631 While target = (While) t;
633 target.expr = expr.Clone (clonectx);
634 target.Statement = Statement.Clone (clonectx);
637 public override object Accept (StructuralVisitor visitor)
639 return visitor.Visit (this);
642 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
644 if (!infinite)
645 return;
647 if (end_reachable_das == null)
648 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
650 end_reachable_das.Add (fc.DefiniteAssignment);
653 public override void SetEndReachable ()
655 end_reachable = true;
659 public class For : LoopStatement
661 bool infinite, empty, iterator_reachable, end_reachable;
662 List<DefiniteAssignmentBitSet> end_reachable_das;
664 public For (Location l)
665 : base (null)
667 loc = l;
670 public Statement Initializer {
671 get; set;
674 public Expression Condition {
675 get; set;
678 public Statement Iterator {
679 get; set;
682 public override bool Resolve (BlockContext bc)
684 Initializer.Resolve (bc);
686 if (Condition != null) {
687 Condition = Condition.Resolve (bc);
688 var condition_constant = Condition as Constant;
689 if (condition_constant != null) {
690 if (condition_constant.IsDefaultValue) {
691 empty = true;
692 } else {
693 infinite = true;
696 } else {
697 infinite = true;
700 return base.Resolve (bc) && Iterator.Resolve (bc);
703 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
705 Initializer.FlowAnalysis (fc);
707 DefiniteAssignmentBitSet da_false;
708 if (Condition != null) {
709 Condition.FlowAnalysisConditional (fc);
710 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
711 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
712 } else {
713 da_false = fc.BranchDefiniteAssignment ();
716 Statement.FlowAnalysis (fc);
718 Iterator.FlowAnalysis (fc);
721 // Special case infinite for with breaks
723 if (end_reachable_das != null) {
724 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
725 end_reachable_das = null;
728 fc.DefiniteAssignment = da_false;
730 if (infinite && !end_reachable)
731 return true;
733 return false;
736 public override Reachability MarkReachable (Reachability rc)
738 base.MarkReachable (rc);
740 Initializer.MarkReachable (rc);
742 var body_rc = Statement.MarkReachable (rc);
743 if (!body_rc.IsUnreachable || iterator_reachable) {
744 Iterator.MarkReachable (rc);
748 // When infinite for end is unreachable via break anything what follows is unreachable too
750 if (infinite && !end_reachable) {
751 return Reachability.CreateUnreachable ();
754 return rc;
757 protected override void DoEmit (EmitContext ec)
759 if (Initializer != null)
760 Initializer.Emit (ec);
762 if (empty) {
763 Condition.EmitSideEffect (ec);
764 return;
767 Label old_begin = ec.LoopBegin;
768 Label old_end = ec.LoopEnd;
769 Label loop = ec.DefineLabel ();
770 Label test = ec.DefineLabel ();
772 ec.LoopBegin = ec.DefineLabel ();
773 ec.LoopEnd = ec.DefineLabel ();
775 ec.Emit (OpCodes.Br, test);
776 ec.MarkLabel (loop);
778 Condition?.EmitPrepare (ec);
779 Statement.Emit (ec);
781 ec.MarkLabel (ec.LoopBegin);
782 Iterator.Emit (ec);
784 ec.MarkLabel (test);
786 // If test is null, there is no test, and we are just
787 // an infinite loop
789 if (Condition != null) {
790 ec.Mark (Condition.Location);
793 // The Resolve code already catches the case for
794 // Test == Constant (false) so we know that
795 // this is true
797 if (Condition is Constant) {
798 Condition.EmitSideEffect (ec);
799 ec.Emit (OpCodes.Br, loop);
800 } else {
801 Condition.EmitBranchable (ec, loop, true);
804 } else
805 ec.Emit (OpCodes.Br, loop);
806 ec.MarkLabel (ec.LoopEnd);
808 ec.LoopBegin = old_begin;
809 ec.LoopEnd = old_end;
812 protected override void CloneTo (CloneContext clonectx, Statement t)
814 For target = (For) t;
816 if (Initializer != null)
817 target.Initializer = Initializer.Clone (clonectx);
818 if (Condition != null)
819 target.Condition = Condition.Clone (clonectx);
820 if (Iterator != null)
821 target.Iterator = Iterator.Clone (clonectx);
822 target.Statement = Statement.Clone (clonectx);
825 public override object Accept (StructuralVisitor visitor)
827 return visitor.Visit (this);
830 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
832 if (!infinite)
833 return;
835 if (end_reachable_das == null)
836 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
838 end_reachable_das.Add (fc.DefiniteAssignment);
841 public override void SetEndReachable ()
843 end_reachable = true;
846 public override void SetIteratorReachable ()
848 iterator_reachable = true;
852 public abstract class LoopStatement : Statement
854 protected LoopStatement (Statement statement)
856 Statement = statement;
859 public Statement Statement { get; set; }
861 public override bool Resolve (BlockContext bc)
863 var prev_loop = bc.EnclosingLoop;
864 var prev_los = bc.EnclosingLoopOrSwitch;
865 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
866 var ok = Statement.Resolve (bc);
867 bc.EnclosingLoopOrSwitch = prev_los;
868 bc.EnclosingLoop = prev_loop;
870 return ok;
874 // Needed by possibly infinite loops statements (for, while) and switch statment
876 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
880 public virtual void SetEndReachable ()
884 public virtual void SetIteratorReachable ()
889 public class StatementExpression : Statement
891 ExpressionStatement expr;
893 public StatementExpression (ExpressionStatement expr)
895 this.expr = expr;
896 loc = expr.StartLocation;
899 public StatementExpression (ExpressionStatement expr, Location loc)
901 this.expr = expr;
902 this.loc = loc;
905 public ExpressionStatement Expr {
906 get {
907 return this.expr;
911 protected override void CloneTo (CloneContext clonectx, Statement t)
913 StatementExpression target = (StatementExpression) t;
914 target.expr = (ExpressionStatement) expr.Clone (clonectx);
917 protected override void DoEmit (EmitContext ec)
919 expr.EmitStatement (ec);
922 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
924 expr.FlowAnalysis (fc);
925 return false;
928 public override Reachability MarkReachable (Reachability rc)
930 base.MarkReachable (rc);
931 return expr.MarkReachable (rc);
934 public override bool Resolve (BlockContext ec)
936 expr = expr.ResolveStatement (ec);
937 return expr != null;
940 public override object Accept (StructuralVisitor visitor)
942 return visitor.Visit (this);
946 public class StatementErrorExpression : Statement
948 Expression expr;
950 public StatementErrorExpression (Expression expr)
952 this.expr = expr;
953 this.loc = expr.StartLocation;
956 public Expression Expr {
957 get {
958 return expr;
962 public override bool Resolve (BlockContext bc)
964 expr.Error_InvalidExpressionStatement (bc);
965 return true;
968 protected override void DoEmit (EmitContext ec)
970 throw new NotSupportedException ();
973 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
975 return false;
978 protected override void CloneTo (CloneContext clonectx, Statement target)
980 var t = (StatementErrorExpression) target;
982 t.expr = expr.Clone (clonectx);
985 public override object Accept (StructuralVisitor visitor)
987 return visitor.Visit (this);
992 // Simple version of statement list not requiring a block
994 public class StatementList : Statement
996 List<Statement> statements;
998 public StatementList (Statement first, Statement second)
1000 statements = new List<Statement> { first, second };
1003 #region Properties
1004 public IList<Statement> Statements {
1005 get {
1006 return statements;
1009 #endregion
1011 public void Add (Statement statement)
1013 statements.Add (statement);
1016 public override bool Resolve (BlockContext ec)
1018 foreach (var s in statements)
1019 s.Resolve (ec);
1021 return true;
1024 protected override void DoEmit (EmitContext ec)
1026 foreach (var s in statements)
1027 s.Emit (ec);
1030 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1032 foreach (var s in statements)
1033 s.FlowAnalysis (fc);
1035 return false;
1038 public override Reachability MarkReachable (Reachability rc)
1040 base.MarkReachable (rc);
1042 Reachability res = rc;
1043 foreach (var s in statements)
1044 res = s.MarkReachable (rc);
1046 return res;
1049 protected override void CloneTo (CloneContext clonectx, Statement target)
1051 StatementList t = (StatementList) target;
1053 t.statements = new List<Statement> (statements.Count);
1054 foreach (Statement s in statements)
1055 t.statements.Add (s.Clone (clonectx));
1058 public override object Accept (StructuralVisitor visitor)
1060 return visitor.Visit (this);
1065 // For statements which require special handling when inside try or catch block
1067 public abstract class ExitStatement : Statement
1069 protected bool unwind_protect;
1071 protected abstract bool DoResolve (BlockContext bc);
1072 protected abstract bool IsLocalExit { get; }
1074 public override bool Resolve (BlockContext bc)
1076 var res = DoResolve (bc);
1078 if (!IsLocalExit) {
1080 // We are inside finally scope but is it the scope we are exiting
1082 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1084 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1085 if (b.IsFinallyBlock) {
1086 Error_FinallyClauseExit (bc);
1087 break;
1090 if (b is ParametersBlock)
1091 break;
1096 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1097 return res;
1100 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1102 if (IsLocalExit)
1103 return true;
1105 if (fc.TryFinally != null) {
1106 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1107 } else {
1108 fc.ParametersBlock.CheckControlExit (fc);
1111 return true;
1115 /// <summary>
1116 /// Implements the return statement
1117 /// </summary>
1118 public class Return : ExitStatement
1120 Expression expr;
1121 bool expr_returns;
1123 public Return (Expression expr, Location l)
1125 this.expr = expr;
1126 loc = l;
1129 #region Properties
1131 public Expression Expr {
1132 get {
1133 return expr;
1135 protected set {
1136 expr = value;
1140 protected override bool IsLocalExit {
1141 get {
1142 return false;
1146 #endregion
1148 protected override bool DoResolve (BlockContext ec)
1150 var block_return_type = ec.ReturnType;
1152 if (expr == null) {
1153 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1154 return true;
1157 // Return must not be followed by an expression when
1158 // the method return type is Task
1160 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1161 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1162 var s_return_type = storey.ReturnType;
1163 if (s_return_type == ec.Module.PredefinedTypes.Task.TypeSpec) {
1165 // Extra trick not to emit ret/leave inside awaiter body
1167 expr = EmptyExpression.Null;
1168 return true;
1171 if (s_return_type.IsGenericTask || (s_return_type.Arity == 1 && s_return_type.IsCustomTaskType ()))
1172 block_return_type = s_return_type.TypeArguments[0];
1175 if (ec.CurrentIterator != null) {
1176 Error_ReturnFromIterator (ec);
1177 } else if (block_return_type != InternalType.ErrorType) {
1178 ec.Report.Error (126, loc,
1179 "An object of a type convertible to `{0}' is required for the return statement",
1180 block_return_type.GetSignatureForError ());
1183 return false;
1186 expr = expr.Resolve (ec);
1188 AnonymousExpression am = ec.CurrentAnonymousMethod;
1189 if (am == null) {
1190 if (block_return_type.Kind == MemberKind.Void) {
1191 ec.Report.Error (127, loc,
1192 "`{0}': A return keyword must not be followed by any expression when method returns void",
1193 ec.GetSignatureForError ());
1195 return false;
1197 } else {
1198 if (am.IsIterator) {
1199 Error_ReturnFromIterator (ec);
1200 return false;
1203 var async_block = am as AsyncInitializer;
1204 if (async_block != null) {
1205 if (expr != null) {
1206 var storey = (AsyncTaskStorey) am.Storey;
1207 var async_type = storey.ReturnType;
1209 if (async_type == null && async_block.ReturnTypeInference != null) {
1210 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1211 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1212 else
1213 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1214 return true;
1217 if (async_type.Kind == MemberKind.Void) {
1218 ec.Report.Error (8030, loc,
1219 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1220 return false;
1223 if (!async_type.IsGeneric) {
1224 if (this is ContextualReturn)
1225 return true;
1227 if (async_block.DelegateType != null) {
1228 ec.Report.Error (8031, loc,
1229 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1230 } else {
1231 ec.Report.Error (1997, loc,
1232 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1233 ec.GetSignatureForError ());
1235 return false;
1239 // The return type is actually Task<T> type argument
1241 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1242 ec.Report.Error (4016, loc,
1243 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1244 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1245 } else {
1246 block_return_type = async_type.TypeArguments[0];
1249 } else {
1250 if (block_return_type.Kind == MemberKind.Void) {
1251 ec.Report.Error (8030, loc,
1252 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1253 return false;
1256 var l = am as AnonymousMethodBody;
1257 if (l != null && expr != null) {
1258 if (l.ReturnTypeInference != null) {
1259 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1260 return true;
1264 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1265 // unexpected debugging experience
1267 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1268 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1274 if (expr == null)
1275 return false;
1277 if (expr is ReferenceExpression && block_return_type.Kind != MemberKind.ByRef) {
1278 ec.Report.Error (8149, loc, "By-reference returns can only be used in methods that return by reference");
1279 return false;
1282 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1283 if (block_return_type.Kind == MemberKind.ByRef) {
1284 var ref_expr = Expr as ReferenceExpression;
1285 if (ref_expr == null) {
1286 ec.Report.Error (8150, loc, "By-reference return is required when method returns by reference");
1287 return false;
1290 var byref_return = (ReferenceContainer)block_return_type;
1292 if (expr.Type != byref_return.Element) {
1293 ec.Report.Error (8151, loc, "The return by reference expression must be of type `{0}' because this method returns by reference",
1294 byref_return.GetSignatureForError ());
1295 return false;
1297 } else {
1299 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1301 if (expr == null) {
1302 if (am != null && block_return_type == ec.ReturnType) {
1303 ec.Report.Error (1662, loc,
1304 "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",
1305 am.ContainerType, am.GetSignatureForError ());
1307 return false;
1312 return true;
1315 protected override void DoEmit (EmitContext ec)
1317 if (expr != null) {
1319 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1320 if (async_body != null) {
1321 var storey = (AsyncTaskStorey)async_body.Storey;
1322 Label exit_label = async_body.BodyEnd;
1325 // It's null for await without async
1327 if (storey.HoistedReturnValue != null) {
1329 // Special case hoisted return value (happens in try/finally scenario)
1331 if (ec.TryFinallyUnwind != null) {
1332 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1335 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1336 async_return.EmitAssign (ec, expr, false, false);
1337 ec.EmitEpilogue ();
1338 } else {
1339 expr.Emit (ec);
1341 if (ec.TryFinallyUnwind != null)
1342 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1345 ec.Emit (OpCodes.Leave, exit_label);
1346 return;
1349 expr.Emit (ec);
1350 ec.EmitEpilogue ();
1352 if (unwind_protect || ec.EmitAccurateDebugInfo)
1353 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1356 if (unwind_protect) {
1357 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1358 } else if (ec.EmitAccurateDebugInfo) {
1359 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1360 } else {
1361 ec.Emit (OpCodes.Ret);
1365 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1367 if (expr != null)
1368 expr.FlowAnalysis (fc);
1370 if (!expr_returns)
1371 base.DoFlowAnalysis (fc);
1373 return true;
1376 void Error_ReturnFromIterator (ResolveContext rc)
1378 rc.Report.Error (1622, loc,
1379 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1382 public override Reachability MarkReachable (Reachability rc)
1384 base.MarkReachable (rc);
1386 if (Expr != null) {
1387 rc = Expr.MarkReachable (rc);
1388 expr_returns = rc.IsUnreachable;
1391 return Reachability.CreateUnreachable ();
1394 protected override void CloneTo (CloneContext clonectx, Statement t)
1396 Return target = (Return) t;
1397 // It's null for simple return;
1398 if (expr != null)
1399 target.expr = expr.Clone (clonectx);
1402 public override object Accept (StructuralVisitor visitor)
1404 return visitor.Visit (this);
1408 public class Goto : ExitStatement
1410 string target;
1411 LabeledStatement label;
1412 TryFinally try_finally;
1414 public Goto (string label, Location l)
1416 loc = l;
1417 target = label;
1420 public string Target {
1421 get { return target; }
1424 protected override bool IsLocalExit {
1425 get {
1426 return true;
1430 protected override bool DoResolve (BlockContext bc)
1432 label = bc.CurrentBlock.LookupLabel (target);
1433 if (label == null) {
1434 Error_UnknownLabel (bc, target, loc);
1435 return false;
1438 try_finally = bc.CurrentTryBlock as TryFinally;
1440 CheckExitBoundaries (bc, label.Block);
1442 return true;
1445 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1447 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1448 label);
1451 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1453 // Goto to unreachable label
1454 if (label == null)
1455 return true;
1457 if (fc.AddReachedLabel (label))
1458 return true;
1460 label.Block.ScanGotoJump (label, fc);
1461 return true;
1464 public override Reachability MarkReachable (Reachability rc)
1466 if (rc.IsUnreachable)
1467 return rc;
1469 base.MarkReachable (rc);
1471 if (try_finally != null) {
1472 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1473 label.AddGotoReference (rc);
1474 } else {
1475 label = null;
1477 } else {
1478 label.AddGotoReference (rc);
1481 return Reachability.CreateUnreachable ();
1484 protected override void CloneTo (CloneContext clonectx, Statement target)
1486 // Nothing to clone
1489 protected override void DoEmit (EmitContext ec)
1491 // This should only happen for goto from try block to unrechable label
1492 if (label == null)
1493 return;
1495 Label l = label.LabelTarget (ec);
1497 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1498 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1499 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
1502 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1505 bool IsLeavingFinally (Block labelBlock)
1507 var b = try_finally.Statement as Block;
1508 while (b != null) {
1509 if (b == labelBlock)
1510 return true;
1512 b = b.Parent;
1515 return false;
1518 public override object Accept (StructuralVisitor visitor)
1520 return visitor.Visit (this);
1524 public class LabeledStatement : Statement {
1525 string name;
1526 bool defined;
1527 bool referenced;
1528 Label label;
1529 Block block;
1531 public LabeledStatement (string name, Block block, Location l)
1533 this.name = name;
1534 this.block = block;
1535 this.loc = l;
1538 public Label LabelTarget (EmitContext ec)
1540 if (defined)
1541 return label;
1543 label = ec.DefineLabel ();
1544 defined = true;
1545 return label;
1548 public Block Block {
1549 get {
1550 return block;
1554 public string Name {
1555 get { return name; }
1558 protected override void CloneTo (CloneContext clonectx, Statement target)
1560 var t = (LabeledStatement) target;
1562 t.block = clonectx.RemapBlockCopy (block);
1565 public override bool Resolve (BlockContext bc)
1567 return true;
1570 protected override void DoEmit (EmitContext ec)
1572 LabelTarget (ec);
1573 ec.MarkLabel (label);
1576 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1578 if (!referenced) {
1579 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1582 return false;
1585 public override Reachability MarkReachable (Reachability rc)
1587 base.MarkReachable (rc);
1589 if (referenced)
1590 rc = new Reachability ();
1592 return rc;
1595 public void AddGotoReference (Reachability rc)
1597 if (referenced)
1598 return;
1600 referenced = true;
1601 MarkReachable (rc);
1603 block.ScanGotoJump (this);
1606 public override object Accept (StructuralVisitor visitor)
1608 return visitor.Visit (this);
1613 /// <summary>
1614 /// `goto default' statement
1615 /// </summary>
1616 public class GotoDefault : SwitchGoto
1618 public GotoDefault (Location l)
1619 : base (l)
1623 public override bool Resolve (BlockContext bc)
1625 if (bc.Switch == null) {
1626 Error_GotoCaseRequiresSwitchBlock (bc);
1627 return false;
1630 bc.Switch.RegisterGotoCase (null, null);
1631 base.Resolve (bc);
1633 return true;
1636 protected override void DoEmit (EmitContext ec)
1638 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1641 public override Reachability MarkReachable (Reachability rc)
1643 if (!rc.IsUnreachable) {
1644 var label = switch_statement.DefaultLabel;
1645 if (label.IsUnreachable) {
1646 label.MarkReachable (rc);
1647 switch_statement.Block.ScanGotoJump (label);
1651 return base.MarkReachable (rc);
1654 public override object Accept (StructuralVisitor visitor)
1656 return visitor.Visit (this);
1660 /// <summary>
1661 /// `goto case' statement
1662 /// </summary>
1663 public class GotoCase : SwitchGoto
1665 Expression expr;
1667 public GotoCase (Expression e, Location l)
1668 : base (l)
1670 expr = e;
1673 public Expression Expr {
1674 get {
1675 return expr;
1679 public SwitchLabel Label { get; set; }
1681 public override bool Resolve (BlockContext ec)
1683 if (ec.Switch == null) {
1684 Error_GotoCaseRequiresSwitchBlock (ec);
1685 return false;
1688 Constant c = expr.ResolveLabelConstant (ec);
1689 if (c == null) {
1690 return false;
1693 Constant res;
1694 if (ec.Switch.IsNullable && c is NullLiteral) {
1695 res = c;
1696 } else {
1697 TypeSpec type = ec.Switch.SwitchType;
1698 res = c.Reduce (ec, type);
1699 if (res == null) {
1700 c.Error_ValueCannotBeConverted (ec, type, true);
1701 return false;
1704 if (!Convert.ImplicitStandardConversionExists (c, type))
1705 ec.Report.Warning (469, 2, loc,
1706 "The `goto case' value is not implicitly convertible to type `{0}'",
1707 type.GetSignatureForError ());
1711 ec.Switch.RegisterGotoCase (this, res);
1712 base.Resolve (ec);
1713 expr = res;
1715 return true;
1718 protected override void DoEmit (EmitContext ec)
1720 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1723 protected override void CloneTo (CloneContext clonectx, Statement t)
1725 GotoCase target = (GotoCase) t;
1727 target.expr = expr.Clone (clonectx);
1730 public override Reachability MarkReachable (Reachability rc)
1732 if (!rc.IsUnreachable) {
1733 var label = switch_statement.FindLabel ((Constant) expr);
1734 if (label.IsUnreachable) {
1735 label.MarkReachable (rc);
1736 switch_statement.Block.ScanGotoJump (label);
1740 return base.MarkReachable (rc);
1743 public override object Accept (StructuralVisitor visitor)
1745 return visitor.Visit (this);
1749 public abstract class SwitchGoto : Statement
1751 protected bool unwind_protect;
1752 protected Switch switch_statement;
1754 protected SwitchGoto (Location loc)
1756 this.loc = loc;
1759 protected override void CloneTo (CloneContext clonectx, Statement target)
1761 // Nothing to clone
1764 public override bool Resolve (BlockContext bc)
1766 CheckExitBoundaries (bc, bc.Switch.Block);
1768 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1769 switch_statement = bc.Switch;
1771 return true;
1774 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1776 return true;
1779 public override Reachability MarkReachable (Reachability rc)
1781 base.MarkReachable (rc);
1782 return Reachability.CreateUnreachable ();
1785 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1787 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1791 public class Throw : Statement {
1792 Expression expr;
1794 public Throw (Expression expr, Location l)
1796 this.expr = expr;
1797 loc = l;
1800 public Expression Expr {
1801 get {
1802 return this.expr;
1806 public static Expression ConvertType (ResolveContext rc, Expression expr)
1808 var et = rc.BuiltinTypes.Exception;
1809 if (Convert.ImplicitConversionExists (rc, expr, et))
1810 expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
1811 else {
1812 rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1813 expr = EmptyCast.Create (expr, et);
1816 return expr;
1819 public override bool Resolve (BlockContext ec)
1821 if (expr == null) {
1822 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1823 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1824 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1825 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1826 if (b.IsFinallyBlock) {
1827 ec.Report.Error (724, loc,
1828 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1829 break;
1834 return true;
1837 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1839 if (expr == null)
1840 return false;
1842 expr = ConvertType (ec, expr);
1844 return true;
1847 protected override void DoEmit (EmitContext ec)
1849 if (expr == null) {
1850 var atv = ec.AsyncThrowVariable;
1851 if (atv != null) {
1852 if (atv.HoistedVariant != null) {
1853 atv.HoistedVariant.Emit (ec);
1854 } else {
1855 atv.Emit (ec);
1858 ec.Emit (OpCodes.Throw);
1859 } else {
1860 ec.Emit (OpCodes.Rethrow);
1862 } else {
1863 expr.Emit (ec);
1865 ec.Emit (OpCodes.Throw);
1869 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1871 if (expr != null)
1872 expr.FlowAnalysis (fc);
1874 return true;
1877 public override Reachability MarkReachable (Reachability rc)
1879 base.MarkReachable (rc);
1880 return Reachability.CreateUnreachable ();
1883 protected override void CloneTo (CloneContext clonectx, Statement t)
1885 Throw target = (Throw) t;
1887 if (expr != null)
1888 target.expr = expr.Clone (clonectx);
1891 public override object Accept (StructuralVisitor visitor)
1893 return visitor.Visit (this);
1897 public class Break : LocalExitStatement
1899 public Break (Location l)
1900 : base (l)
1904 public override object Accept (StructuralVisitor visitor)
1906 return visitor.Visit (this);
1909 protected override void DoEmit (EmitContext ec)
1911 var l = ec.LoopEnd;
1913 if (ec.TryFinallyUnwind != null) {
1914 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1915 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1918 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1921 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1923 enclosing_loop.AddEndDefiniteAssignment (fc);
1924 return true;
1927 protected override bool DoResolve (BlockContext bc)
1929 enclosing_loop = bc.EnclosingLoopOrSwitch;
1930 return base.DoResolve (bc);
1933 public override Reachability MarkReachable (Reachability rc)
1935 base.MarkReachable (rc);
1937 if (!rc.IsUnreachable)
1938 enclosing_loop.SetEndReachable ();
1940 return Reachability.CreateUnreachable ();
1944 public class Continue : LocalExitStatement
1946 public Continue (Location l)
1947 : base (l)
1951 public override object Accept (StructuralVisitor visitor)
1953 return visitor.Visit (this);
1957 protected override void DoEmit (EmitContext ec)
1959 var l = ec.LoopBegin;
1961 if (ec.TryFinallyUnwind != null) {
1962 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1963 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1966 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1969 protected override bool DoResolve (BlockContext bc)
1971 enclosing_loop = bc.EnclosingLoop;
1972 return base.DoResolve (bc);
1975 public override Reachability MarkReachable (Reachability rc)
1977 base.MarkReachable (rc);
1979 if (!rc.IsUnreachable)
1980 enclosing_loop.SetIteratorReachable ();
1982 return Reachability.CreateUnreachable ();
1986 public abstract class LocalExitStatement : ExitStatement
1988 protected LoopStatement enclosing_loop;
1990 protected LocalExitStatement (Location loc)
1992 this.loc = loc;
1995 protected override bool IsLocalExit {
1996 get {
1997 return true;
2001 protected override void CloneTo (CloneContext clonectx, Statement t)
2003 // nothing needed.
2006 protected override bool DoResolve (BlockContext bc)
2008 if (enclosing_loop == null) {
2009 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
2010 return false;
2013 var block = enclosing_loop.Statement as Block;
2015 // Don't need to do extra checks for simple statements loops
2016 if (block != null) {
2017 CheckExitBoundaries (bc, block);
2020 return true;
2024 public interface ILocalVariable
2026 void Emit (EmitContext ec);
2027 void EmitAssign (EmitContext ec);
2028 void EmitAddressOf (EmitContext ec);
2031 public interface INamedBlockVariable
2033 Block Block { get; }
2034 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
2035 bool IsDeclared { get; }
2036 bool IsParameter { get; }
2037 Location Location { get; }
2040 public class BlockVariableDeclarator
2042 LocalVariable li;
2043 Expression initializer;
2045 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2047 if (li.Type != null)
2048 throw new ArgumentException ("Expected null variable type");
2050 this.li = li;
2051 this.initializer = initializer;
2054 #region Properties
2056 public LocalVariable Variable {
2057 get {
2058 return li;
2062 public Expression Initializer {
2063 get {
2064 return initializer;
2066 set {
2067 initializer = value;
2071 #endregion
2073 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2075 var t = (BlockVariableDeclarator) MemberwiseClone ();
2076 if (initializer != null)
2077 t.initializer = initializer.Clone (cloneCtx);
2079 return t;
2083 public class BlockVariable : Statement
2085 Expression initializer;
2086 protected FullNamedExpression type_expr;
2087 protected LocalVariable li;
2088 protected List<BlockVariableDeclarator> declarators;
2089 TypeSpec type;
2091 public BlockVariable (FullNamedExpression type, LocalVariable li)
2093 this.type_expr = type;
2094 this.li = li;
2095 this.loc = type_expr.Location;
2098 protected BlockVariable (LocalVariable li)
2100 this.li = li;
2103 #region Properties
2105 public List<BlockVariableDeclarator> Declarators {
2106 get {
2107 return declarators;
2111 public Expression Initializer {
2112 get {
2113 return initializer;
2115 set {
2116 initializer = value;
2120 public FullNamedExpression TypeExpression {
2121 get {
2122 return type_expr;
2126 public LocalVariable Variable {
2127 get {
2128 return li;
2132 #endregion
2134 public void AddDeclarator (BlockVariableDeclarator decl)
2136 if (declarators == null)
2137 declarators = new List<BlockVariableDeclarator> ();
2139 declarators.Add (decl);
2142 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2144 if (bc.Report.Errors != 0)
2145 return;
2147 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2149 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2150 new MemberName (li.Name, li.Location), null);
2152 container.AddField (f);
2153 f.Define ();
2155 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2156 li.SetIsUsed ();
2159 public override bool Resolve (BlockContext bc)
2161 return Resolve (bc, true);
2164 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2166 if (type == null && !li.IsCompilerGenerated) {
2167 var vexpr = type_expr as VarExpr;
2170 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2171 // same name exists or as a keyword when no type was found
2173 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2174 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2175 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2177 if (li.IsFixed) {
2178 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2179 return false;
2182 if (li.IsConstant) {
2183 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2184 return false;
2187 if (Initializer == null) {
2188 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2189 return false;
2192 if (declarators != null) {
2193 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2194 declarators = null;
2197 Initializer = Initializer.Resolve (bc);
2198 if (Initializer != null) {
2199 ((VarExpr) type_expr).InferType (bc, Initializer);
2200 type = type_expr.Type;
2201 } else {
2202 // Set error type to indicate the var was placed correctly but could
2203 // not be infered
2205 // var a = missing ();
2207 type = InternalType.ErrorType;
2211 if (type == null) {
2212 type = ResolveTypeExpression (bc);
2213 if (type == null)
2214 return false;
2216 if (li.IsConstant && !type.IsConstantCompatible) {
2217 Const.Error_InvalidConstantType (type, loc, bc.Report);
2221 if (type.IsStatic)
2222 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2224 li.Type = type;
2227 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2228 if (eval_global) {
2229 CreateEvaluatorVariable (bc, li);
2230 } else if (type != InternalType.ErrorType) {
2231 li.PrepareAssignmentAnalysis (bc);
2234 if (initializer != null) {
2235 if (li.IsByRef) {
2236 if (!(initializer is ReferenceExpression)) {
2237 bc.Report.Error (8172, loc, "Cannot initialize a by-reference variable `{0}' with a value", li.Name);
2238 return false;
2241 if (bc.CurrentAnonymousMethod is AsyncInitializer) {
2242 bc.Report.Error (8177, loc, "Async methods cannot use by-reference variables");
2243 } else if (bc.CurrentIterator != null) {
2244 bc.Report.Error (8176, loc, "Iterators cannot use by-reference variables");
2247 } else {
2248 if (initializer is ReferenceExpression) {
2249 bc.Report.Error (8171, loc, "Cannot initialize a by-value variable `{0}' with a reference expression", li.Name);
2250 return false;
2254 initializer = ResolveInitializer (bc, li, initializer);
2255 // li.Variable.DefinitelyAssigned
2258 if (declarators != null) {
2259 foreach (var d in declarators) {
2260 d.Variable.Type = li.Type;
2261 if (eval_global) {
2262 CreateEvaluatorVariable (bc, d.Variable);
2263 } else if (type != InternalType.ErrorType) {
2264 d.Variable.PrepareAssignmentAnalysis (bc);
2267 if (d.Initializer != null && resolveDeclaratorInitializers) {
2268 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2269 // d.Variable.DefinitelyAssigned
2274 return true;
2277 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2279 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2280 return a.ResolveStatement (bc);
2283 protected virtual TypeSpec ResolveTypeExpression (BlockContext bc)
2285 return type_expr.ResolveAsType (bc);
2288 protected override void DoEmit (EmitContext ec)
2290 li.CreateBuilder (ec);
2292 if (Initializer != null && !IsUnreachable)
2293 ((ExpressionStatement) Initializer).EmitStatement (ec);
2295 if (declarators != null) {
2296 foreach (var d in declarators) {
2297 d.Variable.CreateBuilder (ec);
2298 if (d.Initializer != null && !IsUnreachable) {
2299 ec.Mark (d.Variable.Location);
2300 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2306 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2308 if (Initializer != null)
2309 Initializer.FlowAnalysis (fc);
2311 if (declarators != null) {
2312 foreach (var d in declarators) {
2313 if (d.Initializer != null)
2314 d.Initializer.FlowAnalysis (fc);
2318 return false;
2321 public override Reachability MarkReachable (Reachability rc)
2323 base.MarkReachable (rc);
2324 return initializer == null ? rc : initializer.MarkReachable (rc);
2327 protected override void CloneTo (CloneContext clonectx, Statement target)
2329 BlockVariable t = (BlockVariable) target;
2331 if (type_expr != null)
2332 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2334 if (initializer != null)
2335 t.initializer = initializer.Clone (clonectx);
2337 if (declarators != null) {
2338 t.declarators = null;
2339 foreach (var d in declarators)
2340 t.AddDeclarator (d.Clone (clonectx));
2344 public override object Accept (StructuralVisitor visitor)
2346 return visitor.Visit (this);
2350 public class BlockConstant : BlockVariable
2352 public BlockConstant (FullNamedExpression type, LocalVariable li)
2353 : base (type, li)
2357 public override void Emit (EmitContext ec)
2359 if (!Variable.IsUsed)
2360 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2362 // Nothing to emit, not even sequence point
2365 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2367 initializer = initializer.Resolve (bc);
2368 if (initializer == null)
2369 return null;
2371 Constant c;
2372 if (initializer.Type == InternalType.DefaultType)
2373 c = New.Constantify (li.Type, initializer.Location);
2374 else
2375 c = initializer as Constant;
2377 if (c == null) {
2378 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2379 return null;
2382 c = c.ConvertImplicitly (li.Type);
2383 if (c == null) {
2384 if (TypeSpec.IsReferenceType (li.Type))
2385 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2386 else
2387 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2389 return null;
2392 li.ConstantValue = c;
2393 return initializer;
2396 public override object Accept (StructuralVisitor visitor)
2398 return visitor.Visit (this);
2403 // The information about a user-perceived local variable
2405 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2407 [Flags]
2408 public enum Flags
2410 Used = 1,
2411 IsThis = 1 << 1,
2412 AddressTaken = 1 << 2,
2413 CompilerGenerated = 1 << 3,
2414 Constant = 1 << 4,
2415 ForeachVariable = 1 << 5 | ReadonlyMask,
2416 FixedVariable = 1 << 6 | ReadonlyMask,
2417 UsingVariable = 1 << 7 | ReadonlyMask,
2418 IsLocked = 1 << 8,
2419 SymbolFileHidden = 1 << 9,
2420 ByRef = 1 << 10,
2421 PointerByRef = 1 << 11,
2423 ReadonlyMask = 1 << 20
2426 TypeSpec type;
2427 readonly string name;
2428 readonly Location loc;
2429 readonly Block block;
2430 Flags flags;
2431 Constant const_value;
2433 public VariableInfo VariableInfo;
2434 HoistedVariable hoisted_variant;
2436 LocalBuilder builder;
2438 public LocalVariable (Block block, string name, Location loc)
2440 this.block = block;
2441 this.name = name;
2442 this.loc = loc;
2445 public LocalVariable (Block block, string name, Flags flags, Location loc)
2446 : this (block, name, loc)
2448 this.flags = flags;
2452 // Used by variable declarators
2454 public LocalVariable (LocalVariable li, string name, Location loc)
2455 : this (li.block, name, li.flags, loc)
2459 #region Properties
2461 public bool AddressTaken {
2462 get {
2463 return (flags & Flags.AddressTaken) != 0;
2467 public Block Block {
2468 get {
2469 return block;
2473 public Constant ConstantValue {
2474 get {
2475 return const_value;
2477 set {
2478 const_value = value;
2483 // Hoisted local variable variant
2485 public HoistedVariable HoistedVariant {
2486 get {
2487 return hoisted_variant;
2489 set {
2490 hoisted_variant = value;
2494 public bool Created {
2495 get {
2496 return builder != null;
2500 public bool IsDeclared {
2501 get {
2502 return type != null;
2506 public bool IsByRef => (flags & Flags.ByRef) != 0;
2508 public bool IsCompilerGenerated {
2509 get {
2510 return (flags & Flags.CompilerGenerated) != 0;
2514 public bool IsConstant {
2515 get {
2516 return (flags & Flags.Constant) != 0;
2520 public bool IsLocked {
2521 get {
2522 return (flags & Flags.IsLocked) != 0;
2524 set {
2525 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2529 public bool IsThis {
2530 get {
2531 return (flags & Flags.IsThis) != 0;
2535 public bool IsUsed {
2536 get {
2537 return (flags & Flags.Used) != 0;
2541 public bool IsFixed {
2542 get {
2543 return (flags & Flags.FixedVariable) == Flags.FixedVariable;
2545 set {
2546 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2550 bool INamedBlockVariable.IsParameter {
2551 get {
2552 return false;
2556 public bool IsReadonly {
2557 get {
2558 return (flags & Flags.ReadonlyMask) != 0;
2562 public Location Location {
2563 get {
2564 return loc;
2568 public string Name {
2569 get {
2570 return name;
2574 public TypeSpec Type {
2575 get {
2576 return type;
2578 set {
2579 type = value;
2583 #endregion
2585 public void CreateBuilder (EmitContext ec)
2587 if ((flags & Flags.Used) == 0) {
2588 if (VariableInfo == null) {
2589 // Missing flow analysis or wrong variable flags
2590 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2593 if (VariableInfo.IsEverAssigned)
2594 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2595 else
2596 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2599 if (HoistedVariant != null)
2600 return;
2602 if (builder != null) {
2603 if ((flags & Flags.CompilerGenerated) != 0)
2604 return;
2606 // To avoid Used warning duplicates
2607 throw new InternalErrorException ("Already created variable `{0}'", name);
2610 if (IsByRef) {
2611 builder = ec.DeclareLocal (ReferenceContainer.MakeType (ec.Module, Type), IsFixed);
2612 } else if ((flags & Flags.PointerByRef) != 0) {
2613 builder = ec.DeclareLocal (ReferenceContainer.MakeType (ec.Module, ((PointerContainer) Type).Element), IsFixed);
2614 } else {
2616 // All fixed variabled are pinned, a slot has to be alocated
2618 builder = ec.DeclareLocal (Type, IsFixed);
2621 if ((flags & Flags.SymbolFileHidden) == 0)
2622 ec.DefineLocalVariable (name, builder);
2625 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false, Flags additionalFlags = 0)
2627 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used | additionalFlags, loc);
2628 if (!writeToSymbolFile)
2629 li.flags |= Flags.SymbolFileHidden;
2631 li.Type = type;
2632 return li;
2635 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2637 if (IsConstant && const_value != null) {
2638 SetIsUsed ();
2639 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2642 return new LocalVariableReference (this, loc);
2645 public void Emit (EmitContext ec)
2647 // TODO: Need something better for temporary variables
2648 if ((flags & Flags.CompilerGenerated) != 0)
2649 CreateBuilder (ec);
2651 ec.Emit (OpCodes.Ldloc, builder);
2654 public void EmitAssign (EmitContext ec)
2656 // TODO: Need something better for temporary variables
2657 if ((flags & Flags.CompilerGenerated) != 0)
2658 CreateBuilder (ec);
2660 ec.Emit (OpCodes.Stloc, builder);
2663 public void EmitAddressOf (EmitContext ec)
2665 // TODO: Need something better for temporary variables
2666 if ((flags & Flags.CompilerGenerated) != 0)
2667 CreateBuilder (ec);
2669 if (IsByRef)
2670 ec.Emit (OpCodes.Ldloc, builder);
2671 else
2672 ec.Emit (OpCodes.Ldloca, builder);
2675 public static string GetCompilerGeneratedName (Block block)
2677 // HACK: Debugger depends on the name semantics
2678 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2681 public string GetReadOnlyContext ()
2683 switch (flags & (Flags.ForeachVariable | Flags.FixedVariable | Flags.UsingVariable)) {
2684 case Flags.FixedVariable:
2685 return "fixed variable";
2686 case Flags.ForeachVariable:
2687 return "foreach iteration variable";
2688 case Flags.UsingVariable:
2689 return "using variable";
2692 throw new InternalErrorException ("Variable is not readonly");
2695 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2697 if (VariableInfo == null)
2698 throw new Exception ();
2700 if (IsAssigned (fc))
2701 return true;
2703 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2706 public bool IsAssigned (FlowAnalysisContext fc)
2708 return fc.IsDefinitelyAssigned (VariableInfo);
2711 public void PrepareAssignmentAnalysis (BlockContext bc)
2714 // No need to run assignment analysis for these guys
2716 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2717 return;
2719 VariableInfo = VariableInfo.Create (bc, this);
2723 // Mark the variables as referenced in the user code
2725 public void SetIsUsed ()
2727 flags |= Flags.Used;
2730 public void SetIsPointerByRef ()
2732 flags |= Flags.PointerByRef;
2735 public void SetHasAddressTaken ()
2737 flags |= (Flags.AddressTaken | Flags.Used);
2740 public override string ToString ()
2742 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2746 /// <summary>
2747 /// Block represents a C# block.
2748 /// </summary>
2750 /// <remarks>
2751 /// This class is used in a number of places: either to represent
2752 /// explicit blocks that the programmer places or implicit blocks.
2754 /// Implicit blocks are used as labels or to introduce variable
2755 /// declarations.
2757 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2758 /// they contain extra information that is not necessary on normal blocks.
2759 /// </remarks>
2760 public class Block : Statement {
2761 [Flags]
2762 public enum Flags
2764 Unchecked = 1,
2765 ReachableEnd = 8,
2766 Unsafe = 16,
2767 HasCapturedVariable = 64,
2768 HasCapturedThis = 1 << 7,
2769 IsExpressionTree = 1 << 8,
2770 CompilerGenerated = 1 << 9,
2771 HasAsyncModifier = 1 << 10,
2772 Resolved = 1 << 11,
2773 YieldBlock = 1 << 12,
2774 AwaitBlock = 1 << 13,
2775 FinallyBlock = 1 << 14,
2776 CatchBlock = 1 << 15,
2777 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2778 Iterator = 1 << 20,
2779 NoFlowAnalysis = 1 << 21,
2780 InitializationEmitted = 1 << 22
2783 public Block Parent;
2784 public Location StartLocation;
2785 public Location EndLocation;
2787 public ExplicitBlock Explicit;
2788 public ParametersBlock ParametersBlock;
2790 protected Flags flags;
2793 // The statements in this block
2795 protected List<Statement> statements;
2797 protected List<Statement> scope_initializers;
2799 int? resolving_init_idx;
2801 Block original;
2803 #if DEBUG
2804 static int id;
2805 public int ID = id++;
2807 static int clone_id_counter;
2808 int clone_id;
2809 #endif
2811 // int assignable_slots;
2813 public Block (Block parent, Location start, Location end)
2814 : this (parent, 0, start, end)
2818 public Block (Block parent, Flags flags, Location start, Location end)
2820 if (parent != null) {
2821 // the appropriate constructors will fixup these fields
2822 ParametersBlock = parent.ParametersBlock;
2823 Explicit = parent.Explicit;
2826 this.Parent = parent;
2827 this.flags = flags;
2828 this.StartLocation = start;
2829 this.EndLocation = end;
2830 this.loc = start;
2831 statements = new List<Statement> (4);
2833 this.original = this;
2836 #region Properties
2838 public Block Original {
2839 get {
2840 return original;
2842 protected set {
2843 original = value;
2847 public bool IsCompilerGenerated {
2848 get { return (flags & Flags.CompilerGenerated) != 0; }
2849 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2853 public bool IsCatchBlock {
2854 get {
2855 return (flags & Flags.CatchBlock) != 0;
2859 public bool IsFinallyBlock {
2860 get {
2861 return (flags & Flags.FinallyBlock) != 0;
2865 public bool Unchecked {
2866 get { return (flags & Flags.Unchecked) != 0; }
2867 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2870 public bool Unsafe {
2871 get { return (flags & Flags.Unsafe) != 0; }
2872 set { flags |= Flags.Unsafe; }
2875 public List<Statement> Statements {
2876 get { return statements; }
2879 #endregion
2881 public void SetEndLocation (Location loc)
2883 EndLocation = loc;
2886 public void AddLabel (LabeledStatement target)
2888 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2891 public void AddLocalName (LocalVariable li)
2893 AddLocalName (li.Name, li);
2896 public virtual void AddLocalName (string name, INamedBlockVariable li, bool canShadowChildrenBlockName = false)
2898 ParametersBlock.TopBlock.AddLocalName (name, li, canShadowChildrenBlockName);
2901 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2903 if (reason == null) {
2904 Error_AlreadyDeclared (name, variable);
2905 return;
2908 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2909 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2910 "to `{0}', which is already used in a `{1}' scope to denote something else",
2911 name, reason);
2914 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2916 var pi = variable as ParametersBlock.ParameterInfo;
2917 if (pi != null) {
2918 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2919 } else {
2920 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2921 "A local variable named `{0}' is already defined in this scope", name);
2925 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2927 ParametersBlock.TopBlock.Report.Error (412, loc,
2928 "The type parameter name `{0}' is the same as local variable or parameter name",
2929 name);
2933 // It should be used by expressions which require to
2934 // register a statement during resolve process.
2936 public void AddScopeStatement (Statement s)
2938 if (scope_initializers == null)
2939 scope_initializers = new List<Statement> ();
2942 // Simple recursive helper, when resolve scope initializer another
2943 // new scope initializer can be added, this ensures it's initialized
2944 // before existing one. For now this can happen with expression trees
2945 // in base ctor initializer only
2947 if (resolving_init_idx.HasValue) {
2948 scope_initializers.Insert (resolving_init_idx.Value, s);
2949 ++resolving_init_idx;
2950 } else {
2951 scope_initializers.Add (s);
2955 public void InsertStatement (int index, Statement s)
2957 statements.Insert (index, s);
2960 public void AddStatement (Statement s)
2962 statements.Add (s);
2965 public LabeledStatement LookupLabel (string name)
2967 return ParametersBlock.GetLabel (name, this);
2970 public override Reachability MarkReachable (Reachability rc)
2972 if (rc.IsUnreachable)
2973 return rc;
2975 MarkReachableScope (rc);
2977 foreach (var s in statements) {
2978 rc = s.MarkReachable (rc);
2979 if (rc.IsUnreachable) {
2980 if ((flags & Flags.ReachableEnd) != 0)
2981 return new Reachability ();
2983 return rc;
2987 flags |= Flags.ReachableEnd;
2989 return rc;
2992 public void MarkReachableScope (Reachability rc)
2994 base.MarkReachable (rc);
2996 if (scope_initializers != null) {
2997 foreach (var si in scope_initializers)
2998 si.MarkReachable (rc);
3002 public override bool Resolve (BlockContext bc)
3004 if ((flags & Flags.Resolved) != 0)
3005 return true;
3007 Block prev_block = bc.CurrentBlock;
3008 bc.CurrentBlock = this;
3011 // Compiler generated scope statements
3013 if (scope_initializers != null) {
3014 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
3015 scope_initializers[resolving_init_idx.Value].Resolve (bc);
3018 resolving_init_idx = null;
3021 bool ok = true;
3022 int statement_count = statements.Count;
3023 for (int ix = 0; ix < statement_count; ix++){
3024 Statement s = statements [ix];
3026 if (!s.Resolve (bc)) {
3027 ok = false;
3028 statements [ix] = new EmptyStatement (s.loc);
3029 continue;
3033 bc.CurrentBlock = prev_block;
3035 flags |= Flags.Resolved;
3036 return ok;
3039 protected override void DoEmit (EmitContext ec)
3041 for (int ix = 0; ix < statements.Count; ix++){
3042 statements [ix].Emit (ec);
3046 public override void Emit (EmitContext ec)
3048 if (scope_initializers != null)
3049 EmitScopeInitializers (ec);
3051 DoEmit (ec);
3054 protected void EmitScopeInitializers (EmitContext ec)
3056 foreach (Statement s in scope_initializers)
3057 s.Emit (ec);
3060 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3062 if (scope_initializers != null) {
3063 foreach (var si in scope_initializers)
3064 si.FlowAnalysis (fc);
3067 return DoFlowAnalysis (fc, 0);
3070 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
3072 bool end_unreachable = !reachable;
3073 bool goto_flow_analysis = startIndex != 0;
3074 for (; startIndex < statements.Count; ++startIndex) {
3075 var s = statements[startIndex];
3077 end_unreachable = s.FlowAnalysis (fc);
3078 if (s.IsUnreachable) {
3079 statements [startIndex] = RewriteUnreachableStatement (s);
3080 continue;
3084 // Statement end reachability is needed mostly due to goto support. Consider
3086 // if (cond) {
3087 // goto X;
3088 // } else {
3089 // goto Y;
3090 // }
3091 // X:
3093 // X label is reachable only via goto not as another statement after if. We need
3094 // this for flow-analysis only to carry variable info correctly.
3096 if (end_unreachable) {
3097 bool after_goto_case = goto_flow_analysis && s is GotoCase;
3099 var f = s as TryFinally;
3100 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3102 // Special case for try-finally with unreachable code after
3103 // finally block. Try block has to include leave opcode but there is
3104 // no label to leave to after unreachable finally block closing
3105 // brace. This sentinel ensures there is always IL instruction to
3106 // leave to even if we know it'll never be reached.
3108 statements.Insert (startIndex + 1, new SentinelStatement ());
3109 } else {
3110 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3111 s = statements [startIndex];
3112 if (s is SwitchLabel) {
3113 if (!after_goto_case)
3114 s.FlowAnalysis (fc);
3116 break;
3119 if (s.IsUnreachable) {
3120 s.FlowAnalysis (fc);
3121 statements [startIndex] = RewriteUnreachableStatement (s);
3127 // Idea is to stop after goto case because goto case will always have at least same
3128 // variable assigned as switch case label. This saves a lot for complex goto case tests
3130 if (after_goto_case)
3131 break;
3133 continue;
3136 var lb = s as LabeledStatement;
3137 if (lb != null && fc.AddReachedLabel (lb))
3138 break;
3142 // The condition should be true unless there is forward jumping goto
3144 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3145 // Debug.Fail ();
3147 return !Explicit.HasReachableClosingBrace;
3150 static Statement RewriteUnreachableStatement (Statement s)
3152 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3153 // analysis. Even csc report unreachable warning for it but it's actually used hence
3154 // we try to emulate this behaviour
3156 // Consider:
3157 // goto L;
3158 // int v;
3159 // L:
3160 // v = 1;
3162 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3163 return s;
3165 return new EmptyStatement (s.loc);
3168 public void ScanGotoJump (Statement label)
3170 int i;
3171 for (i = 0; i < statements.Count; ++i) {
3172 if (statements[i] == label)
3173 break;
3176 var rc = new Reachability ();
3177 for (++i; i < statements.Count; ++i) {
3178 var s = statements[i];
3179 rc = s.MarkReachable (rc);
3180 if (rc.IsUnreachable)
3181 return;
3184 flags |= Flags.ReachableEnd;
3187 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3189 int i;
3190 for (i = 0; i < statements.Count; ++i) {
3191 if (statements[i] == label)
3192 break;
3195 DoFlowAnalysis (fc, ++i);
3198 #if DEBUG
3199 public override string ToString ()
3201 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3203 #endif
3205 protected override void CloneTo (CloneContext clonectx, Statement t)
3207 Block target = (Block) t;
3208 #if DEBUG
3209 target.clone_id = ++clone_id_counter;
3210 #endif
3212 clonectx.AddBlockMap (this, target);
3213 if (original != this)
3214 clonectx.AddBlockMap (original, target);
3216 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3217 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3219 if (Parent != null)
3220 target.Parent = clonectx.RemapBlockCopy (Parent);
3222 target.statements = new List<Statement> (statements.Count);
3223 foreach (Statement s in statements)
3224 target.statements.Add (s.Clone (clonectx));
3227 public override object Accept (StructuralVisitor visitor)
3229 return visitor.Visit (this);
3233 public class ExplicitBlock : Block
3235 protected AnonymousMethodStorey am_storey;
3236 int debug_scope_index;
3238 public ExplicitBlock (Block parent, Location start, Location end)
3239 : this (parent, (Flags) 0, start, end)
3243 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3244 : base (parent, flags, start, end)
3246 this.Explicit = this;
3249 #region Properties
3251 public AnonymousMethodStorey AnonymousMethodStorey {
3252 get {
3253 return am_storey;
3257 public bool HasAwait {
3258 get {
3259 return (flags & Flags.AwaitBlock) != 0;
3263 public bool HasCapturedThis {
3264 set {
3265 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3267 get {
3268 return (flags & Flags.HasCapturedThis) != 0;
3273 // Used to indicate that the block has reference to parent
3274 // block and cannot be made static when defining anonymous method
3276 public bool HasCapturedVariable {
3277 set {
3278 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3280 get {
3281 return (flags & Flags.HasCapturedVariable) != 0;
3285 public bool HasReachableClosingBrace {
3286 get {
3287 return (flags & Flags.ReachableEnd) != 0;
3289 set {
3290 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3294 public bool HasYield {
3295 get {
3296 return (flags & Flags.YieldBlock) != 0;
3300 #endregion
3303 // Creates anonymous method storey in current block
3305 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3308 // Return same story for iterator and async blocks unless we are
3309 // in nested anonymous method
3311 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3312 return ec.CurrentAnonymousMethod.Storey;
3314 if (am_storey == null) {
3315 MemberBase mc = ec.MemberContext as MemberBase;
3318 // Creates anonymous method storey for this block
3320 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3323 return am_storey;
3326 public void EmitScopeInitialization (EmitContext ec)
3328 if ((flags & Flags.InitializationEmitted) != 0)
3329 return;
3331 if (am_storey != null) {
3332 DefineStoreyContainer (ec, am_storey);
3333 am_storey.EmitStoreyInstantiation (ec, this);
3336 if (scope_initializers != null)
3337 EmitScopeInitializers (ec);
3339 flags |= Flags.InitializationEmitted;
3342 public override void Emit (EmitContext ec)
3344 // TODO: It's needed only when scope has variable (normal or lifted)
3345 var scopeIndex = GetDebugSymbolScopeIndex ();
3346 if (scopeIndex > 0) {
3347 ec.BeginScope (scopeIndex);
3350 EmitScopeInitialization (ec);
3352 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3353 ec.Emit (OpCodes.Nop);
3356 DoEmit (ec);
3358 if (scopeIndex > 0)
3359 ec.EndScope ();
3361 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3362 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3363 ec.Emit (OpCodes.Nop);
3367 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3369 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3370 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3371 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3375 // Creates anonymous method storey
3377 storey.CreateContainer ();
3378 storey.DefineContainer ();
3379 storey.ExpandBaseInterfaces ();
3381 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3384 // Only first storey in path will hold this reference. All children blocks will
3385 // reference it indirectly using $ref field
3387 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3388 if (b.Parent != null) {
3389 var s = b.Parent.Explicit.AnonymousMethodStorey;
3390 if (s != null) {
3391 storey.HoistedThis = s.HoistedThis;
3392 break;
3396 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3397 if (storey.HoistedThis == null)
3398 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3400 if (storey.HoistedThis != null)
3401 break;
3406 // We are the first storey on path and 'this' has to be hoisted
3408 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3409 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3411 // ThisReferencesFromChildrenBlock holds all reference even if they
3412 // are not on this path. It saves some memory otherwise it'd have to
3413 // be in every explicit block. We run this check to see if the reference
3414 // is valid for this storey
3416 Block block_on_path = ref_block;
3417 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3419 if (block_on_path == null)
3420 continue;
3422 if (storey.HoistedThis == null) {
3423 storey.AddCapturedThisField (ec, null);
3426 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3427 ParametersBlock pb;
3428 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3430 if (b_storey != null) {
3432 // Don't add storey cross reference for `this' when the storey ends up not
3433 // beeing attached to any parent
3435 if (b.ParametersBlock.StateMachine == null) {
3436 AnonymousMethodStorey s = null;
3437 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3438 s = ab.Explicit.AnonymousMethodStorey;
3439 if (s != null)
3440 break;
3443 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3444 if (s == null) {
3445 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3446 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3447 break;
3453 // Stop propagation inside same top block
3455 if (b.ParametersBlock == ParametersBlock.Original) {
3456 b_storey.AddParentStoreyReference (ec, storey);
3457 // b_storey.HoistedThis = storey.HoistedThis;
3458 break;
3461 b = pb = b.ParametersBlock;
3462 } else {
3463 pb = b as ParametersBlock;
3466 if (pb != null && pb.StateMachine != null) {
3467 if (pb.StateMachine == storey)
3468 break;
3471 // If we are state machine with no parent. We can hook into parent without additional
3472 // reference and capture this directly
3474 ExplicitBlock parent_storey_block = pb;
3475 while (parent_storey_block.Parent != null) {
3476 parent_storey_block = parent_storey_block.Parent.Explicit;
3477 if (parent_storey_block.AnonymousMethodStorey != null) {
3478 break;
3482 if (parent_storey_block.AnonymousMethodStorey == null) {
3483 if (pb.StateMachine.HoistedThis == null) {
3484 pb.StateMachine.AddCapturedThisField (ec, null);
3485 b.HasCapturedThis = true;
3488 continue;
3491 var parent_this_block = pb;
3492 while (parent_this_block.Parent != null) {
3493 parent_this_block = parent_this_block.Parent.ParametersBlock;
3494 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3495 break;
3500 // Add reference to closest storey which holds captured this
3502 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3506 // Add parent storey reference only when this is not captured directly
3508 if (b_storey != null) {
3509 b_storey.AddParentStoreyReference (ec, storey);
3510 b_storey.HoistedThis = storey.HoistedThis;
3517 var ref_blocks = storey.ReferencesFromChildrenBlock;
3518 if (ref_blocks != null) {
3519 foreach (ExplicitBlock ref_block in ref_blocks) {
3520 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3521 if (b.AnonymousMethodStorey != null) {
3522 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3525 // Stop propagation inside same top block
3527 if (b.ParametersBlock == ParametersBlock.Original)
3528 break;
3530 b = b.ParametersBlock;
3533 var pb = b as ParametersBlock;
3534 if (pb != null && pb.StateMachine != null) {
3535 if (pb.StateMachine == storey)
3536 break;
3538 pb.StateMachine.AddParentStoreyReference (ec, storey);
3541 b.HasCapturedVariable = true;
3546 storey.Define ();
3547 storey.PrepareEmit ();
3548 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3551 public void DisableDebugScopeIndex ()
3553 debug_scope_index = -1;
3556 public virtual int GetDebugSymbolScopeIndex ()
3558 if (debug_scope_index == 0)
3559 debug_scope_index = ++ParametersBlock.debug_scope_index;
3561 return debug_scope_index;
3564 public void RegisterAsyncAwait ()
3566 var block = this;
3567 while ((block.flags & Flags.AwaitBlock) == 0) {
3568 block.flags |= Flags.AwaitBlock;
3570 if (block is ParametersBlock)
3571 return;
3573 block = block.Parent.Explicit;
3577 public void RegisterIteratorYield ()
3579 ParametersBlock.TopBlock.IsIterator = true;
3581 var block = this;
3582 while ((block.flags & Flags.YieldBlock) == 0) {
3583 block.flags |= Flags.YieldBlock;
3585 if (block.Parent == null)
3586 return;
3588 block = block.Parent.Explicit;
3592 public void SetCatchBlock ()
3594 flags |= Flags.CatchBlock;
3597 public void SetFinallyBlock ()
3599 flags |= Flags.FinallyBlock;
3602 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3604 tryBlock.statements = statements;
3605 statements = new List<Statement> (1);
3606 statements.Add (tf);
3611 // ParametersBlock was introduced to support anonymous methods
3612 // and lambda expressions
3614 public class ParametersBlock : ExplicitBlock
3616 public class ParameterInfo : INamedBlockVariable
3618 readonly ParametersBlock block;
3619 readonly int index;
3620 public VariableInfo VariableInfo;
3621 bool is_locked;
3623 public ParameterInfo (ParametersBlock block, int index)
3625 this.block = block;
3626 this.index = index;
3629 #region Properties
3631 public ParametersBlock Block {
3632 get {
3633 return block;
3637 Block INamedBlockVariable.Block {
3638 get {
3639 return block;
3643 public bool IsDeclared {
3644 get {
3645 return true;
3649 public bool IsParameter {
3650 get {
3651 return true;
3655 public bool IsLocked {
3656 get {
3657 return is_locked;
3659 set {
3660 is_locked = value;
3664 public Location Location {
3665 get {
3666 return Parameter.Location;
3670 public Parameter Parameter {
3671 get {
3672 return block.Parameters [index];
3676 public TypeSpec ParameterType {
3677 get {
3678 return Parameter.Type;
3682 #endregion
3684 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3686 return new ParameterReference (this, loc);
3691 // Block is converted into an expression
3693 sealed class BlockScopeExpression : Expression
3695 Expression child;
3696 readonly ParametersBlock block;
3698 public BlockScopeExpression (Expression child, ParametersBlock block)
3700 this.child = child;
3701 this.block = block;
3704 public override bool ContainsEmitWithAwait ()
3706 return child.ContainsEmitWithAwait ();
3709 public override Expression CreateExpressionTree (ResolveContext ec)
3711 throw new NotSupportedException ();
3714 protected override Expression DoResolve (ResolveContext ec)
3716 if (child == null)
3717 return null;
3719 child = child.Resolve (ec);
3720 if (child == null)
3721 return null;
3723 eclass = child.eclass;
3724 type = child.Type;
3725 return this;
3728 public override void Emit (EmitContext ec)
3730 block.EmitScopeInitializers (ec);
3731 child.Emit (ec);
3735 protected ParametersCompiled parameters;
3736 protected ParameterInfo[] parameter_info;
3737 protected bool resolved;
3738 protected ToplevelBlock top_block;
3739 protected StateMachine state_machine;
3740 protected Dictionary<string, object> labels;
3742 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3743 : base (parent, 0, start, start)
3745 if (parameters == null)
3746 throw new ArgumentNullException ("parameters");
3748 this.parameters = parameters;
3749 ParametersBlock = this;
3751 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3753 this.top_block = parent.ParametersBlock.top_block;
3754 ProcessParameters ();
3757 protected ParametersBlock (ParametersCompiled parameters, Location start)
3758 : base (null, 0, start, start)
3760 if (parameters == null)
3761 throw new ArgumentNullException ("parameters");
3763 this.parameters = parameters;
3764 ParametersBlock = this;
3768 // It's supposed to be used by method body implementation of anonymous methods
3770 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3771 : base (null, 0, source.StartLocation, source.EndLocation)
3773 this.parameters = parameters;
3774 this.statements = source.statements;
3775 this.scope_initializers = source.scope_initializers;
3777 this.resolved = true;
3778 this.reachable = source.reachable;
3779 this.am_storey = source.am_storey;
3780 this.state_machine = source.state_machine;
3781 this.flags = source.flags & Flags.ReachableEnd;
3783 ParametersBlock = this;
3786 // Overwrite original for comparison purposes when linking cross references
3787 // between anonymous methods
3789 Original = source.Original;
3792 #region Properties
3794 public bool HasReferenceToStoreyForInstanceLambdas {
3795 get {
3796 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3798 set {
3799 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3803 public bool IsAsync {
3804 get {
3805 return (flags & Flags.HasAsyncModifier) != 0;
3807 set {
3808 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3813 // Block has been converted to expression tree
3815 public bool IsExpressionTree {
3816 get {
3817 return (flags & Flags.IsExpressionTree) != 0;
3822 // The parameters for the block.
3824 public ParametersCompiled Parameters {
3825 get {
3826 return parameters;
3830 public StateMachine StateMachine {
3831 get {
3832 return state_machine;
3836 public ToplevelBlock TopBlock {
3837 get {
3838 return top_block;
3840 set {
3841 top_block = value;
3845 public bool Resolved {
3846 get {
3847 return (flags & Flags.Resolved) != 0;
3851 public int TemporaryLocalsCount { get; set; }
3853 #endregion
3856 // Checks whether all `out' parameters have been assigned.
3858 public void CheckControlExit (FlowAnalysisContext fc)
3860 CheckControlExit (fc, fc.DefiniteAssignment);
3863 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3865 if (parameter_info == null)
3866 return;
3868 foreach (var p in parameter_info) {
3869 if (p.VariableInfo == null)
3870 continue;
3872 if (p.VariableInfo.IsAssigned (dat))
3873 continue;
3875 fc.Report.Error (177, p.Location,
3876 "The out parameter `{0}' must be assigned to before control leaves the current method",
3877 p.Parameter.Name);
3881 protected override void CloneTo (CloneContext clonectx, Statement t)
3883 base.CloneTo (clonectx, t);
3885 var target = (ParametersBlock) t;
3888 // Clone label statements as well as they contain block reference
3890 var pb = this;
3891 while (true) {
3892 if (pb.labels != null) {
3893 target.labels = new Dictionary<string, object> ();
3895 foreach (var entry in pb.labels) {
3896 var list = entry.Value as List<LabeledStatement>;
3898 if (list != null) {
3899 var list_clone = new List<LabeledStatement> ();
3900 foreach (var lentry in list) {
3901 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3904 target.labels.Add (entry.Key, list_clone);
3905 } else {
3906 var labeled = (LabeledStatement) entry.Value;
3907 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3911 break;
3914 if (pb.Parent == null)
3915 break;
3917 pb = pb.Parent.ParametersBlock;
3921 public override Expression CreateExpressionTree (ResolveContext ec)
3923 if (statements.Count == 1) {
3924 Expression expr = statements[0].CreateExpressionTree (ec);
3925 if (scope_initializers != null)
3926 expr = new BlockScopeExpression (expr, this);
3928 return expr;
3931 return base.CreateExpressionTree (ec);
3934 public override void Emit (EmitContext ec)
3936 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3937 DefineStoreyContainer (ec, state_machine);
3938 state_machine.EmitStoreyInstantiation (ec, this);
3941 base.Emit (ec);
3944 public void EmitEmbedded (EmitContext ec)
3946 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3947 DefineStoreyContainer (ec, state_machine);
3948 state_machine.EmitStoreyInstantiation (ec, this);
3951 base.Emit (ec);
3954 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3956 var res = base.DoFlowAnalysis (fc);
3958 if (HasReachableClosingBrace)
3959 CheckControlExit (fc);
3961 return res;
3964 public override int GetDebugSymbolScopeIndex ()
3966 return 0;
3969 public LabeledStatement GetLabel (string name, Block block)
3972 // Cloned parameters blocks can have their own cloned version of top-level labels
3974 if (labels == null) {
3975 if (Parent != null)
3976 return Parent.ParametersBlock.GetLabel (name, block);
3978 return null;
3981 object value;
3982 if (!labels.TryGetValue (name, out value)) {
3983 return null;
3986 var label = value as LabeledStatement;
3987 Block b = block;
3988 if (label != null) {
3989 if (IsLabelVisible (label, b))
3990 return label;
3992 } else {
3993 List<LabeledStatement> list = (List<LabeledStatement>) value;
3994 for (int i = 0; i < list.Count; ++i) {
3995 label = list[i];
3996 if (IsLabelVisible (label, b))
3997 return label;
4001 return null;
4004 static bool IsLabelVisible (LabeledStatement label, Block b)
4006 do {
4007 if (label.Block == b)
4008 return true;
4009 b = b.Parent;
4010 } while (b != null);
4012 return false;
4015 public ParameterInfo GetParameterInfo (Parameter p)
4017 for (int i = 0; i < parameters.Count; ++i) {
4018 if (parameters[i] == p)
4019 return parameter_info[i];
4022 throw new ArgumentException ("Invalid parameter");
4025 public ParameterReference GetParameterReference (int index, Location loc)
4027 return new ParameterReference (parameter_info[index], loc);
4030 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
4032 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
4034 CloneContext clonectx = new CloneContext ();
4035 return Clone (clonectx);
4038 protected void ProcessParameters ()
4040 if (parameters.Count == 0)
4041 return;
4043 parameter_info = new ParameterInfo[parameters.Count];
4044 for (int i = 0; i < parameter_info.Length; ++i) {
4045 var p = parameters.FixedParameters[i];
4046 if (p == null)
4047 continue;
4049 // TODO: Should use Parameter only and more block there
4050 parameter_info[i] = new ParameterInfo (this, i);
4051 if (p.Name != null)
4052 AddLocalName (p.Name, parameter_info[i]);
4056 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
4058 var src = stmt.Block;
4061 // Cannot remap label block if the label was not yet cloned which
4062 // can happen in case of anonymous method inside anoynymous method
4063 // with a label. But in this case we don't care because goto cannot
4064 // jump of out anonymous method
4066 if (src.ParametersBlock != this)
4067 return stmt;
4069 var src_stmts = src.Statements;
4070 for (int i = 0; i < src_stmts.Count; ++i) {
4071 if (src_stmts[i] == stmt)
4072 return (LabeledStatement) dst.Statements[i];
4075 throw new InternalErrorException ("Should never be reached");
4078 public override bool Resolve (BlockContext bc)
4080 // TODO: if ((flags & Flags.Resolved) != 0)
4082 if (resolved)
4083 return true;
4085 resolved = true;
4087 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4088 flags |= Flags.IsExpressionTree;
4090 try {
4091 PrepareAssignmentAnalysis (bc);
4093 if (!base.Resolve (bc))
4094 return false;
4096 } catch (Exception e) {
4097 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4098 throw;
4100 if (bc.CurrentBlock != null) {
4101 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4102 } else {
4103 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4108 // If an asynchronous body of F is either an expression classified as nothing, or a
4109 // statement block where no return statements have expressions, the inferred return type is Task
4111 if (IsAsync) {
4112 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4113 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4114 am.ReturnTypeInference = null;
4115 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4116 return true;
4120 return true;
4123 void PrepareAssignmentAnalysis (BlockContext bc)
4125 for (int i = 0; i < parameters.Count; ++i) {
4126 var par = parameters.FixedParameters[i];
4128 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4129 continue;
4131 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4135 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4137 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4138 var stateMachine = new IteratorStorey (iterator);
4140 state_machine = stateMachine;
4141 iterator.SetStateMachine (stateMachine);
4143 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4144 tlb.Original = this;
4145 tlb.state_machine = stateMachine;
4146 tlb.AddStatement (new Return (iterator, iterator.Location));
4147 return tlb;
4150 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4152 for (int i = 0; i < parameters.Count; i++) {
4153 Parameter p = parameters[i];
4154 Parameter.Modifier mod = p.ModFlags;
4155 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4156 host.Compiler.Report.Error (1988, p.Location,
4157 "Async methods cannot have ref or out parameters");
4158 return this;
4161 if (p is ArglistParameter) {
4162 host.Compiler.Report.Error (4006, p.Location,
4163 "__arglist is not allowed in parameter list of async methods");
4164 return this;
4167 if (parameters.Types[i].IsPointer) {
4168 host.Compiler.Report.Error (4005, p.Location,
4169 "Async methods cannot have unsafe parameters");
4170 return this;
4174 if (!HasAwait) {
4175 host.Compiler.Report.Warning (1998, 1, loc,
4176 "Async block lacks `await' operator and will run synchronously");
4179 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4180 var initializer = new AsyncInitializer (this, host, block_type);
4181 initializer.Type = block_type;
4182 initializer.DelegateType = delegateType;
4184 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4186 state_machine = stateMachine;
4187 initializer.SetStateMachine (stateMachine);
4189 const Flags flags = Flags.CompilerGenerated;
4191 var b = this is ToplevelBlock ?
4192 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4193 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4195 b.Original = this;
4196 b.state_machine = stateMachine;
4197 b.AddStatement (new AsyncInitializerStatement (initializer));
4198 return b;
4205 public class ToplevelBlock : ParametersBlock
4207 LocalVariable this_variable;
4208 CompilerContext compiler;
4209 Dictionary<string, object> names;
4211 List<ExplicitBlock> this_references;
4213 public ToplevelBlock (CompilerContext ctx, Location loc)
4214 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4218 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4219 : base (parameters, start)
4221 this.compiler = ctx;
4222 this.flags = flags;
4223 top_block = this;
4225 ProcessParameters ();
4229 // Recreates a top level block from parameters block. Used for
4230 // compiler generated methods where the original block comes from
4231 // explicit child block. This works for already resolved blocks
4232 // only to ensure we resolve them in the correct flow order
4234 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4235 : base (source, parameters)
4237 this.compiler = source.TopBlock.compiler;
4238 top_block = this;
4241 public bool IsIterator {
4242 get {
4243 return (flags & Flags.Iterator) != 0;
4245 set {
4246 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4250 public Report Report {
4251 get {
4252 return compiler.Report;
4257 // Used by anonymous blocks to track references of `this' variable
4259 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4260 get {
4261 return this_references;
4266 // Returns the "this" instance variable of this block.
4267 // See AddThisVariable() for more information.
4269 public LocalVariable ThisVariable {
4270 get {
4271 return this_variable;
4275 public override void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4277 if (names == null)
4278 names = new Dictionary<string, object> ();
4280 object value;
4281 if (!names.TryGetValue (name, out value)) {
4282 names.Add (name, li);
4283 return;
4286 INamedBlockVariable existing = value as INamedBlockVariable;
4287 List<INamedBlockVariable> existing_list;
4288 if (existing != null) {
4289 existing_list = new List<INamedBlockVariable> ();
4290 existing_list.Add (existing);
4291 names[name] = existing_list;
4292 } else {
4293 existing_list = (List<INamedBlockVariable>) value;
4297 // A collision checking between local names
4299 var variable_block = li.Block.Explicit;
4300 for (int i = 0; i < existing_list.Count; ++i) {
4301 existing = existing_list[i];
4302 Block b = existing.Block.Explicit;
4304 // Collision at same level
4305 if (variable_block == b) {
4306 li.Block.Error_AlreadyDeclared (name, li);
4307 break;
4310 // Collision with parent
4311 Block parent = variable_block;
4312 while ((parent = parent.Parent) != null) {
4313 if (parent == b) {
4314 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4315 i = existing_list.Count;
4316 break;
4320 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4321 // Collision with children
4322 while ((b = b.Parent) != null) {
4323 if (variable_block == b) {
4324 li.Block.Error_AlreadyDeclared (name, li, "child");
4325 i = existing_list.Count;
4326 break;
4332 existing_list.Add (li);
4335 public void AddLabel (string name, LabeledStatement label)
4337 if (labels == null)
4338 labels = new Dictionary<string, object> ();
4340 object value;
4341 if (!labels.TryGetValue (name, out value)) {
4342 labels.Add (name, label);
4343 return;
4346 LabeledStatement existing = value as LabeledStatement;
4347 List<LabeledStatement> existing_list;
4348 if (existing != null) {
4349 existing_list = new List<LabeledStatement> ();
4350 existing_list.Add (existing);
4351 labels[name] = existing_list;
4352 } else {
4353 existing_list = (List<LabeledStatement>) value;
4357 // A collision checking between labels
4359 for (int i = 0; i < existing_list.Count; ++i) {
4360 existing = existing_list[i];
4361 Block b = existing.Block;
4363 // Collision at same level
4364 if (label.Block == b) {
4365 Report.SymbolRelatedToPreviousError (existing.loc, name);
4366 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4367 break;
4370 // Collision with parent
4371 b = label.Block;
4372 while ((b = b.Parent) != null) {
4373 if (existing.Block == b) {
4374 Report.Error (158, label.loc,
4375 "The label `{0}' shadows another label by the same name in a contained scope", name);
4376 i = existing_list.Count;
4377 break;
4381 // Collision with with children
4382 b = existing.Block;
4383 while ((b = b.Parent) != null) {
4384 if (label.Block == b) {
4385 Report.Error (158, label.loc,
4386 "The label `{0}' shadows another label by the same name in a contained scope", name);
4387 i = existing_list.Count;
4388 break;
4393 existing_list.Add (label);
4396 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4398 if (this_references == null)
4399 this_references = new List<ExplicitBlock> ();
4401 if (!this_references.Contains (block))
4402 this_references.Add (block);
4405 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4407 this_references.Remove (block);
4411 // Creates an arguments set from all parameters, useful for method proxy calls
4413 public Arguments GetAllParametersArguments ()
4415 int count = parameters.Count;
4416 Arguments args = new Arguments (count);
4417 for (int i = 0; i < count; ++i) {
4418 var pi = parameter_info[i];
4419 var arg_expr = GetParameterReference (i, pi.Location);
4421 Argument.AType atype_modifier;
4422 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4423 case Parameter.Modifier.REF:
4424 atype_modifier = Argument.AType.Ref;
4425 break;
4426 case Parameter.Modifier.OUT:
4427 atype_modifier = Argument.AType.Out;
4428 break;
4429 default:
4430 atype_modifier = 0;
4431 break;
4434 args.Add (new Argument (arg_expr, atype_modifier));
4437 return args;
4441 // Lookup inside a block, the returned value can represent 3 states
4443 // true+variable: A local name was found and it's valid
4444 // false+variable: A local name was found in a child block only
4445 // false+null: No local name was found
4447 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4449 if (names == null)
4450 return false;
4452 object value;
4453 if (!names.TryGetValue (name, out value))
4454 return false;
4456 variable = value as INamedBlockVariable;
4457 Block b = block;
4458 if (variable != null) {
4459 do {
4460 if (variable.Block == b.Original)
4461 return true;
4463 b = b.Parent;
4464 } while (b != null);
4466 b = variable.Block;
4467 do {
4468 if (block == b)
4469 return false;
4471 b = b.Parent;
4472 } while (b != null);
4473 } else {
4474 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4475 for (int i = 0; i < list.Count; ++i) {
4476 variable = list[i];
4477 do {
4478 if (variable.Block == b.Original)
4479 return true;
4481 b = b.Parent;
4482 } while (b != null);
4484 b = variable.Block;
4485 do {
4486 if (block == b)
4487 return false;
4489 b = b.Parent;
4490 } while (b != null);
4492 b = block;
4496 variable = null;
4497 return false;
4500 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4502 if (block.names != null) {
4503 foreach (var n in block.names) {
4504 var variable = n.Value as INamedBlockVariable;
4505 if (variable != null) {
4506 if (variable.Block.ParametersBlock == pb)
4507 AddLocalName (n.Key, variable, false);
4508 continue;
4511 foreach (var v in (List<INamedBlockVariable>) n.Value)
4512 if (v.Block.ParametersBlock == pb)
4513 AddLocalName (n.Key, v, false);
4518 // <summary>
4519 // This is used by non-static `struct' constructors which do not have an
4520 // initializer - in this case, the constructor must initialize all of the
4521 // struct's fields. To do this, we add a "this" variable and use the flow
4522 // analysis code to ensure that it's been fully initialized before control
4523 // leaves the constructor.
4524 // </summary>
4525 public void AddThisVariable (BlockContext bc)
4527 if (this_variable != null)
4528 throw new InternalErrorException (StartLocation.ToString ());
4530 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4531 this_variable.Type = bc.CurrentType;
4532 this_variable.PrepareAssignmentAnalysis (bc);
4535 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4538 // If we're a non-static struct constructor which doesn't have an
4539 // initializer, then we must initialize all of the struct's fields.
4541 if (this_variable != null)
4542 this_variable.IsThisAssigned (fc, this);
4544 base.CheckControlExit (fc, dat);
4547 public HashSet<LocalVariable> GetUndeclaredVariables ()
4549 if (names == null)
4550 return null;
4552 HashSet<LocalVariable> variables = null;
4554 foreach (var entry in names) {
4555 var complex = entry.Value as List<INamedBlockVariable>;
4556 if (complex != null) {
4557 foreach (var centry in complex) {
4558 if (IsUndeclaredVariable (centry)) {
4559 if (variables == null)
4560 variables = new HashSet<LocalVariable> ();
4562 variables.Add ((LocalVariable) centry);
4565 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4566 if (variables == null)
4567 variables = new HashSet<LocalVariable> ();
4569 variables.Add ((LocalVariable)entry.Value);
4573 return variables;
4576 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4578 var lv = namedBlockVariable as LocalVariable;
4579 return lv != null && !lv.IsDeclared;
4582 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4584 if (names == null)
4585 return;
4587 foreach (var entry in names) {
4588 var complex = entry.Value as List<INamedBlockVariable>;
4589 if (complex != null) {
4590 foreach (var centry in complex) {
4591 var lv = centry as LocalVariable;
4592 if (lv != null && undeclaredVariables.Contains (lv)) {
4593 lv.Type = null;
4596 } else {
4597 var lv = entry.Value as LocalVariable;
4598 if (lv != null && undeclaredVariables.Contains (lv))
4599 lv.Type = null;
4604 public override void Emit (EmitContext ec)
4606 if (Report.Errors > 0)
4607 return;
4609 try {
4610 if (IsCompilerGenerated) {
4611 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4612 base.Emit (ec);
4614 } else {
4615 base.Emit (ec);
4619 // If `HasReturnLabel' is set, then we already emitted a
4620 // jump to the end of the method, so we must emit a `ret'
4621 // there.
4623 // Unfortunately, System.Reflection.Emit automatically emits
4624 // a leave to the end of a finally block. This is a problem
4625 // if no code is following the try/finally block since we may
4626 // jump to a point after the end of the method.
4627 // As a workaround, we're always creating a return label in
4628 // this case.
4630 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4631 if (ec.HasReturnLabel)
4632 ec.MarkLabel (ec.ReturnLabel);
4634 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4635 ec.Mark (EndLocation);
4637 if (ec.ReturnType.Kind != MemberKind.Void)
4638 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4640 ec.Emit (OpCodes.Ret);
4643 } catch (Exception e) {
4644 throw new InternalErrorException (e, StartLocation);
4648 public bool Resolve (BlockContext bc, IMethodData md)
4650 if (resolved)
4651 return true;
4653 var errors = bc.Report.Errors;
4655 base.Resolve (bc);
4657 if (bc.Report.Errors > errors)
4658 return false;
4660 MarkReachable (new Reachability ());
4662 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4663 // TODO: var md = bc.CurrentMemberDefinition;
4664 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4667 if ((flags & Flags.NoFlowAnalysis) != 0)
4668 return true;
4670 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4671 try {
4672 FlowAnalysis (fc);
4673 } catch (Exception e) {
4674 throw new InternalErrorException (e, StartLocation);
4677 return true;
4681 public class SwitchLabel : Statement
4683 Constant converted;
4684 Expression label;
4686 Label? il_label;
4689 // if expr == null, then it is the default case.
4691 public SwitchLabel (Expression expr, Location l)
4693 label = expr;
4694 loc = l;
4697 public bool IsDefault {
4698 get {
4699 return label == null;
4703 public Expression Label {
4704 get {
4705 return label;
4709 public Location Location {
4710 get {
4711 return loc;
4715 public Constant Converted {
4716 get {
4717 return converted;
4719 set {
4720 converted = value;
4724 public bool PatternMatching { get; set; }
4726 public bool SectionStart { get; set; }
4728 public Label GetILLabel (EmitContext ec)
4730 if (il_label == null){
4731 il_label = ec.DefineLabel ();
4734 return il_label.Value;
4737 protected override void DoEmit (EmitContext ec)
4739 ec.MarkLabel (GetILLabel (ec));
4742 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4744 if (!SectionStart)
4745 return false;
4747 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4748 return false;
4751 public override bool Resolve (BlockContext bc)
4753 if (ResolveAndReduce (bc))
4754 bc.Switch.RegisterLabel (bc, this);
4756 return true;
4760 // Resolves the expression, reduces it to a literal if possible
4761 // and then converts it to the requested type.
4763 bool ResolveAndReduce (BlockContext bc)
4765 if (IsDefault)
4766 return true;
4768 var switch_statement = bc.Switch;
4770 if (PatternMatching) {
4771 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4772 return label != null;
4775 var c = label.ResolveLabelConstant (bc);
4776 if (c == null)
4777 return false;
4779 if (switch_statement.IsNullable && c is NullLiteral) {
4780 converted = c;
4781 return true;
4784 if (switch_statement.IsPatternMatching) {
4785 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4786 return true;
4789 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4790 return converted != null;
4793 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4795 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4796 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4799 protected override void CloneTo (CloneContext clonectx, Statement target)
4801 var t = (SwitchLabel) target;
4802 if (label != null)
4803 t.label = label.Clone (clonectx);
4806 public override object Accept (StructuralVisitor visitor)
4808 return visitor.Visit (this);
4811 public string GetSignatureForError ()
4813 string label;
4814 if (converted == null)
4815 label = "default";
4816 else
4817 label = converted.GetValueAsLiteral ();
4819 return string.Format ("case {0}:", label);
4823 public class Switch : LoopStatement
4825 // structure used to hold blocks of keys while calculating table switch
4826 sealed class LabelsRange : IComparable<LabelsRange>
4828 public readonly long min;
4829 public long max;
4830 public readonly List<long> label_values;
4832 public LabelsRange (long value)
4834 min = max = value;
4835 label_values = new List<long> ();
4836 label_values.Add (value);
4839 public LabelsRange (long min, long max, ICollection<long> values)
4841 this.min = min;
4842 this.max = max;
4843 this.label_values = new List<long> (values);
4846 public long Range {
4847 get {
4848 return max - min + 1;
4852 public bool AddValue (long value)
4854 var gap = value - min + 1;
4855 // Ensure the range has > 50% occupancy
4856 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4857 return false;
4859 max = value;
4860 label_values.Add (value);
4861 return true;
4864 public int CompareTo (LabelsRange other)
4866 int nLength = label_values.Count;
4867 int nLengthOther = other.label_values.Count;
4868 if (nLengthOther == nLength)
4869 return (int) (other.min - min);
4871 return nLength - nLengthOther;
4875 sealed class DispatchStatement : Statement
4877 readonly Switch body;
4879 public DispatchStatement (Switch body)
4881 this.body = body;
4884 protected override void CloneTo (CloneContext clonectx, Statement target)
4886 throw new NotImplementedException ();
4889 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4891 return false;
4894 protected override void DoEmit (EmitContext ec)
4896 body.EmitDispatch (ec);
4900 class MissingBreak : Statement
4902 readonly SwitchLabel label;
4904 public MissingBreak (SwitchLabel sl)
4906 this.label = sl;
4907 this.loc = sl.loc;
4910 public bool FallOut { get; set; }
4912 protected override void DoEmit (EmitContext ec)
4916 protected override void CloneTo (CloneContext clonectx, Statement target)
4920 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4922 if (FallOut) {
4923 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4924 label.GetSignatureForError ());
4925 } else {
4926 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4927 label.GetSignatureForError ());
4929 return true;
4933 public Expression Expr;
4936 // Mapping of all labels to their SwitchLabels
4938 Dictionary<long, SwitchLabel> labels;
4939 Dictionary<string, SwitchLabel> string_labels;
4940 List<SwitchLabel> case_labels;
4942 List<Tuple<GotoCase, Constant>> goto_cases;
4943 List<DefiniteAssignmentBitSet> end_reachable_das;
4945 /// <summary>
4946 /// The governing switch type
4947 /// </summary>
4948 public TypeSpec SwitchType;
4950 Expression new_expr;
4952 SwitchLabel case_null;
4953 SwitchLabel case_default;
4955 Label defaultLabel, nullLabel;
4956 VariableReference value;
4957 ExpressionStatement string_dictionary;
4958 FieldExpr switch_cache_field;
4959 ExplicitBlock block;
4960 bool end_reachable;
4963 // Nullable Types support
4965 Nullable.Unwrap unwrap;
4967 public Switch (Expression e, ExplicitBlock block, Location l)
4968 : base (block)
4970 Expr = e;
4971 this.block = block;
4972 loc = l;
4975 public SwitchLabel ActiveLabel { get; set; }
4977 public ExplicitBlock Block {
4978 get {
4979 return block;
4983 public SwitchLabel DefaultLabel {
4984 get {
4985 return case_default;
4989 public bool IsNullable {
4990 get {
4991 return unwrap != null;
4995 public bool IsPatternMatching {
4996 get {
4997 return new_expr == null && SwitchType != null;
5001 public List<SwitchLabel> RegisteredLabels {
5002 get {
5003 return case_labels;
5007 public VariableReference ExpressionValue {
5008 get {
5009 return value;
5014 // Determines the governing type for a switch. The returned
5015 // expression might be the expression from the switch, or an
5016 // expression that includes any potential conversions to
5018 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
5020 switch (expr.Type.BuiltinType) {
5021 case BuiltinTypeSpec.Type.Byte:
5022 case BuiltinTypeSpec.Type.SByte:
5023 case BuiltinTypeSpec.Type.UShort:
5024 case BuiltinTypeSpec.Type.Short:
5025 case BuiltinTypeSpec.Type.UInt:
5026 case BuiltinTypeSpec.Type.Int:
5027 case BuiltinTypeSpec.Type.ULong:
5028 case BuiltinTypeSpec.Type.Long:
5029 case BuiltinTypeSpec.Type.Char:
5030 case BuiltinTypeSpec.Type.String:
5031 case BuiltinTypeSpec.Type.Bool:
5032 return expr;
5035 if (expr.Type.IsEnum)
5036 return expr;
5039 // Try to find a *user* defined implicit conversion.
5041 // If there is no implicit conversion, or if there are multiple
5042 // conversions, we have to report an error
5044 Expression converted = null;
5045 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
5047 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
5048 break;
5050 var restr = Convert.UserConversionRestriction.ImplicitOnly |
5051 Convert.UserConversionRestriction.ProbingOnly;
5053 if (unwrapExpr)
5054 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
5056 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
5057 if (e == null)
5058 continue;
5061 // Ignore over-worked ImplicitUserConversions that do
5062 // an implicit conversion in addition to the user conversion.
5064 var uc = e as UserCast;
5065 if (uc == null)
5066 continue;
5068 if (converted != null){
5069 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
5070 return null;
5073 converted = e;
5075 return converted;
5078 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
5080 var types = module.Compiler.BuiltinTypes;
5082 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5083 TypeSpec[] stypes = new[] {
5084 types.SByte,
5085 types.Byte,
5086 types.Short,
5087 types.UShort,
5088 types.Int,
5089 types.UInt,
5090 types.Long,
5091 types.ULong,
5092 types.Char,
5093 types.String
5096 if (nullable != null) {
5098 Array.Resize (ref stypes, stypes.Length + 9);
5100 for (int i = 0; i < 9; ++i) {
5101 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5105 return stypes;
5108 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5110 case_labels.Add (sl);
5112 if (sl.IsDefault) {
5113 if (case_default != null) {
5114 sl.Error_AlreadyOccurs (rc, case_default);
5115 } else {
5116 case_default = sl;
5119 return;
5122 if (sl.Converted == null)
5123 return;
5125 try {
5126 if (string_labels != null) {
5127 string string_value = sl.Converted.GetValue () as string;
5128 if (string_value == null)
5129 case_null = sl;
5130 else
5131 string_labels.Add (string_value, sl);
5132 } else {
5133 if (sl.Converted.IsNull) {
5134 case_null = sl;
5135 } else {
5136 labels.Add (sl.Converted.GetValueAsLong (), sl);
5139 } catch (ArgumentException) {
5140 if (string_labels != null)
5141 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5142 else
5143 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5148 // This method emits code for a lookup-based switch statement (non-string)
5149 // Basically it groups the cases into blocks that are at least half full,
5150 // and then spits out individual lookup opcodes for each block.
5151 // It emits the longest blocks first, and short blocks are just
5152 // handled with direct compares.
5154 void EmitTableSwitch (EmitContext ec, Expression val)
5156 if (labels != null && labels.Count > 0) {
5157 List<LabelsRange> ranges;
5158 if (string_labels != null) {
5159 // We have done all hard work for string already
5160 // setup single range only
5161 ranges = new List<LabelsRange> (1);
5162 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5163 } else {
5164 var element_keys = new long[labels.Count];
5165 labels.Keys.CopyTo (element_keys, 0);
5166 Array.Sort (element_keys);
5169 // Build possible ranges of switch labes to reduce number
5170 // of comparisons
5172 ranges = new List<LabelsRange> (element_keys.Length);
5173 var range = new LabelsRange (element_keys[0]);
5174 ranges.Add (range);
5175 for (int i = 1; i < element_keys.Length; ++i) {
5176 var l = element_keys[i];
5177 if (range.AddValue (l))
5178 continue;
5180 range = new LabelsRange (l);
5181 ranges.Add (range);
5184 // sort the blocks so we can tackle the largest ones first
5185 ranges.Sort ();
5188 Label lbl_default = defaultLabel;
5189 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5191 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5192 LabelsRange kb = ranges[range_index];
5193 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5195 // Optimize small ranges using simple equality check
5196 if (kb.Range <= 2) {
5197 foreach (var key in kb.label_values) {
5198 SwitchLabel sl = labels[key];
5199 if (sl == case_default || sl == case_null)
5200 continue;
5202 if (sl.Converted.IsZeroInteger) {
5203 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5204 } else {
5205 val.Emit (ec);
5206 sl.Converted.Emit (ec);
5207 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5210 } else {
5211 // TODO: if all the keys in the block are the same and there are
5212 // no gaps/defaults then just use a range-check.
5213 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5214 // TODO: optimize constant/I4 cases
5216 // check block range (could be > 2^31)
5217 val.Emit (ec);
5218 ec.EmitLong (kb.min);
5219 ec.Emit (OpCodes.Blt, lbl_default);
5221 val.Emit (ec);
5222 ec.EmitLong (kb.max);
5223 ec.Emit (OpCodes.Bgt, lbl_default);
5225 // normalize range
5226 val.Emit (ec);
5227 if (kb.min != 0) {
5228 ec.EmitLong (kb.min);
5229 ec.Emit (OpCodes.Sub);
5232 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5233 } else {
5234 // normalize range
5235 val.Emit (ec);
5236 int first = (int) kb.min;
5237 if (first > 0) {
5238 ec.EmitInt (first);
5239 ec.Emit (OpCodes.Sub);
5240 } else if (first < 0) {
5241 ec.EmitInt (-first);
5242 ec.Emit (OpCodes.Add);
5246 // first, build the list of labels for the switch
5247 int iKey = 0;
5248 long cJumps = kb.Range;
5249 Label[] switch_labels = new Label[cJumps];
5250 for (int iJump = 0; iJump < cJumps; iJump++) {
5251 var key = kb.label_values[iKey];
5252 if (key == kb.min + iJump) {
5253 switch_labels[iJump] = labels[key].GetILLabel (ec);
5254 iKey++;
5255 } else {
5256 switch_labels[iJump] = lbl_default;
5260 // emit the switch opcode
5261 ec.Emit (OpCodes.Switch, switch_labels);
5264 // mark the default for this block
5265 if (range_index != 0)
5266 ec.MarkLabel (lbl_default);
5269 // the last default just goes to the end
5270 if (ranges.Count > 0)
5271 ec.Emit (OpCodes.Br, lbl_default);
5275 public SwitchLabel FindLabel (Constant value)
5277 SwitchLabel sl = null;
5279 if (string_labels != null) {
5280 string s = value.GetValue () as string;
5281 if (s == null) {
5282 if (case_null != null)
5283 sl = case_null;
5284 else if (case_default != null)
5285 sl = case_default;
5286 } else {
5287 string_labels.TryGetValue (s, out sl);
5289 } else {
5290 if (value is NullLiteral) {
5291 sl = case_null;
5292 } else {
5293 labels.TryGetValue (value.GetValueAsLong (), out sl);
5297 if (sl == null || sl.SectionStart)
5298 return sl;
5301 // Always return section start, it simplifies handling of switch labels
5303 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5304 var cs = case_labels [idx];
5305 if (cs.SectionStart)
5306 return cs;
5310 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5312 Expr.FlowAnalysis (fc);
5314 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5315 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5316 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5318 block.FlowAnalysis (fc);
5320 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5322 if (end_reachable_das != null) {
5323 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5324 InitialDefinitiveAssignment |= sections_das;
5325 end_reachable_das = null;
5328 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5330 return case_default != null && !end_reachable;
5333 public override bool Resolve (BlockContext ec)
5335 Expr = Expr.Resolve (ec);
5336 if (Expr == null)
5337 return false;
5340 // LAMESPEC: User conversion from non-nullable governing type has a priority
5342 new_expr = SwitchGoverningType (ec, Expr, false);
5344 if (new_expr == null) {
5345 if (Expr.Type.IsNullableType) {
5346 unwrap = Nullable.Unwrap.Create (Expr, false);
5347 if (unwrap == null)
5348 return false;
5351 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5352 // involving nullable Expr and nullable governing type is
5354 new_expr = SwitchGoverningType (ec, unwrap, true);
5358 Expression switch_expr;
5359 if (new_expr == null) {
5360 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5361 if (Expr.Type != InternalType.ErrorType) {
5362 ec.Report.Error (151, loc,
5363 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5364 Expr.Type.GetSignatureForError ());
5367 return false;
5370 switch_expr = Expr;
5371 SwitchType = Expr.Type;
5372 } else {
5373 switch_expr = new_expr;
5374 SwitchType = new_expr.Type;
5375 if (SwitchType.IsNullableType) {
5376 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5377 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5380 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5381 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5382 return false;
5385 if (block.Statements.Count == 0)
5386 return true;
5388 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5389 string_labels = new Dictionary<string, SwitchLabel> ();
5390 } else {
5391 labels = new Dictionary<long, SwitchLabel> ();
5395 var constant = switch_expr as Constant;
5398 // Don't need extra variable for constant switch or switch with
5399 // only default case
5401 if (constant == null) {
5403 // Store switch expression for comparison purposes
5405 value = switch_expr as VariableReference;
5406 if (value == null && !HasOnlyDefaultSection ()) {
5407 var current_block = ec.CurrentBlock;
5408 ec.CurrentBlock = Block;
5409 // Create temporary variable inside switch scope
5410 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5411 value.Resolve (ec);
5412 ec.CurrentBlock = current_block;
5416 case_labels = new List<SwitchLabel> ();
5418 Switch old_switch = ec.Switch;
5419 ec.Switch = this;
5420 var parent_los = ec.EnclosingLoopOrSwitch;
5421 ec.EnclosingLoopOrSwitch = this;
5423 var ok = Statement.Resolve (ec);
5425 ec.EnclosingLoopOrSwitch = parent_los;
5426 ec.Switch = old_switch;
5429 // Check if all goto cases are valid. Needs to be done after switch
5430 // is resolved because goto can jump forward in the scope.
5432 if (goto_cases != null) {
5433 foreach (var gc in goto_cases) {
5434 if (gc.Item1 == null) {
5435 if (DefaultLabel == null) {
5436 Goto.Error_UnknownLabel (ec, "default", loc);
5439 continue;
5442 var sl = FindLabel (gc.Item2);
5443 if (sl == null) {
5444 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5445 } else {
5446 gc.Item1.Label = sl;
5451 if (!ok)
5452 return false;
5454 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5455 ResolveStringSwitchMap (ec);
5459 // Anonymous storey initialization has to happen before
5460 // any generated switch dispatch
5462 block.InsertStatement (0, new DispatchStatement (this));
5464 return true;
5467 bool HasOnlyDefaultSection ()
5469 for (int i = 0; i < block.Statements.Count; ++i) {
5470 var s = block.Statements[i] as SwitchLabel;
5472 if (s == null || s.IsDefault)
5473 continue;
5475 return false;
5478 return true;
5481 public override Reachability MarkReachable (Reachability rc)
5483 if (rc.IsUnreachable)
5484 return rc;
5486 base.MarkReachable (rc);
5488 block.MarkReachableScope (rc);
5490 if (block.Statements.Count == 0)
5491 return rc;
5493 SwitchLabel constant_label = null;
5494 var constant = new_expr as Constant;
5496 if (constant != null) {
5497 constant_label = FindLabel (constant) ?? case_default;
5498 if (constant_label == null) {
5499 block.Statements.RemoveAt (0);
5500 return rc;
5504 var section_rc = new Reachability ();
5505 SwitchLabel prev_label = null;
5507 for (int i = 0; i < block.Statements.Count; ++i) {
5508 var s = block.Statements[i];
5509 var sl = s as SwitchLabel;
5511 if (sl != null && sl.SectionStart) {
5513 // Section is marked already via goto case
5515 if (!sl.IsUnreachable) {
5516 section_rc = new Reachability ();
5517 continue;
5520 if (section_rc.IsUnreachable) {
5522 // Common case. Previous label section end is unreachable as
5523 // it ends with break, return, etc. For next section revert
5524 // to reachable again unless we have constant switch block
5526 section_rc = constant_label != null && constant_label != sl ?
5527 Reachability.CreateUnreachable () :
5528 new Reachability ();
5529 } else if (prev_label != null) {
5531 // Error case as control cannot fall through from one case label
5533 sl.SectionStart = false;
5534 s = new MissingBreak (prev_label);
5535 s.MarkReachable (rc);
5536 block.Statements.Insert (i - 1, s);
5537 ++i;
5538 } else if (constant_label != null && constant_label != sl) {
5540 // Special case for the first unreachable label in constant
5541 // switch block
5543 section_rc = Reachability.CreateUnreachable ();
5546 prev_label = sl;
5549 section_rc = s.MarkReachable (section_rc);
5552 if (!section_rc.IsUnreachable && prev_label != null) {
5553 prev_label.SectionStart = false;
5554 var s = new MissingBreak (prev_label) {
5555 FallOut = true
5558 s.MarkReachable (rc);
5559 block.Statements.Add (s);
5563 // Reachability can affect parent only when all possible paths are handled but
5564 // we still need to run reachability check on switch body to check for fall-through
5566 if (case_default == null && constant_label == null)
5567 return rc;
5570 // We have at least one local exit from the switch
5572 if (end_reachable)
5573 return rc;
5575 return Reachability.CreateUnreachable ();
5578 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5580 if (goto_cases == null)
5581 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5583 goto_cases.Add (Tuple.Create (gotoCase, value));
5587 // Converts string switch into string hashtable
5589 void ResolveStringSwitchMap (ResolveContext ec)
5591 FullNamedExpression string_dictionary_type;
5592 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5593 string_dictionary_type = new TypeExpression (
5594 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5595 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5596 loc);
5597 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5598 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5599 } else {
5600 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5601 return;
5604 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5605 Field field = new Field (ctype, string_dictionary_type,
5606 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5607 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5608 if (!field.Define ())
5609 return;
5610 ctype.AddField (field);
5612 var init = new List<Expression> ();
5613 int counter = -1;
5614 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5615 string value = null;
5617 foreach (SwitchLabel sl in case_labels) {
5619 if (sl.SectionStart)
5620 labels.Add (++counter, sl);
5622 if (sl == case_default || sl == case_null)
5623 continue;
5625 value = (string) sl.Converted.GetValue ();
5626 var init_args = new List<Expression> (2);
5627 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5629 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5630 init_args.Add (sl.Converted);
5632 init.Add (new CollectionElementInitializer (init_args, loc));
5635 Arguments args = new Arguments (1);
5636 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5637 Expression initializer = new NewInitialize (string_dictionary_type, args,
5638 new CollectionOrObjectInitializers (init, loc), loc);
5640 switch_cache_field = new FieldExpr (field, loc);
5641 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5644 void DoEmitStringSwitch (EmitContext ec)
5646 Label l_initialized = ec.DefineLabel ();
5649 // Skip initialization when value is null
5651 value.EmitBranchable (ec, nullLabel, false);
5654 // Check if string dictionary is initialized and initialize
5656 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5657 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5658 string_dictionary.EmitStatement (ec);
5660 ec.MarkLabel (l_initialized);
5662 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5664 ResolveContext rc = new ResolveContext (ec.MemberContext);
5666 if (switch_cache_field.Type.IsGeneric) {
5667 Arguments get_value_args = new Arguments (2);
5668 get_value_args.Add (new Argument (value));
5669 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5670 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5671 if (get_item == null)
5672 return;
5675 // A value was not found, go to default case
5677 get_item.EmitBranchable (ec, defaultLabel, false);
5678 } else {
5679 Arguments get_value_args = new Arguments (1);
5680 get_value_args.Add (new Argument (value));
5682 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5683 if (get_item == null)
5684 return;
5686 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5687 get_item_object.EmitAssign (ec, get_item, true, false);
5688 ec.Emit (OpCodes.Brfalse, defaultLabel);
5690 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5691 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5693 get_item_int.EmitStatement (ec);
5694 get_item_object.Release (ec);
5697 EmitTableSwitch (ec, string_switch_variable);
5698 string_switch_variable.Release (ec);
5702 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5704 void EmitShortSwitch (EmitContext ec)
5706 MethodSpec equal_method = null;
5707 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5708 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5711 if (equal_method != null) {
5712 value.EmitBranchable (ec, nullLabel, false);
5715 for (int i = 0; i < case_labels.Count; ++i) {
5716 var label = case_labels [i];
5717 if (label == case_default || label == case_null)
5718 continue;
5720 var constant = label.Converted;
5722 if (constant == null) {
5723 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5724 continue;
5727 if (equal_method != null) {
5728 value.Emit (ec);
5729 constant.Emit (ec);
5731 var call = new CallEmitter ();
5732 call.EmitPredefined (ec, equal_method, new Arguments (0));
5733 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5734 continue;
5737 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5738 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5739 continue;
5742 value.Emit (ec);
5743 constant.Emit (ec);
5744 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5747 ec.Emit (OpCodes.Br, defaultLabel);
5750 void EmitDispatch (EmitContext ec)
5752 if (IsPatternMatching) {
5753 EmitShortSwitch (ec);
5754 return;
5757 if (value == null) {
5759 // Constant switch, we've already done the work if there is only 1 label
5760 // referenced
5762 int reachable = 0;
5763 foreach (var sl in case_labels) {
5764 if (sl.IsUnreachable)
5765 continue;
5767 if (reachable++ > 0) {
5768 var constant = (Constant) new_expr;
5769 var constant_label = FindLabel (constant) ?? case_default;
5771 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5772 break;
5776 return;
5779 if (string_dictionary != null) {
5780 DoEmitStringSwitch (ec);
5781 } else if (case_labels.Count < 4 || string_labels != null) {
5782 EmitShortSwitch (ec);
5783 } else {
5784 EmitTableSwitch (ec, value);
5788 protected override void DoEmit (EmitContext ec)
5791 // Setup the codegen context
5793 Label old_end = ec.LoopEnd;
5794 Switch old_switch = ec.Switch;
5796 ec.LoopEnd = ec.DefineLabel ();
5797 ec.Switch = this;
5799 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5800 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5802 if (value != null) {
5803 ec.Mark (loc);
5805 var switch_expr = new_expr ?? Expr;
5806 if (IsNullable) {
5807 unwrap.EmitCheck (ec);
5808 ec.Emit (OpCodes.Brfalse, nullLabel);
5809 value.EmitAssign (ec, switch_expr, false, false);
5810 } else if (switch_expr != value) {
5811 value.EmitAssign (ec, switch_expr, false, false);
5816 // Next statement is compiler generated we don't need extra
5817 // nop when we can use the statement for sequence point
5819 ec.Mark (block.StartLocation);
5820 block.IsCompilerGenerated = true;
5821 } else {
5822 new_expr.EmitSideEffect (ec);
5825 block.Emit (ec);
5827 // Restore context state.
5828 ec.MarkLabel (ec.LoopEnd);
5831 // Restore the previous context
5833 ec.LoopEnd = old_end;
5834 ec.Switch = old_switch;
5837 protected override void CloneTo (CloneContext clonectx, Statement t)
5839 Switch target = (Switch) t;
5841 target.Expr = Expr.Clone (clonectx);
5842 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5845 public override object Accept (StructuralVisitor visitor)
5847 return visitor.Visit (this);
5850 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5852 if (case_default == null && !(new_expr is Constant))
5853 return;
5855 if (end_reachable_das == null)
5856 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5858 end_reachable_das.Add (fc.DefiniteAssignment);
5861 public override void SetEndReachable ()
5863 end_reachable = true;
5867 // A place where execution can restart in a state machine
5868 public abstract class ResumableStatement : Statement
5870 bool prepared;
5871 protected Label resume_point;
5873 public Label PrepareForEmit (EmitContext ec)
5875 if (!prepared) {
5876 prepared = true;
5877 resume_point = ec.DefineLabel ();
5879 return resume_point;
5882 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5884 return end;
5887 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5892 public abstract class TryFinallyBlock : ExceptionStatement
5894 protected Statement stmt;
5895 Label dispose_try_block;
5896 bool prepared_for_dispose, emitted_dispose;
5897 Method finally_host;
5899 protected TryFinallyBlock (Statement stmt, Location loc)
5900 : base (loc)
5902 this.stmt = stmt;
5905 #region Properties
5907 public Statement Statement {
5908 get {
5909 return stmt;
5913 #endregion
5915 protected abstract void EmitTryBody (EmitContext ec);
5916 public abstract void EmitFinallyBody (EmitContext ec);
5918 public override Label PrepareForDispose (EmitContext ec, Label end)
5920 if (!prepared_for_dispose) {
5921 prepared_for_dispose = true;
5922 dispose_try_block = ec.DefineLabel ();
5924 return dispose_try_block;
5927 protected sealed override void DoEmit (EmitContext ec)
5929 EmitTryBodyPrepare (ec);
5930 EmitTryBody (ec);
5932 bool beginFinally = EmitBeginFinallyBlock (ec);
5934 Label start_finally = ec.DefineLabel ();
5935 if (resume_points != null && beginFinally) {
5936 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5938 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5939 ec.Emit (OpCodes.Brfalse_S, start_finally);
5940 ec.Emit (OpCodes.Endfinally);
5943 ec.MarkLabel (start_finally);
5945 if (finally_host != null) {
5946 finally_host.Define ();
5947 finally_host.PrepareEmit ();
5948 finally_host.Emit ();
5950 // Now it's safe to add, to close it properly and emit sequence points
5951 finally_host.Parent.AddMember (finally_host);
5953 var ce = new CallEmitter ();
5954 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5955 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5956 } else {
5957 EmitFinallyBody (ec);
5960 if (beginFinally)
5961 ec.EndExceptionBlock ();
5964 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5966 if (emitted_dispose)
5967 return;
5969 emitted_dispose = true;
5971 Label end_of_try = ec.DefineLabel ();
5973 // Ensure that the only way we can get into this code is through a dispatcher
5974 if (have_dispatcher)
5975 ec.Emit (OpCodes.Br, end);
5977 ec.BeginExceptionBlock ();
5979 ec.MarkLabel (dispose_try_block);
5981 Label[] labels = null;
5982 for (int i = 0; i < resume_points.Count; ++i) {
5983 ResumableStatement s = resume_points[i];
5984 Label ret = s.PrepareForDispose (ec, end_of_try);
5985 if (ret.Equals (end_of_try) && labels == null)
5986 continue;
5987 if (labels == null) {
5988 labels = new Label[resume_points.Count];
5989 for (int j = 0; j < i; ++j)
5990 labels[j] = end_of_try;
5992 labels[i] = ret;
5995 if (labels != null) {
5996 int j;
5997 for (j = 1; j < labels.Length; ++j)
5998 if (!labels[0].Equals (labels[j]))
5999 break;
6000 bool emit_dispatcher = j < labels.Length;
6002 if (emit_dispatcher) {
6003 ec.Emit (OpCodes.Ldloc, pc);
6004 ec.EmitInt (first_resume_pc);
6005 ec.Emit (OpCodes.Sub);
6006 ec.Emit (OpCodes.Switch, labels);
6009 foreach (ResumableStatement s in resume_points)
6010 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
6013 ec.MarkLabel (end_of_try);
6015 ec.BeginFinallyBlock ();
6017 if (finally_host != null) {
6018 var ce = new CallEmitter ();
6019 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
6020 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
6021 } else {
6022 EmitFinallyBody (ec);
6025 ec.EndExceptionBlock ();
6028 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6030 var res = stmt.FlowAnalysis (fc);
6031 parent_try_block = null;
6032 return res;
6035 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
6037 ec.BeginFinallyBlock ();
6038 return true;
6041 public override Reachability MarkReachable (Reachability rc)
6043 base.MarkReachable (rc);
6044 return Statement.MarkReachable (rc);
6047 public override bool Resolve (BlockContext bc)
6049 bool ok;
6051 parent_try_block = bc.CurrentTryBlock;
6052 bc.CurrentTryBlock = this;
6054 if (stmt is TryCatch) {
6055 ok = stmt.Resolve (bc);
6056 } else {
6057 using (bc.Set (ResolveContext.Options.TryScope)) {
6058 ok = stmt.Resolve (bc);
6062 bc.CurrentTryBlock = parent_try_block;
6065 // Finally block inside iterator is called from MoveNext and
6066 // Dispose methods that means we need to lift the block into
6067 // newly created host method to emit the body only once. The
6068 // original block then simply calls the newly generated method.
6070 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
6071 var b = stmt as Block;
6072 if (b != null && b.Explicit.HasYield) {
6073 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
6077 return base.Resolve (bc) && ok;
6082 // Base class for blocks using exception handling
6084 public abstract class ExceptionStatement : ResumableStatement
6086 protected List<ResumableStatement> resume_points;
6087 protected int first_resume_pc;
6088 protected ExceptionStatement parent_try_block;
6089 protected int first_catch_resume_pc = -1;
6091 protected ExceptionStatement (Location loc)
6093 this.loc = loc;
6096 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6098 StateMachineInitializer state_machine = null;
6099 if (resume_points != null) {
6100 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6102 ec.EmitInt ((int) IteratorStorey.State.Running);
6103 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6107 // The resume points in catch section when this is try-catch-finally
6109 if (IsRewrittenTryCatchFinally ()) {
6110 ec.BeginExceptionBlock ();
6112 if (first_catch_resume_pc >= 0) {
6114 ec.MarkLabel (resume_point);
6116 // For normal control flow, we want to fall-through the Switch
6117 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6118 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6119 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6120 ec.Emit (OpCodes.Sub);
6122 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6123 for (int i = 0; i < labels.Length; ++i)
6124 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6125 ec.Emit (OpCodes.Switch, labels);
6129 ec.BeginExceptionBlock ();
6132 // The resume points for try section
6134 if (resume_points != null && first_catch_resume_pc != 0) {
6135 if (first_catch_resume_pc < 0)
6136 ec.MarkLabel (resume_point);
6138 // For normal control flow, we want to fall-through the Switch
6139 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6140 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6141 ec.EmitInt (first_resume_pc);
6142 ec.Emit (OpCodes.Sub);
6144 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6145 for (int i = 0; i < labels.Length; ++i)
6146 labels[i] = resume_points[i].PrepareForEmit (ec);
6147 ec.Emit (OpCodes.Switch, labels);
6151 bool IsRewrittenTryCatchFinally ()
6153 var tf = this as TryFinally;
6154 if (tf == null)
6155 return false;
6157 var tc = tf.Statement as TryCatch;
6158 if (tc == null)
6159 return false;
6161 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6164 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6166 if (parent_try_block != null) {
6167 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6168 } else {
6169 pc = stateMachine.AddResumePoint (this);
6172 if (resume_points == null) {
6173 resume_points = new List<ResumableStatement> ();
6174 first_resume_pc = pc;
6177 if (pc != first_resume_pc + resume_points.Count)
6178 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6180 var tf = this as TryFinally;
6181 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6182 first_catch_resume_pc = resume_points.Count;
6185 resume_points.Add (stmt);
6186 return pc;
6190 public class Lock : TryFinallyBlock
6192 Expression expr;
6193 TemporaryVariableReference expr_copy;
6194 TemporaryVariableReference lock_taken;
6196 public Lock (Expression expr, Statement stmt, Location loc)
6197 : base (stmt, loc)
6199 this.expr = expr;
6202 public Expression Expr {
6203 get {
6204 return this.expr;
6208 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6210 expr.FlowAnalysis (fc);
6211 return base.DoFlowAnalysis (fc);
6214 public override bool Resolve (BlockContext ec)
6216 expr = expr.Resolve (ec);
6217 if (expr == null)
6218 return false;
6220 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6221 ec.Report.Error (185, loc,
6222 "`{0}' is not a reference type as required by the lock statement",
6223 expr.Type.GetSignatureForError ());
6226 if (expr.Type.IsGenericParameter) {
6227 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6230 VariableReference lv = expr as VariableReference;
6231 bool locked;
6232 if (lv != null) {
6233 locked = lv.IsLockedByStatement;
6234 lv.IsLockedByStatement = true;
6235 } else {
6236 lv = null;
6237 locked = false;
6241 // Have to keep original lock value around to unlock same location
6242 // in the case of original value has changed or is null
6244 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6245 expr_copy.Resolve (ec);
6248 // Ensure Monitor methods are available
6250 if (ResolvePredefinedMethods (ec) > 1) {
6251 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6252 lock_taken.Resolve (ec);
6255 using (ec.Set (ResolveContext.Options.LockScope)) {
6256 base.Resolve (ec);
6259 if (lv != null) {
6260 lv.IsLockedByStatement = locked;
6263 return true;
6266 protected override void EmitTryBodyPrepare (EmitContext ec)
6268 expr_copy.EmitAssign (ec, expr);
6270 if (lock_taken != null) {
6272 // Initialize ref variable
6274 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6275 } else {
6277 // Monitor.Enter (expr_copy)
6279 expr_copy.Emit (ec);
6280 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6283 base.EmitTryBodyPrepare (ec);
6286 protected override void EmitTryBody (EmitContext ec)
6289 // Monitor.Enter (expr_copy, ref lock_taken)
6291 if (lock_taken != null) {
6292 expr_copy.Emit (ec);
6293 lock_taken.LocalInfo.CreateBuilder (ec);
6294 lock_taken.AddressOf (ec, AddressOp.Load);
6295 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6298 Statement.Emit (ec);
6301 public override void EmitFinallyBody (EmitContext ec)
6304 // if (lock_taken) Monitor.Exit (expr_copy)
6306 Label skip = ec.DefineLabel ();
6308 if (lock_taken != null) {
6309 lock_taken.Emit (ec);
6310 ec.Emit (OpCodes.Brfalse_S, skip);
6313 expr_copy.Emit (ec);
6314 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6315 if (m != null)
6316 ec.Emit (OpCodes.Call, m);
6318 ec.MarkLabel (skip);
6321 int ResolvePredefinedMethods (ResolveContext rc)
6323 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6324 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6325 if (m != null)
6326 return 4;
6328 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6329 if (m != null)
6330 return 1;
6332 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6333 return 0;
6336 protected override void CloneTo (CloneContext clonectx, Statement t)
6338 Lock target = (Lock) t;
6340 target.expr = expr.Clone (clonectx);
6341 target.stmt = Statement.Clone (clonectx);
6344 public override object Accept (StructuralVisitor visitor)
6346 return visitor.Visit (this);
6351 public class Unchecked : Statement {
6352 public Block Block;
6354 public Unchecked (Block b, Location loc)
6356 Block = b;
6357 b.Unchecked = true;
6358 this.loc = loc;
6361 public override bool Resolve (BlockContext ec)
6363 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6364 return Block.Resolve (ec);
6367 protected override void DoEmit (EmitContext ec)
6369 using (ec.With (EmitContext.Options.CheckedScope, false))
6370 Block.Emit (ec);
6373 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6375 return Block.FlowAnalysis (fc);
6378 public override Reachability MarkReachable (Reachability rc)
6380 base.MarkReachable (rc);
6381 return Block.MarkReachable (rc);
6384 protected override void CloneTo (CloneContext clonectx, Statement t)
6386 Unchecked target = (Unchecked) t;
6388 target.Block = clonectx.LookupBlock (Block);
6391 public override object Accept (StructuralVisitor visitor)
6393 return visitor.Visit (this);
6397 public class Checked : Statement {
6398 public Block Block;
6400 public Checked (Block b, Location loc)
6402 Block = b;
6403 b.Unchecked = false;
6404 this.loc = loc;
6407 public override bool Resolve (BlockContext ec)
6409 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6410 return Block.Resolve (ec);
6413 protected override void DoEmit (EmitContext ec)
6415 using (ec.With (EmitContext.Options.CheckedScope, true))
6416 Block.Emit (ec);
6419 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6421 return Block.FlowAnalysis (fc);
6424 public override Reachability MarkReachable (Reachability rc)
6426 base.MarkReachable (rc);
6427 return Block.MarkReachable (rc);
6430 protected override void CloneTo (CloneContext clonectx, Statement t)
6432 Checked target = (Checked) t;
6434 target.Block = clonectx.LookupBlock (Block);
6437 public override object Accept (StructuralVisitor visitor)
6439 return visitor.Visit (this);
6443 public class Unsafe : Statement {
6444 public Block Block;
6446 public Unsafe (Block b, Location loc)
6448 Block = b;
6449 Block.Unsafe = true;
6450 this.loc = loc;
6453 public override bool Resolve (BlockContext ec)
6455 if (ec.CurrentIterator != null)
6456 Expression.UnsafeInsideIteratorError (ec, loc);
6458 using (ec.Set (ResolveContext.Options.UnsafeScope))
6459 return Block.Resolve (ec);
6462 protected override void DoEmit (EmitContext ec)
6464 Block.Emit (ec);
6467 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6469 return Block.FlowAnalysis (fc);
6472 public override Reachability MarkReachable (Reachability rc)
6474 base.MarkReachable (rc);
6475 return Block.MarkReachable (rc);
6478 protected override void CloneTo (CloneContext clonectx, Statement t)
6480 Unsafe target = (Unsafe) t;
6482 target.Block = clonectx.LookupBlock (Block);
6485 public override object Accept (StructuralVisitor visitor)
6487 return visitor.Visit (this);
6492 // Fixed statement
6494 public class Fixed : Statement
6496 abstract class Emitter : ShimExpression
6498 protected LocalVariable vi;
6500 protected Emitter (Expression expr, LocalVariable li)
6501 : base (expr)
6503 vi = li;
6506 public abstract void EmitExit (EmitContext ec);
6508 public override void FlowAnalysis (FlowAnalysisContext fc)
6510 expr.FlowAnalysis (fc);
6514 sealed class ExpressionEmitter : Emitter {
6515 public ExpressionEmitter (Expression converted, LocalVariable li)
6516 : base (converted, li)
6520 protected override Expression DoResolve (ResolveContext rc)
6522 throw new NotImplementedException ();
6525 public override void Emit (EmitContext ec) {
6527 // Store pointer in pinned location
6529 expr.Emit (ec);
6530 vi.EmitAssign (ec);
6533 public override void EmitExit (EmitContext ec)
6535 ec.EmitInt (0);
6536 ec.Emit (OpCodes.Conv_U);
6537 vi.EmitAssign (ec);
6541 class StringEmitter : Emitter
6543 LocalVariable pinned_string;
6545 public StringEmitter (Expression expr, LocalVariable li)
6546 : base (expr, li)
6550 protected override Expression DoResolve (ResolveContext rc)
6552 pinned_string = new LocalVariable (vi.Block, "$pinned",
6553 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6554 vi.Location);
6555 pinned_string.Type = rc.BuiltinTypes.String;
6556 vi.IsFixed = false;
6558 eclass = ExprClass.Variable;
6559 type = rc.BuiltinTypes.Int;
6560 return this;
6563 public override void Emit (EmitContext ec)
6565 pinned_string.CreateBuilder (ec);
6567 expr.Emit (ec);
6568 pinned_string.EmitAssign (ec);
6570 // TODO: Should use Binary::Add
6571 pinned_string.Emit (ec);
6572 ec.Emit (OpCodes.Conv_U);
6574 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6575 if (m == null)
6576 return;
6578 var null_value = ec.DefineLabel ();
6579 vi.EmitAssign (ec);
6580 vi.Emit (ec);
6581 ec.Emit (OpCodes.Brfalse_S, null_value);
6583 vi.Emit (ec);
6584 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6585 //pe.InstanceExpression = pinned_string;
6586 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6588 ec.Emit (OpCodes.Add);
6589 vi.EmitAssign (ec);
6591 ec.MarkLabel (null_value);
6594 public override void EmitExit (EmitContext ec)
6596 ec.EmitNull ();
6597 pinned_string.EmitAssign (ec);
6601 public class VariableDeclaration : BlockVariable
6603 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6604 : base (type, li)
6608 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6610 if (!Variable.Type.IsPointer && li == Variable) {
6611 bc.Report.Error (209, TypeExpression.Location,
6612 "The type of locals declared in a fixed statement must be a pointer type");
6613 return null;
6616 var res = initializer.Resolve (bc);
6617 if (res == null)
6618 return null;
6621 // Case 1: Array
6623 var ac = res.Type as ArrayContainer;
6624 if (ac != null) {
6625 TypeSpec array_type = ac.Element;
6628 // Provided that array_type is unmanaged,
6630 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6631 return null;
6633 Expression res_init;
6634 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6635 res_init = res;
6636 } else {
6637 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6638 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6639 res = expr_variable.CreateReferenceExpression (bc, loc);
6643 // and T* is implicitly convertible to the
6644 // pointer type given in the fixed statement.
6646 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6648 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6649 if (converted == null)
6650 return null;
6653 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6655 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6656 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6657 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6658 new NullLiteral (loc),
6659 converted, loc);
6661 converted = converted.Resolve (bc);
6663 return new ExpressionEmitter (converted, li);
6667 // Case 2: string
6669 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6670 return new StringEmitter (res, li).Resolve (bc);
6673 // Case 3: fixed buffer
6674 if (res is FixedBufferPtr) {
6675 return new ExpressionEmitter (res, li);
6679 // Case 4: & object.
6681 Unary u = res as Unary;
6682 if (u != null) {
6683 bool already_fixed = true;
6685 if (u.Oper == Unary.Operator.AddressOf) {
6686 IVariableReference vr = u.Expr as IVariableReference;
6687 if (vr == null || !vr.IsFixed) {
6688 already_fixed = false;
6692 if (already_fixed) {
6693 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6694 return null;
6697 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6698 return new ExpressionEmitter (res, li);
6701 if (initializer is Cast) {
6702 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6703 return null;
6707 // Case 5: by-ref GetPinnableReference method on the rhs expression
6709 var method = GetPinnableReference (bc, res);
6710 if (method == null) {
6711 bc.Report.Error (8385, initializer.Location, "The given expression cannot be used in a fixed statement");
6712 return null;
6715 var compiler = bc.Module.Compiler;
6716 if (compiler.Settings.Version < LanguageVersion.V_7_3) {
6717 bc.Report.FeatureIsNotAvailable (compiler, initializer.Location, "extensible fixed statement");
6720 method.InstanceExpression = res;
6721 res = new Invocation.Predefined (method, null).ResolveLValue (bc, EmptyExpression.OutAccess);
6722 if (res == null)
6723 return null;
6725 ReferenceContainer rType = (ReferenceContainer)method.BestCandidateReturnType;
6726 PointerContainer lType = li.Type as PointerContainer;
6727 if (rType.Element != lType?.Element) {
6728 // CSC: Should be better error code
6729 res.Error_ValueCannotBeConverted (bc, lType, false);
6730 return null;
6733 li.SetIsPointerByRef ();
6734 return new ExpressionEmitter (res, li);
6737 MethodGroupExpr GetPinnableReference (BlockContext bc, Expression expr)
6739 TypeSpec type = expr.Type;
6740 var mexpr = Expression.MemberLookup (bc, false, type,
6741 "GetPinnableReference", 0, Expression.MemberLookupRestrictions.ExactArity, loc);
6743 if (mexpr == null)
6744 return null;
6746 var mg = mexpr as MethodGroupExpr;
6747 if (mg == null)
6748 return null;
6750 mg.InstanceExpression = expr;
6752 // TODO: handle extension methods
6753 Arguments args = new Arguments (0);
6754 mg = mg.OverloadResolve (bc, ref args, null, OverloadResolver.Restrictions.None);
6756 if (mg == null || mg.BestCandidate.IsStatic || !mg.BestCandidate.IsPublic || mg.BestCandidateReturnType.Kind != MemberKind.ByRef || !mg.BestCandidate.Parameters.IsEmpty) {
6757 if (bc.Module.Compiler.Settings.Version > LanguageVersion.V_7_2) {
6758 bc.Report.Warning (280, 2, expr.Location, "`{0}' has the wrong signature to be used in extensible fixed statement", mg.GetSignatureForError ());
6761 return null;
6764 return mg;
6769 VariableDeclaration decl;
6770 Statement statement;
6771 bool has_ret;
6773 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6775 this.decl = decl;
6776 statement = stmt;
6777 loc = l;
6780 #region Properties
6782 public Statement Statement {
6783 get {
6784 return statement;
6788 public BlockVariable Variables {
6789 get {
6790 return decl;
6794 #endregion
6796 public override bool Resolve (BlockContext bc)
6798 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6799 if (!decl.Resolve (bc))
6800 return false;
6803 return statement.Resolve (bc);
6806 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6808 decl.FlowAnalysis (fc);
6809 return statement.FlowAnalysis (fc);
6812 protected override void DoEmit (EmitContext ec)
6814 decl.Variable.CreateBuilder (ec);
6815 decl.Initializer.Emit (ec);
6816 if (decl.Declarators != null) {
6817 foreach (var d in decl.Declarators) {
6818 d.Variable.CreateBuilder (ec);
6819 d.Initializer.Emit (ec);
6823 statement.Emit (ec);
6825 if (has_ret)
6826 return;
6829 // Clear the pinned variable
6831 ((Emitter) decl.Initializer).EmitExit (ec);
6832 if (decl.Declarators != null) {
6833 foreach (var d in decl.Declarators) {
6834 ((Emitter)d.Initializer).EmitExit (ec);
6839 public override Reachability MarkReachable (Reachability rc)
6841 base.MarkReachable (rc);
6843 decl.MarkReachable (rc);
6845 rc = statement.MarkReachable (rc);
6847 // TODO: What if there is local exit?
6848 has_ret = rc.IsUnreachable;
6849 return rc;
6852 protected override void CloneTo (CloneContext clonectx, Statement t)
6854 Fixed target = (Fixed) t;
6856 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6857 target.statement = statement.Clone (clonectx);
6860 public override object Accept (StructuralVisitor visitor)
6862 return visitor.Visit (this);
6866 public class Catch : Statement
6868 class CatchVariableStore : Statement
6870 readonly Catch ctch;
6872 public CatchVariableStore (Catch ctch)
6874 this.ctch = ctch;
6877 protected override void CloneTo (CloneContext clonectx, Statement target)
6881 protected override void DoEmit (EmitContext ec)
6883 // Emits catch variable debug information inside correct block
6884 ctch.EmitCatchVariableStore (ec);
6887 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6889 return true;
6893 class FilterStatement : Statement
6895 readonly Catch ctch;
6897 public FilterStatement (Catch ctch)
6899 this.ctch = ctch;
6902 protected override void CloneTo (CloneContext clonectx, Statement target)
6906 protected override void DoEmit (EmitContext ec)
6908 if (ctch.li != null) {
6909 if (ctch.hoisted_temp != null)
6910 ctch.hoisted_temp.Emit (ec);
6911 else
6912 ctch.li.Emit (ec);
6914 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6915 ec.Emit (OpCodes.Box, ctch.type);
6918 var expr_start = ec.DefineLabel ();
6919 var end = ec.DefineLabel ();
6921 ec.Emit (OpCodes.Brtrue_S, expr_start);
6922 ec.EmitInt (0);
6923 ec.Emit (OpCodes.Br, end);
6924 ec.MarkLabel (expr_start);
6926 ctch.Filter.Emit (ec);
6928 ec.MarkLabel (end);
6929 ec.Emit (OpCodes.Endfilter);
6930 ec.BeginFilterHandler ();
6931 ec.Emit (OpCodes.Pop);
6934 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6936 ctch.Filter.FlowAnalysis (fc);
6937 return true;
6940 public override bool Resolve (BlockContext bc)
6942 ctch.Filter = ctch.Filter.Resolve (bc);
6944 if (ctch.Filter != null) {
6945 if (ctch.Filter.ContainsEmitWithAwait ()) {
6946 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6949 var c = ctch.Filter as Constant;
6950 if (c != null && !c.IsDefaultValue) {
6951 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6955 return true;
6959 ExplicitBlock block;
6960 LocalVariable li;
6961 FullNamedExpression type_expr;
6962 CompilerAssign assign;
6963 TypeSpec type;
6964 LocalTemporary hoisted_temp;
6966 public Catch (ExplicitBlock block, Location loc)
6968 this.block = block;
6969 this.loc = loc;
6972 #region Properties
6974 public ExplicitBlock Block {
6975 get {
6976 return block;
6980 public TypeSpec CatchType {
6981 get {
6982 return type;
6986 public Expression Filter {
6987 get; set;
6990 public bool IsGeneral {
6991 get {
6992 return type_expr == null;
6996 public FullNamedExpression TypeExpression {
6997 get {
6998 return type_expr;
7000 set {
7001 type_expr = value;
7005 public LocalVariable Variable {
7006 get {
7007 return li;
7009 set {
7010 li = value;
7014 #endregion
7016 protected override void DoEmit (EmitContext ec)
7018 if (Filter != null) {
7019 ec.BeginExceptionFilterBlock ();
7020 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
7022 if (Block.HasAwait) {
7023 Block.EmitScopeInitialization (ec);
7024 } else {
7025 Block.Emit (ec);
7028 return;
7031 if (IsGeneral)
7032 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
7033 else
7034 ec.BeginCatchBlock (CatchType);
7036 if (li == null)
7037 ec.Emit (OpCodes.Pop);
7039 if (Block.HasAwait) {
7040 if (li != null)
7041 EmitCatchVariableStore (ec);
7042 } else {
7043 Block.Emit (ec);
7047 void EmitCatchVariableStore (EmitContext ec)
7049 li.CreateBuilder (ec);
7052 // For hoisted catch variable we have to use a temporary local variable
7053 // for captured variable initialization during storey setup because variable
7054 // needs to be on the stack after storey instance for stfld operation
7056 if (li.HoistedVariant != null) {
7057 hoisted_temp = new LocalTemporary (li.Type);
7058 hoisted_temp.Store (ec);
7060 // switch to assignment from temporary variable and not from top of the stack
7061 assign.UpdateSource (hoisted_temp);
7065 public override bool Resolve (BlockContext bc)
7067 using (bc.Set (ResolveContext.Options.CatchScope)) {
7068 if (type_expr == null) {
7069 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
7070 if (!block.HasAwait || Filter != null)
7071 block.AddScopeStatement (new CatchVariableStore (this));
7073 Expression source = new EmptyExpression (li.Type);
7074 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
7075 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
7077 } else {
7078 type = type_expr.ResolveAsType (bc);
7079 if (type == null)
7080 return false;
7082 if (li == null)
7083 CreateExceptionVariable (type);
7085 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
7086 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
7087 } else if (li != null) {
7088 li.Type = type;
7089 li.PrepareAssignmentAnalysis (bc);
7091 // source variable is at the top of the stack
7092 Expression source = new EmptyExpression (li.Type);
7093 if (li.Type.IsGenericParameter)
7094 source = new UnboxCast (source, li.Type);
7096 if (!block.HasAwait || Filter != null)
7097 block.AddScopeStatement (new CatchVariableStore (this));
7100 // Uses Location.Null to hide from symbol file
7102 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
7103 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
7107 if (Filter != null) {
7108 Block.AddScopeStatement (new FilterStatement (this));
7111 Block.SetCatchBlock ();
7112 return Block.Resolve (bc);
7116 bool CreateExceptionVariable (TypeSpec type)
7118 if (!Block.HasAwait)
7119 return false;
7121 // TODO: Scan the block for rethrow expression
7122 //if (!Block.HasRethrow)
7123 // return;
7125 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
7126 return true;
7129 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7131 if (li != null && !li.IsCompilerGenerated) {
7132 fc.SetVariableAssigned (li.VariableInfo, true);
7135 return block.FlowAnalysis (fc);
7138 public override Reachability MarkReachable (Reachability rc)
7140 base.MarkReachable (rc);
7142 var c = Filter as Constant;
7143 if (c != null && c.IsDefaultValue)
7144 return Reachability.CreateUnreachable ();
7146 return block.MarkReachable (rc);
7149 protected override void CloneTo (CloneContext clonectx, Statement t)
7151 Catch target = (Catch) t;
7153 if (type_expr != null)
7154 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
7156 if (Filter != null)
7157 target.Filter = Filter.Clone (clonectx);
7159 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
7163 public class TryFinally : TryFinallyBlock
7165 ExplicitBlock fini;
7166 List<DefiniteAssignmentBitSet> try_exit_dat;
7167 List<Tuple<Label, bool>> redirected_jumps;
7168 Label? start_fin_label;
7170 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7171 : base (stmt, loc)
7173 this.fini = fini;
7176 public ExplicitBlock FinallyBlock {
7177 get {
7178 return fini;
7182 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7184 if (try_exit_dat == null)
7185 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7187 try_exit_dat.Add (vector);
7190 public override bool Resolve (BlockContext bc)
7192 bool ok = base.Resolve (bc);
7194 fini.SetFinallyBlock ();
7195 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7196 ok &= fini.Resolve (bc);
7199 return ok;
7202 protected override void EmitTryBody (EmitContext ec)
7204 if (fini.HasAwait) {
7205 if (ec.TryFinallyUnwind == null)
7206 ec.TryFinallyUnwind = new List<TryFinally> ();
7208 ec.TryFinallyUnwind.Add (this);
7209 stmt.Emit (ec);
7211 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7212 ec.EndExceptionBlock ();
7214 ec.TryFinallyUnwind.Remove (this);
7216 if (start_fin_label != null)
7217 ec.MarkLabel (start_fin_label.Value);
7219 return;
7222 stmt.Emit (ec);
7225 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7227 if (fini.HasAwait)
7228 return false;
7230 return base.EmitBeginFinallyBlock (ec);
7233 public override void EmitFinallyBody (EmitContext ec)
7235 if (!fini.HasAwait) {
7236 fini.Emit (ec);
7237 return;
7241 // Emits catch block like
7243 // catch (object temp) {
7244 // this.exception_field = temp;
7245 // }
7247 var type = ec.BuiltinTypes.Object;
7248 ec.BeginCatchBlock (type);
7250 var temp = ec.GetTemporaryLocal (type);
7251 ec.Emit (OpCodes.Stloc, temp);
7253 var exception_field = ec.GetTemporaryField (type);
7254 exception_field.AutomaticallyReuse = false;
7255 ec.EmitThis ();
7256 ec.Emit (OpCodes.Ldloc, temp);
7257 exception_field.EmitAssignFromStack (ec);
7259 ec.EndExceptionBlock ();
7261 ec.FreeTemporaryLocal (temp, type);
7263 fini.Emit (ec);
7266 // Emits exception rethrow
7268 // if (this.exception_field != null)
7269 // throw this.exception_field;
7271 exception_field.Emit (ec);
7272 var skip_throw = ec.DefineLabel ();
7273 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7274 exception_field.Emit (ec);
7275 ec.Emit (OpCodes.Throw);
7276 ec.MarkLabel (skip_throw);
7278 exception_field.PrepareCleanup (ec);
7280 EmitUnwindFinallyTable (ec);
7283 bool IsParentBlock (Block block)
7285 for (Block b = fini; b != null; b = b.Parent) {
7286 if (b == block)
7287 return true;
7290 return false;
7293 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
7295 int idx;
7296 if (labelBlock != null) {
7297 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7298 var fin = ec.TryFinallyUnwind [idx - 1];
7299 if (!fin.IsParentBlock (labelBlock))
7300 break;
7302 } else {
7303 idx = 0;
7306 bool set_return_state = true;
7308 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7309 var fin = ec.TryFinallyUnwind [idx];
7310 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7311 break;
7313 fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
7314 set_return_state = false;
7316 if (fin.start_fin_label == null) {
7317 fin.start_fin_label = ec.DefineLabel ();
7320 label = fin.start_fin_label.Value;
7323 return label;
7326 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
7328 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
7331 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
7333 if (redirected_jumps == null) {
7334 redirected_jumps = new List<Tuple<Label, bool>> ();
7336 // Add fallthrough label
7337 redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
7339 if (setReturnState)
7340 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7343 int index = redirected_jumps.FindIndex (l => l.Item1 == label);
7344 if (index < 0) {
7345 redirected_jumps.Add (Tuple.Create (label, unwindProtect));
7346 index = redirected_jumps.Count - 1;
7350 // Indicates we have captured exit jump
7352 if (setReturnState) {
7353 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7354 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7359 // Emits state table of jumps outside of try block and reload of return
7360 // value when try block returns value
7362 void EmitUnwindFinallyTable (EmitContext ec)
7364 if (redirected_jumps == null)
7365 return;
7367 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7368 initializer.HoistedReturnState.EmitLoad (ec);
7370 var jumps_table = new Label [redirected_jumps.Count];
7371 List<Tuple<Label, Label>> leave_redirect = null;
7372 for (int i = 0; i < jumps_table.Length; ++i) {
7373 var val = redirected_jumps [i];
7375 if (val.Item2) {
7376 if (leave_redirect == null)
7377 leave_redirect = new List<Tuple<Label, Label>> ();
7378 var label = ec.DefineLabel ();
7379 leave_redirect.Add (Tuple.Create (label, val.Item1));
7380 jumps_table [i] = label;
7381 } else {
7382 jumps_table [i] = val.Item1;
7386 ec.Emit (OpCodes.Switch, jumps_table);
7388 if (leave_redirect != null) {
7389 foreach (var entry in leave_redirect) {
7390 ec.MarkLabel (entry.Item1);
7391 ec.Emit (OpCodes.Leave, entry.Item2);
7395 // Mark fallthrough label
7396 ec.MarkLabel (jumps_table [0]);
7399 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7401 var da = fc.BranchDefiniteAssignment ();
7403 var tf = fc.TryFinally;
7404 fc.TryFinally = this;
7406 var res_stmt = Statement.FlowAnalysis (fc);
7408 fc.TryFinally = tf;
7410 var try_da = fc.DefiniteAssignment;
7411 fc.DefiniteAssignment = da;
7413 var res_fin = fini.FlowAnalysis (fc);
7415 if (try_exit_dat != null) {
7417 // try block has global exit but we need to run definite assignment check
7418 // for parameter block out parameter after finally block because it's always
7419 // executed before exit
7421 foreach (var try_da_part in try_exit_dat)
7422 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7424 try_exit_dat = null;
7427 fc.DefiniteAssignment |= try_da;
7428 return res_stmt | res_fin;
7431 public override Reachability MarkReachable (Reachability rc)
7434 // Mark finally block first for any exit statement in try block
7435 // to know whether the code which follows finally is reachable
7437 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7440 protected override void CloneTo (CloneContext clonectx, Statement t)
7442 TryFinally target = (TryFinally) t;
7444 target.stmt = stmt.Clone (clonectx);
7445 if (fini != null)
7446 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7449 public override object Accept (StructuralVisitor visitor)
7451 return visitor.Visit (this);
7455 public class TryCatch : ExceptionStatement
7457 public Block Block;
7458 List<Catch> clauses;
7459 readonly bool inside_try_finally;
7460 List<Catch> catch_sm;
7462 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7463 : base (l)
7465 this.Block = block;
7466 this.clauses = catch_clauses;
7467 this.inside_try_finally = inside_try_finally;
7470 public List<Catch> Clauses {
7471 get {
7472 return clauses;
7476 public bool HasClauseWithAwait {
7477 get {
7478 return catch_sm != null;
7482 public bool IsTryCatchFinally {
7483 get {
7484 return inside_try_finally;
7488 public override bool Resolve (BlockContext bc)
7490 bool ok;
7492 using (bc.Set (ResolveContext.Options.TryScope)) {
7494 parent_try_block = bc.CurrentTryBlock;
7496 if (IsTryCatchFinally) {
7497 ok = Block.Resolve (bc);
7498 } else {
7499 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7500 bc.CurrentTryBlock = this;
7501 ok = Block.Resolve (bc);
7502 bc.CurrentTryBlock = parent_try_block;
7507 var prev_catch = bc.CurrentTryCatch;
7508 bc.CurrentTryCatch = this;
7510 for (int i = 0; i < clauses.Count; ++i) {
7511 var c = clauses[i];
7513 ok &= c.Resolve (bc);
7515 if (c.Block.HasAwait) {
7516 if (catch_sm == null)
7517 catch_sm = new List<Catch> ();
7519 catch_sm.Add (c);
7522 if (c.Filter != null)
7523 continue;
7525 TypeSpec resolved_type = c.CatchType;
7526 if (resolved_type == null)
7527 continue;
7529 for (int ii = 0; ii < clauses.Count; ++ii) {
7530 if (ii == i)
7531 continue;
7533 if (clauses[ii].Filter != null)
7534 continue;
7536 if (clauses[ii].IsGeneral) {
7537 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7538 continue;
7540 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7541 continue;
7543 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7544 continue;
7546 bc.Report.Warning (1058, 1, c.loc,
7547 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7549 continue;
7552 if (ii >= i)
7553 continue;
7555 var ct = clauses[ii].CatchType;
7556 if (ct == null)
7557 continue;
7559 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7560 bc.Report.Error (160, c.loc,
7561 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7562 ct.GetSignatureForError ());
7563 ok = false;
7568 bc.CurrentTryCatch = prev_catch;
7570 return base.Resolve (bc) && ok;
7573 protected sealed override void DoEmit (EmitContext ec)
7575 if (!inside_try_finally)
7576 EmitTryBodyPrepare (ec);
7578 Block.Emit (ec);
7580 LocalBuilder state_variable = null;
7581 foreach (Catch c in clauses) {
7582 c.Emit (ec);
7584 if (catch_sm != null) {
7585 if (state_variable == null) {
7587 // Cannot reuse temp variable because non-catch path assumes the value is 0
7588 // which may not be true for reused local variable
7590 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7593 var index = catch_sm.IndexOf (c);
7594 if (index < 0)
7595 continue;
7597 ec.EmitInt (index + 1);
7598 ec.Emit (OpCodes.Stloc, state_variable);
7602 if (state_variable == null) {
7603 if (!inside_try_finally)
7604 ec.EndExceptionBlock ();
7605 } else {
7606 ec.EndExceptionBlock ();
7608 ec.Emit (OpCodes.Ldloc, state_variable);
7610 var labels = new Label [catch_sm.Count + 1];
7611 for (int i = 0; i < labels.Length; ++i) {
7612 labels [i] = ec.DefineLabel ();
7615 var end = ec.DefineLabel ();
7616 ec.Emit (OpCodes.Switch, labels);
7618 // 0 value is default label
7619 ec.MarkLabel (labels [0]);
7620 ec.Emit (OpCodes.Br, end);
7622 var atv = ec.AsyncThrowVariable;
7623 Catch c = null;
7624 for (int i = 0; i < catch_sm.Count; ++i) {
7625 if (c != null && c.Block.HasReachableClosingBrace)
7626 ec.Emit (OpCodes.Br, end);
7628 ec.MarkLabel (labels [i + 1]);
7630 ec.EmitInt (0);
7631 ec.Emit (OpCodes.Stloc, state_variable);
7633 c = catch_sm [i];
7634 ec.AsyncThrowVariable = c.Variable;
7635 c.Block.Emit (ec);
7637 ec.AsyncThrowVariable = atv;
7639 ec.MarkLabel (end);
7643 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7645 var start_fc = fc.BranchDefiniteAssignment ();
7646 var res = Block.FlowAnalysis (fc);
7648 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7650 foreach (var c in clauses) {
7651 fc.BranchDefiniteAssignment (start_fc);
7652 if (!c.FlowAnalysis (fc)) {
7653 if (try_fc == null)
7654 try_fc = fc.DefiniteAssignment;
7655 else
7656 try_fc &= fc.DefiniteAssignment;
7658 res = false;
7662 fc.DefiniteAssignment = try_fc ?? start_fc;
7663 parent_try_block = null;
7664 return res;
7667 public override Reachability MarkReachable (Reachability rc)
7669 if (rc.IsUnreachable)
7670 return rc;
7672 base.MarkReachable (rc);
7674 var tc_rc = Block.MarkReachable (rc);
7676 foreach (var c in clauses)
7677 tc_rc &= c.MarkReachable (rc);
7679 return tc_rc;
7682 protected override void CloneTo (CloneContext clonectx, Statement t)
7684 TryCatch target = (TryCatch) t;
7686 target.Block = clonectx.LookupBlock (Block);
7687 if (clauses != null){
7688 target.clauses = new List<Catch> ();
7689 foreach (Catch c in clauses)
7690 target.clauses.Add ((Catch) c.Clone (clonectx));
7694 public override object Accept (StructuralVisitor visitor)
7696 return visitor.Visit (this);
7700 public class Using : TryFinallyBlock
7702 public class VariableDeclaration : BlockVariable
7704 Statement dispose_call;
7706 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7707 : base (type, li)
7711 public VariableDeclaration (LocalVariable li, Location loc)
7712 : base (li)
7714 reachable = true;
7715 this.loc = loc;
7718 public VariableDeclaration (Expression expr)
7719 : base (null)
7721 loc = expr.Location;
7722 Initializer = expr;
7725 #region Properties
7727 public bool IsNested { get; private set; }
7729 #endregion
7731 public void EmitDispose (EmitContext ec)
7733 dispose_call.Emit (ec);
7736 public override bool Resolve (BlockContext bc)
7738 if (IsNested)
7739 return true;
7741 return base.Resolve (bc, false);
7744 public Expression ResolveExpression (BlockContext bc)
7746 var e = Initializer.Resolve (bc);
7747 if (e == null)
7748 return null;
7750 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7751 Initializer = ResolveInitializer (bc, Variable, e);
7752 return e;
7755 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7757 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7758 initializer = initializer.Resolve (bc);
7759 if (initializer == null)
7760 return null;
7762 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7763 Arguments args = new Arguments (1);
7764 args.Add (new Argument (initializer));
7765 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7766 if (initializer == null)
7767 return null;
7769 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7770 dispose_call = CreateDisposeCall (bc, var);
7771 dispose_call.Resolve (bc);
7773 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7776 if (li == Variable) {
7777 CheckIDiposableConversion (bc, li, initializer);
7778 dispose_call = CreateDisposeCall (bc, li);
7779 dispose_call.Resolve (bc);
7782 return base.ResolveInitializer (bc, li, initializer);
7785 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7787 var type = li.Type;
7789 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7790 if (type.IsNullableType) {
7791 // it's handled in CreateDisposeCall
7792 return;
7795 if (type != InternalType.ErrorType) {
7796 bc.Report.SymbolRelatedToPreviousError (type);
7797 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7798 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7799 type.GetSignatureForError ());
7802 return;
7806 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7808 var target = bc.BuiltinTypes.IDisposable;
7809 var tp = type as TypeParameterSpec;
7810 if (tp != null)
7811 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7813 return type.ImplementsInterface (target, false);
7816 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7818 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7819 var type = lv.Type;
7820 var loc = lv.Location;
7822 var idt = bc.BuiltinTypes.IDisposable;
7823 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7825 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7826 dispose_mg.InstanceExpression = type.IsNullableType ?
7827 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7828 lvr;
7831 // Hide it from symbol file via null location
7833 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7835 // Add conditional call when disposing possible null variable
7836 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7837 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7839 return dispose;
7842 public void ResolveDeclaratorInitializer (BlockContext bc)
7844 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7847 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7849 for (int i = declarators.Count - 1; i >= 0; --i) {
7850 var d = declarators [i];
7851 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7852 vd.Initializer = d.Initializer;
7853 vd.IsNested = true;
7854 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7855 vd.dispose_call.Resolve (bc);
7857 stmt = new Using (vd, stmt, d.Variable.Location);
7860 declarators = null;
7861 return stmt;
7864 public override object Accept (StructuralVisitor visitor)
7866 return visitor.Visit (this);
7870 VariableDeclaration decl;
7872 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7873 : base (stmt, loc)
7875 this.decl = decl;
7878 public Using (Expression expr, Statement stmt, Location loc)
7879 : base (stmt, loc)
7881 this.decl = new VariableDeclaration (expr);
7884 #region Properties
7886 public Expression Expr {
7887 get {
7888 return decl.Variable == null ? decl.Initializer : null;
7892 public BlockVariable Variables {
7893 get {
7894 return decl;
7898 #endregion
7900 public override void Emit (EmitContext ec)
7903 // Don't emit sequence point it will be set on variable declaration
7905 DoEmit (ec);
7908 protected override void EmitTryBodyPrepare (EmitContext ec)
7910 decl.Emit (ec);
7911 base.EmitTryBodyPrepare (ec);
7914 protected override void EmitTryBody (EmitContext ec)
7916 stmt.Emit (ec);
7919 public override void EmitFinallyBody (EmitContext ec)
7921 decl.EmitDispose (ec);
7924 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7926 decl.FlowAnalysis (fc);
7927 return stmt.FlowAnalysis (fc);
7930 public override Reachability MarkReachable (Reachability rc)
7932 decl.MarkReachable (rc);
7933 return base.MarkReachable (rc);
7936 public override bool Resolve (BlockContext ec)
7938 VariableReference vr;
7939 bool vr_locked = false;
7941 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7942 if (decl.Variable == null) {
7943 vr = decl.ResolveExpression (ec) as VariableReference;
7944 if (vr != null) {
7945 vr_locked = vr.IsLockedByStatement;
7946 vr.IsLockedByStatement = true;
7948 } else {
7949 if (decl.IsNested) {
7950 decl.ResolveDeclaratorInitializer (ec);
7951 } else {
7952 if (!decl.Resolve (ec))
7953 return false;
7955 if (decl.Declarators != null) {
7956 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7960 vr = null;
7964 var ok = base.Resolve (ec);
7966 if (vr != null)
7967 vr.IsLockedByStatement = vr_locked;
7969 return ok;
7972 protected override void CloneTo (CloneContext clonectx, Statement t)
7974 Using target = (Using) t;
7976 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7977 target.stmt = stmt.Clone (clonectx);
7980 public override object Accept (StructuralVisitor visitor)
7982 return visitor.Visit (this);
7986 /// <summary>
7987 /// Implementation of the foreach C# statement
7988 /// </summary>
7989 public class Foreach : LoopStatement
7991 abstract class IteratorStatement : Statement
7993 protected readonly Foreach for_each;
7995 protected IteratorStatement (Foreach @foreach)
7997 this.for_each = @foreach;
7998 this.loc = @foreach.expr.Location;
8001 protected override void CloneTo (CloneContext clonectx, Statement target)
8003 throw new NotImplementedException ();
8006 public override void Emit (EmitContext ec)
8008 if (ec.EmitAccurateDebugInfo) {
8009 ec.Emit (OpCodes.Nop);
8012 base.Emit (ec);
8015 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8017 throw new NotImplementedException ();
8021 sealed class ArrayForeach : IteratorStatement
8023 TemporaryVariableReference[] lengths;
8024 Expression [] length_exprs;
8025 StatementExpression[] counter;
8026 TemporaryVariableReference[] variables;
8028 TemporaryVariableReference copy;
8030 public ArrayForeach (Foreach @foreach, int rank)
8031 : base (@foreach)
8033 counter = new StatementExpression[rank];
8034 variables = new TemporaryVariableReference[rank];
8035 length_exprs = new Expression [rank];
8038 // Only use temporary length variables when dealing with
8039 // multi-dimensional arrays
8041 if (rank > 1)
8042 lengths = new TemporaryVariableReference [rank];
8045 public override bool Resolve (BlockContext ec)
8047 Block variables_block = for_each.variable.Block;
8048 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
8049 copy.Resolve (ec);
8051 int rank = length_exprs.Length;
8052 Arguments list = new Arguments (rank);
8053 for (int i = 0; i < rank; i++) {
8054 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
8055 variables[i] = v;
8056 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
8057 counter[i].Resolve (ec);
8059 if (rank == 1) {
8060 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
8061 } else {
8062 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
8063 lengths[i].Resolve (ec);
8065 Arguments args = new Arguments (1);
8066 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
8067 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
8070 list.Add (new Argument (v));
8073 var access = new ElementAccess (copy, list, loc).Resolve (ec);
8074 if (access == null)
8075 return false;
8077 TypeSpec var_type;
8078 if (for_each.type is VarExpr) {
8079 // Infer implicitly typed local variable from foreach array type
8080 var_type = access.Type;
8081 } else {
8082 var_type = for_each.type.ResolveAsType (ec);
8084 if (var_type == null)
8085 return false;
8087 access = Convert.ExplicitConversion (ec, access, var_type, loc);
8088 if (access == null)
8089 return false;
8092 for_each.variable.Type = var_type;
8094 var prev_block = ec.CurrentBlock;
8095 ec.CurrentBlock = variables_block;
8096 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
8097 ec.CurrentBlock = prev_block;
8099 if (variable_ref == null)
8100 return false;
8102 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
8104 return for_each.body.Resolve (ec);
8107 protected override void DoEmit (EmitContext ec)
8109 copy.EmitAssign (ec, for_each.expr);
8111 int rank = length_exprs.Length;
8112 Label[] test = new Label [rank];
8113 Label[] loop = new Label [rank];
8115 for (int i = 0; i < rank; i++) {
8116 test [i] = ec.DefineLabel ();
8117 loop [i] = ec.DefineLabel ();
8119 if (lengths != null)
8120 lengths [i].EmitAssign (ec, length_exprs [i]);
8123 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
8124 for (int i = 0; i < rank; i++) {
8125 variables [i].EmitAssign (ec, zero);
8127 ec.Emit (OpCodes.Br, test [i]);
8128 ec.MarkLabel (loop [i]);
8131 for_each.body.Emit (ec);
8133 ec.MarkLabel (ec.LoopBegin);
8134 ec.Mark (for_each.expr.Location);
8136 for (int i = rank - 1; i >= 0; i--){
8137 counter [i].Emit (ec);
8139 ec.MarkLabel (test [i]);
8140 variables [i].Emit (ec);
8142 if (lengths != null)
8143 lengths [i].Emit (ec);
8144 else
8145 length_exprs [i].Emit (ec);
8147 ec.Emit (OpCodes.Blt, loop [i]);
8150 ec.MarkLabel (ec.LoopEnd);
8154 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
8156 class RuntimeDispose : Using.VariableDeclaration
8158 public RuntimeDispose (LocalVariable lv, Location loc)
8159 : base (lv, loc)
8161 reachable = true;
8164 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
8166 // Defered to runtime check
8169 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
8171 var idt = bc.BuiltinTypes.IDisposable;
8174 // Fabricates code like
8176 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8179 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
8181 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
8182 dispose_variable.CreateReferenceExpression (bc, loc),
8183 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
8184 loc), new NullLiteral (loc));
8186 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
8188 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
8189 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
8191 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
8192 return new If (idisaposable_test, dispose, loc);
8196 LocalVariable variable;
8197 Expression expr;
8198 Statement statement;
8199 ExpressionStatement init;
8200 TemporaryVariableReference enumerator_variable;
8201 bool ambiguous_getenumerator_name;
8203 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8204 : base (@foreach)
8206 this.variable = var;
8207 this.expr = expr;
8210 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8212 rc.Report.SymbolRelatedToPreviousError (enumerator);
8213 rc.Report.Error (202, loc,
8214 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8215 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8218 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8221 // Option 1: Try to match by name GetEnumerator first
8223 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8224 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8226 var mg = mexpr as MethodGroupExpr;
8227 if (mg != null) {
8228 mg.InstanceExpression = expr;
8229 Arguments args = new Arguments (0);
8230 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8232 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8233 if (ambiguous_getenumerator_name)
8234 mg = null;
8236 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8237 return mg;
8242 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8244 var t = expr.Type;
8245 PredefinedMember<MethodSpec> iface_candidate = null;
8246 var ptypes = rc.Module.PredefinedTypes;
8247 var gen_ienumerable = ptypes.IEnumerableGeneric;
8248 if (!gen_ienumerable.Define ())
8249 gen_ienumerable = null;
8251 var ifaces = t.Interfaces;
8252 if (ifaces != null) {
8253 foreach (var iface in ifaces) {
8254 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8255 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8256 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8257 rc.Report.Error (1640, loc,
8258 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8259 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8261 return null;
8264 // TODO: Cache this somehow
8265 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8266 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8268 continue;
8271 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8272 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8277 if (iface_candidate == null) {
8278 if (expr.Type == InternalType.DefaultType) {
8279 rc.Report.Error (8312, loc, "Use of default literal is not valid in this context");
8280 } else if (expr.Type != InternalType.ErrorType) {
8281 rc.Report.Error (1579, loc,
8282 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8283 expr.Type.GetSignatureForError (), "GetEnumerator");
8286 return null;
8289 var method = iface_candidate.Resolve (loc);
8290 if (method == null)
8291 return null;
8293 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8294 mg.InstanceExpression = expr;
8295 return mg;
8298 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8300 var ms = MemberCache.FindMember (enumerator.ReturnType,
8301 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8302 BindingRestriction.InstanceOnly) as MethodSpec;
8304 if (ms == null || !ms.IsPublic) {
8305 Error_WrongEnumerator (rc, enumerator);
8306 return null;
8309 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8312 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8314 var ps = MemberCache.FindMember (enumerator.ReturnType,
8315 MemberFilter.Property ("Current", null),
8316 BindingRestriction.InstanceOnly) as PropertySpec;
8318 if (ps == null || !ps.IsPublic) {
8319 Error_WrongEnumerator (rc, enumerator);
8320 return null;
8323 return ps;
8326 public override bool Resolve (BlockContext ec)
8328 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8330 if (is_dynamic) {
8331 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8332 } else if (expr.Type.IsNullableType) {
8333 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8336 var get_enumerator_mg = ResolveGetEnumerator (ec);
8337 if (get_enumerator_mg == null) {
8338 return false;
8341 var get_enumerator = get_enumerator_mg.BestCandidate;
8342 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8343 enumerator_variable.Resolve (ec);
8345 // Prepare bool MoveNext ()
8346 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8347 if (move_next_mg == null) {
8348 return false;
8351 move_next_mg.InstanceExpression = enumerator_variable;
8353 // Prepare ~T~ Current { get; }
8354 var current_prop = ResolveCurrent (ec, get_enumerator);
8355 if (current_prop == null) {
8356 return false;
8359 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8360 if (current_pe == null)
8361 return false;
8363 VarExpr ve = for_each.type as VarExpr;
8365 if (ve != null) {
8366 if (is_dynamic) {
8367 // Source type is dynamic, set element type to dynamic too
8368 variable.Type = ec.BuiltinTypes.Dynamic;
8369 } else {
8370 // Infer implicitly typed local variable from foreach enumerable type
8371 variable.Type = current_pe.Type;
8373 } else {
8374 if (is_dynamic) {
8375 // Explicit cast of dynamic collection elements has to be done at runtime
8376 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8379 variable.Type = for_each.type.ResolveAsType (ec);
8381 if (variable.Type == null)
8382 return false;
8384 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8385 if (current_pe == null)
8386 return false;
8389 var prev_block = ec.CurrentBlock;
8390 ec.CurrentBlock = for_each.variable.Block;
8391 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8392 ec.CurrentBlock = prev_block;
8393 if (variable_ref == null)
8394 return false;
8396 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8398 var init = new Invocation.Predefined (get_enumerator_mg, null);
8400 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8401 for_each.body, Location.Null);
8403 var enum_type = enumerator_variable.Type;
8406 // Add Dispose method call when enumerator can be IDisposable
8408 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8409 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8411 // Runtime Dispose check
8413 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8414 vd.Initializer = init;
8415 statement = new Using (vd, statement, Location.Null);
8416 } else {
8418 // No Dispose call needed
8420 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8421 this.init.Resolve (ec);
8423 } else {
8425 // Static Dispose check
8427 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8428 vd.Initializer = init;
8429 statement = new Using (vd, statement, Location.Null);
8432 return statement.Resolve (ec);
8435 protected override void DoEmit (EmitContext ec)
8437 enumerator_variable.LocalInfo.CreateBuilder (ec);
8439 if (init != null)
8440 init.EmitStatement (ec);
8442 statement.Emit (ec);
8445 #region IErrorHandler Members
8447 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8449 ec.Report.SymbolRelatedToPreviousError (best);
8450 ec.Report.Warning (278, 2, expr.Location,
8451 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8452 expr.Type.GetSignatureForError (), "enumerable",
8453 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8455 ambiguous_getenumerator_name = true;
8456 return true;
8459 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8461 return false;
8464 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8466 return false;
8469 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8471 return false;
8474 #endregion
8477 Expression type;
8478 LocalVariable variable;
8479 Expression expr;
8480 Block body;
8482 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8483 : base (stmt)
8485 this.type = type;
8486 this.variable = var;
8487 this.expr = expr;
8488 this.body = body;
8489 loc = l;
8492 public Expression Expr {
8493 get { return expr; }
8496 public Expression TypeExpression {
8497 get { return type; }
8500 public LocalVariable Variable {
8501 get { return variable; }
8504 public override Reachability MarkReachable (Reachability rc)
8506 base.MarkReachable (rc);
8508 body.MarkReachable (rc);
8510 return rc;
8513 public override bool Resolve (BlockContext ec)
8515 expr = expr.Resolve (ec);
8516 if (expr == null)
8517 return false;
8519 if (expr.IsNull) {
8520 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8521 return false;
8524 body.AddStatement (Statement);
8526 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8527 Statement = new ArrayForeach (this, 1);
8528 } else if (expr.Type is ArrayContainer) {
8529 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8530 } else {
8531 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8532 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8533 expr.ExprClassName);
8534 return false;
8537 Statement = new CollectionForeach (this, variable, expr);
8540 return base.Resolve (ec);
8543 protected override void DoEmit (EmitContext ec)
8545 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8546 ec.LoopBegin = ec.DefineLabel ();
8547 ec.LoopEnd = ec.DefineLabel ();
8549 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8550 body.Explicit.DisableDebugScopeIndex ();
8552 variable.CreateBuilder (ec);
8554 Statement.Emit (ec);
8556 ec.EndScope ();
8558 ec.LoopBegin = old_begin;
8559 ec.LoopEnd = old_end;
8562 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8564 expr.FlowAnalysis (fc);
8566 var da = fc.BranchDefiniteAssignment ();
8567 body.FlowAnalysis (fc);
8568 fc.DefiniteAssignment = da;
8569 return false;
8572 protected override void CloneTo (CloneContext clonectx, Statement t)
8574 Foreach target = (Foreach) t;
8576 target.type = type.Clone (clonectx);
8577 target.expr = expr.Clone (clonectx);
8578 target.body = (Block) body.Clone (clonectx);
8579 target.Statement = Statement.Clone (clonectx);
8582 public override object Accept (StructuralVisitor visitor)
8584 return visitor.Visit (this);
8588 class SentinelStatement: Statement
8590 protected override void CloneTo (CloneContext clonectx, Statement target)
8594 protected override void DoEmit (EmitContext ec)
8596 var l = ec.DefineLabel ();
8597 ec.MarkLabel (l);
8598 ec.Emit (OpCodes.Br_S, l);
8601 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8603 throw new NotImplementedException ();