2009-07-20 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / statement.cs
blob66e58d3a364c837545a0ff05dda33900f15c30a3
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
13 using System;
14 using System.Text;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections;
19 using System.Collections.Specialized;
21 namespace Mono.CSharp {
23 public abstract class Statement {
24 public Location loc;
26 /// <summary>
27 /// Resolves the statement, true means that all sub-statements
28 /// did resolve ok.
29 // </summary>
30 public virtual bool Resolve (EmitContext ec)
32 return true;
35 /// <summary>
36 /// We already know that the statement is unreachable, but we still
37 /// need to resolve it to catch errors.
38 /// </summary>
39 public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
42 // This conflicts with csc's way of doing this, but IMHO it's
43 // the right thing to do.
45 // If something is unreachable, we still check whether it's
46 // correct. This means that you cannot use unassigned variables
47 // in unreachable code, for instance.
50 if (warn)
51 Report.Warning (162, 2, loc, "Unreachable code detected");
53 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
54 bool ok = Resolve (ec);
55 ec.KillFlowBranching ();
57 return ok;
60 /// <summary>
61 /// Return value indicates whether all code paths emitted return.
62 /// </summary>
63 protected abstract void DoEmit (EmitContext ec);
65 /// <summary>
66 /// Utility wrapper routine for Error, just to beautify the code
67 /// </summary>
68 public void Error (int error, string format, params object[] args)
70 Error (error, String.Format (format, args));
73 public void Error (int error, string s)
75 if (!loc.IsNull)
76 Report.Error (error, loc, s);
77 else
78 Report.Error (error, s);
81 public virtual void Emit (EmitContext ec)
83 ec.Mark (loc);
84 DoEmit (ec);
88 // This routine must be overrided in derived classes and make copies
89 // of all the data that might be modified if resolved
90 //
91 protected abstract void CloneTo (CloneContext clonectx, Statement target);
93 public Statement Clone (CloneContext clonectx)
95 Statement s = (Statement) this.MemberwiseClone ();
96 CloneTo (clonectx, s);
97 return s;
100 public virtual Expression CreateExpressionTree (EmitContext ec)
102 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
103 return null;
106 public Statement PerformClone ()
108 CloneContext clonectx = new CloneContext ();
110 return Clone (clonectx);
113 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
117 // This class is used during the Statement.Clone operation
118 // to remap objects that have been cloned.
120 // Since blocks are cloned by Block.Clone, we need a way for
121 // expressions that must reference the block to be cloned
122 // pointing to the new cloned block.
124 public class CloneContext {
125 Hashtable block_map = new Hashtable ();
126 Hashtable variable_map;
128 public void AddBlockMap (Block from, Block to)
130 if (block_map.Contains (from))
131 return;
132 block_map [from] = to;
135 public Block LookupBlock (Block from)
137 Block result = (Block) block_map [from];
139 if (result == null){
140 result = (Block) from.Clone (this);
141 block_map [from] = result;
144 return result;
148 /// Remaps block to cloned copy if one exists.
150 public Block RemapBlockCopy (Block from)
152 Block mapped_to = (Block)block_map[from];
153 if (mapped_to == null)
154 return from;
156 return mapped_to;
159 public void AddVariableMap (LocalInfo from, LocalInfo to)
161 if (variable_map == null)
162 variable_map = new Hashtable ();
164 if (variable_map.Contains (from))
165 return;
166 variable_map [from] = to;
169 public LocalInfo LookupVariable (LocalInfo from)
171 LocalInfo result = (LocalInfo) variable_map [from];
173 if (result == null)
174 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
176 return result;
180 public sealed class EmptyStatement : Statement {
182 private EmptyStatement () {}
184 public static readonly EmptyStatement Value = new EmptyStatement ();
186 public override bool Resolve (EmitContext ec)
188 return true;
191 public override bool ResolveUnreachable (EmitContext ec, bool warn)
193 return true;
196 protected override void DoEmit (EmitContext ec)
200 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
204 protected override void CloneTo (CloneContext clonectx, Statement target)
206 // nothing needed.
210 public class If : Statement {
211 Expression expr;
212 public Statement TrueStatement;
213 public Statement FalseStatement;
215 bool is_true_ret;
217 public If (Expression expr, Statement true_statement, Location l)
219 this.expr = expr;
220 TrueStatement = true_statement;
221 loc = l;
224 public If (Expression expr,
225 Statement true_statement,
226 Statement false_statement,
227 Location l)
229 this.expr = expr;
230 TrueStatement = true_statement;
231 FalseStatement = false_statement;
232 loc = l;
235 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
237 expr.MutateHoistedGenericType (storey);
238 TrueStatement.MutateHoistedGenericType (storey);
239 if (FalseStatement != null)
240 FalseStatement.MutateHoistedGenericType (storey);
243 public override bool Resolve (EmitContext ec)
245 bool ok = true;
247 Report.Debug (1, "START IF BLOCK", loc);
249 expr = Expression.ResolveBoolean (ec, expr, loc);
250 if (expr == null){
251 ok = false;
252 goto skip;
255 Assign ass = expr as Assign;
256 if (ass != null && ass.Source is Constant) {
257 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
261 // Dead code elimination
263 if (expr is Constant){
264 bool take = !((Constant) expr).IsDefaultValue;
266 if (take){
267 if (!TrueStatement.Resolve (ec))
268 return false;
270 if ((FalseStatement != null) &&
271 !FalseStatement.ResolveUnreachable (ec, true))
272 return false;
273 FalseStatement = null;
274 } else {
275 if (!TrueStatement.ResolveUnreachable (ec, true))
276 return false;
277 TrueStatement = null;
279 if ((FalseStatement != null) &&
280 !FalseStatement.Resolve (ec))
281 return false;
284 return true;
286 skip:
287 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
289 ok &= TrueStatement.Resolve (ec);
291 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
293 ec.CurrentBranching.CreateSibling ();
295 if (FalseStatement != null)
296 ok &= FalseStatement.Resolve (ec);
298 ec.EndFlowBranching ();
300 Report.Debug (1, "END IF BLOCK", loc);
302 return ok;
305 protected override void DoEmit (EmitContext ec)
307 ILGenerator ig = ec.ig;
308 Label false_target = ig.DefineLabel ();
309 Label end;
312 // If we're a boolean constant, Resolve() already
313 // eliminated dead code for us.
315 Constant c = expr as Constant;
316 if (c != null){
317 c.EmitSideEffect (ec);
319 if (!c.IsDefaultValue)
320 TrueStatement.Emit (ec);
321 else if (FalseStatement != null)
322 FalseStatement.Emit (ec);
324 return;
327 expr.EmitBranchable (ec, false_target, false);
329 TrueStatement.Emit (ec);
331 if (FalseStatement != null){
332 bool branch_emitted = false;
334 end = ig.DefineLabel ();
335 if (!is_true_ret){
336 ig.Emit (OpCodes.Br, end);
337 branch_emitted = true;
340 ig.MarkLabel (false_target);
341 FalseStatement.Emit (ec);
343 if (branch_emitted)
344 ig.MarkLabel (end);
345 } else {
346 ig.MarkLabel (false_target);
350 protected override void CloneTo (CloneContext clonectx, Statement t)
352 If target = (If) t;
354 target.expr = expr.Clone (clonectx);
355 target.TrueStatement = TrueStatement.Clone (clonectx);
356 if (FalseStatement != null)
357 target.FalseStatement = FalseStatement.Clone (clonectx);
361 public class Do : Statement {
362 public Expression expr;
363 public Statement EmbeddedStatement;
365 public Do (Statement statement, Expression bool_expr, Location l)
367 expr = bool_expr;
368 EmbeddedStatement = statement;
369 loc = l;
372 public override bool Resolve (EmitContext ec)
374 bool ok = true;
376 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
378 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
380 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
381 if (!EmbeddedStatement.Resolve (ec))
382 ok = false;
383 ec.EndFlowBranching ();
385 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
386 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
388 expr = Expression.ResolveBoolean (ec, expr, loc);
389 if (expr == null)
390 ok = false;
391 else if (expr is Constant){
392 bool infinite = !((Constant) expr).IsDefaultValue;
393 if (infinite)
394 ec.CurrentBranching.CurrentUsageVector.Goto ();
397 ec.EndFlowBranching ();
399 return ok;
402 protected override void DoEmit (EmitContext ec)
404 ILGenerator ig = ec.ig;
405 Label loop = ig.DefineLabel ();
406 Label old_begin = ec.LoopBegin;
407 Label old_end = ec.LoopEnd;
409 ec.LoopBegin = ig.DefineLabel ();
410 ec.LoopEnd = ig.DefineLabel ();
412 ig.MarkLabel (loop);
413 EmbeddedStatement.Emit (ec);
414 ig.MarkLabel (ec.LoopBegin);
417 // Dead code elimination
419 if (expr is Constant){
420 bool res = !((Constant) expr).IsDefaultValue;
422 expr.EmitSideEffect (ec);
423 if (res)
424 ec.ig.Emit (OpCodes.Br, loop);
425 } else
426 expr.EmitBranchable (ec, loop, true);
428 ig.MarkLabel (ec.LoopEnd);
430 ec.LoopBegin = old_begin;
431 ec.LoopEnd = old_end;
434 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
436 expr.MutateHoistedGenericType (storey);
437 EmbeddedStatement.MutateHoistedGenericType (storey);
440 protected override void CloneTo (CloneContext clonectx, Statement t)
442 Do target = (Do) t;
444 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
445 target.expr = expr.Clone (clonectx);
449 public class While : Statement {
450 public Expression expr;
451 public Statement Statement;
452 bool infinite, empty;
454 public While (Expression bool_expr, Statement statement, Location l)
456 this.expr = bool_expr;
457 Statement = statement;
458 loc = l;
461 public override bool Resolve (EmitContext ec)
463 bool ok = true;
465 expr = Expression.ResolveBoolean (ec, expr, loc);
466 if (expr == null)
467 return false;
470 // Inform whether we are infinite or not
472 if (expr is Constant){
473 bool value = !((Constant) expr).IsDefaultValue;
475 if (value == false){
476 if (!Statement.ResolveUnreachable (ec, true))
477 return false;
478 empty = true;
479 return true;
480 } else
481 infinite = true;
484 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
485 if (!infinite)
486 ec.CurrentBranching.CreateSibling ();
488 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
489 if (!Statement.Resolve (ec))
490 ok = false;
491 ec.EndFlowBranching ();
493 // There's no direct control flow from the end of the embedded statement to the end of the loop
494 ec.CurrentBranching.CurrentUsageVector.Goto ();
496 ec.EndFlowBranching ();
498 return ok;
501 protected override void DoEmit (EmitContext ec)
503 if (empty) {
504 expr.EmitSideEffect (ec);
505 return;
508 ILGenerator ig = ec.ig;
509 Label old_begin = ec.LoopBegin;
510 Label old_end = ec.LoopEnd;
512 ec.LoopBegin = ig.DefineLabel ();
513 ec.LoopEnd = ig.DefineLabel ();
516 // Inform whether we are infinite or not
518 if (expr is Constant){
519 // expr is 'true', since the 'empty' case above handles the 'false' case
520 ig.MarkLabel (ec.LoopBegin);
521 expr.EmitSideEffect (ec);
522 Statement.Emit (ec);
523 ig.Emit (OpCodes.Br, ec.LoopBegin);
526 // Inform that we are infinite (ie, `we return'), only
527 // if we do not `break' inside the code.
529 ig.MarkLabel (ec.LoopEnd);
530 } else {
531 Label while_loop = ig.DefineLabel ();
533 ig.Emit (OpCodes.Br, ec.LoopBegin);
534 ig.MarkLabel (while_loop);
536 Statement.Emit (ec);
538 ig.MarkLabel (ec.LoopBegin);
539 ec.Mark (loc);
541 expr.EmitBranchable (ec, while_loop, true);
543 ig.MarkLabel (ec.LoopEnd);
546 ec.LoopBegin = old_begin;
547 ec.LoopEnd = old_end;
550 public override void Emit (EmitContext ec)
552 DoEmit (ec);
555 protected override void CloneTo (CloneContext clonectx, Statement t)
557 While target = (While) t;
559 target.expr = expr.Clone (clonectx);
560 target.Statement = Statement.Clone (clonectx);
563 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
565 expr.MutateHoistedGenericType (storey);
566 Statement.MutateHoistedGenericType (storey);
570 public class For : Statement {
571 Expression Test;
572 Statement InitStatement;
573 Statement Increment;
574 public Statement Statement;
575 bool infinite, empty;
577 public For (Statement init_statement,
578 Expression test,
579 Statement increment,
580 Statement statement,
581 Location l)
583 InitStatement = init_statement;
584 Test = test;
585 Increment = increment;
586 Statement = statement;
587 loc = l;
590 public override bool Resolve (EmitContext ec)
592 bool ok = true;
594 if (InitStatement != null){
595 if (!InitStatement.Resolve (ec))
596 ok = false;
599 if (Test != null){
600 Test = Expression.ResolveBoolean (ec, Test, loc);
601 if (Test == null)
602 ok = false;
603 else if (Test is Constant){
604 bool value = !((Constant) Test).IsDefaultValue;
606 if (value == false){
607 if (!Statement.ResolveUnreachable (ec, true))
608 return false;
609 if ((Increment != null) &&
610 !Increment.ResolveUnreachable (ec, false))
611 return false;
612 empty = true;
613 return true;
614 } else
615 infinite = true;
617 } else
618 infinite = true;
620 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
621 if (!infinite)
622 ec.CurrentBranching.CreateSibling ();
624 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
626 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
627 if (!Statement.Resolve (ec))
628 ok = false;
629 ec.EndFlowBranching ();
631 if (Increment != null){
632 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
633 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
634 ok = false;
635 } else {
636 if (!Increment.Resolve (ec))
637 ok = false;
641 // There's no direct control flow from the end of the embedded statement to the end of the loop
642 ec.CurrentBranching.CurrentUsageVector.Goto ();
644 ec.EndFlowBranching ();
646 return ok;
649 protected override void DoEmit (EmitContext ec)
651 if (InitStatement != null && InitStatement != EmptyStatement.Value)
652 InitStatement.Emit (ec);
654 if (empty) {
655 Test.EmitSideEffect (ec);
656 return;
659 ILGenerator ig = ec.ig;
660 Label old_begin = ec.LoopBegin;
661 Label old_end = ec.LoopEnd;
662 Label loop = ig.DefineLabel ();
663 Label test = ig.DefineLabel ();
665 ec.LoopBegin = ig.DefineLabel ();
666 ec.LoopEnd = ig.DefineLabel ();
668 ig.Emit (OpCodes.Br, test);
669 ig.MarkLabel (loop);
670 Statement.Emit (ec);
672 ig.MarkLabel (ec.LoopBegin);
673 if (Increment != EmptyStatement.Value)
674 Increment.Emit (ec);
676 ig.MarkLabel (test);
678 // If test is null, there is no test, and we are just
679 // an infinite loop
681 if (Test != null){
683 // The Resolve code already catches the case for
684 // Test == Constant (false) so we know that
685 // this is true
687 if (Test is Constant) {
688 Test.EmitSideEffect (ec);
689 ig.Emit (OpCodes.Br, loop);
690 } else {
691 Test.EmitBranchable (ec, loop, true);
694 } else
695 ig.Emit (OpCodes.Br, loop);
696 ig.MarkLabel (ec.LoopEnd);
698 ec.LoopBegin = old_begin;
699 ec.LoopEnd = old_end;
702 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
704 if (InitStatement != null)
705 InitStatement.MutateHoistedGenericType (storey);
706 if (Test != null)
707 Test.MutateHoistedGenericType (storey);
708 if (Increment != null)
709 Increment.MutateHoistedGenericType (storey);
711 Statement.MutateHoistedGenericType (storey);
714 protected override void CloneTo (CloneContext clonectx, Statement t)
716 For target = (For) t;
718 if (InitStatement != null)
719 target.InitStatement = InitStatement.Clone (clonectx);
720 if (Test != null)
721 target.Test = Test.Clone (clonectx);
722 if (Increment != null)
723 target.Increment = Increment.Clone (clonectx);
724 target.Statement = Statement.Clone (clonectx);
728 public class StatementExpression : Statement {
729 ExpressionStatement expr;
731 public StatementExpression (ExpressionStatement expr)
733 this.expr = expr;
734 loc = expr.Location;
737 public override bool Resolve (EmitContext ec)
739 if (expr != null)
740 expr = expr.ResolveStatement (ec);
741 return expr != null;
744 protected override void DoEmit (EmitContext ec)
746 expr.EmitStatement (ec);
749 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
751 expr.MutateHoistedGenericType (storey);
754 public override string ToString ()
756 return "StatementExpression (" + expr + ")";
759 protected override void CloneTo (CloneContext clonectx, Statement t)
761 StatementExpression target = (StatementExpression) t;
763 target.expr = (ExpressionStatement) expr.Clone (clonectx);
767 // A 'return' or a 'yield break'
768 public abstract class ExitStatement : Statement
770 protected bool unwind_protect;
771 protected abstract bool DoResolve (EmitContext ec);
773 public virtual void Error_FinallyClause ()
775 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
778 public sealed override bool Resolve (EmitContext ec)
780 if (!DoResolve (ec))
781 return false;
783 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
784 if (unwind_protect)
785 ec.NeedReturnLabel ();
786 ec.CurrentBranching.CurrentUsageVector.Goto ();
787 return true;
791 /// <summary>
792 /// Implements the return statement
793 /// </summary>
794 public class Return : ExitStatement {
795 protected Expression Expr;
796 public Return (Expression expr, Location l)
798 Expr = expr;
799 loc = l;
802 protected override bool DoResolve (EmitContext ec)
804 if (Expr == null) {
805 if (ec.ReturnType == TypeManager.void_type)
806 return true;
808 Error (126, "An object of a type convertible to `{0}' is required " +
809 "for the return statement",
810 TypeManager.CSharpName (ec.ReturnType));
811 return false;
814 if (ec.CurrentBlock.Toplevel.IsIterator) {
815 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
816 "statement to return a value, or yield break to end the iteration");
819 AnonymousExpression am = ec.CurrentAnonymousMethod;
820 if (am == null && ec.ReturnType == TypeManager.void_type) {
821 MemberCore mc = ec.ResolveContext as MemberCore;
822 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
823 mc.GetSignatureForError ());
826 Expr = Expr.Resolve (ec);
827 if (Expr == null)
828 return false;
830 if (Expr.Type != ec.ReturnType) {
831 if (ec.InferReturnType) {
833 // void cannot be used in contextual return
835 if (Expr.Type == TypeManager.void_type)
836 return false;
838 ec.ReturnType = Expr.Type;
839 } else {
840 Expr = Convert.ImplicitConversionRequired (
841 ec, Expr, ec.ReturnType, loc);
843 if (Expr == null) {
844 if (am != null) {
845 Report.Error (1662, loc,
846 "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",
847 am.ContainerType, am.GetSignatureForError ());
849 return false;
854 return true;
857 protected override void DoEmit (EmitContext ec)
859 if (Expr != null) {
860 Expr.Emit (ec);
862 if (unwind_protect)
863 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
866 if (unwind_protect)
867 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
868 else
869 ec.ig.Emit (OpCodes.Ret);
872 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
874 if (Expr != null)
875 Expr.MutateHoistedGenericType (storey);
878 protected override void CloneTo (CloneContext clonectx, Statement t)
880 Return target = (Return) t;
881 // It's null for simple return;
882 if (Expr != null)
883 target.Expr = Expr.Clone (clonectx);
887 public class Goto : Statement {
888 string target;
889 LabeledStatement label;
890 bool unwind_protect;
892 public override bool Resolve (EmitContext ec)
894 int errors = Report.Errors;
895 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
896 ec.CurrentBranching.CurrentUsageVector.Goto ();
897 return errors == Report.Errors;
900 public Goto (string label, Location l)
902 loc = l;
903 target = label;
906 public string Target {
907 get { return target; }
910 public void SetResolvedTarget (LabeledStatement label)
912 this.label = label;
913 label.AddReference ();
916 protected override void CloneTo (CloneContext clonectx, Statement target)
918 // Nothing to clone
921 protected override void DoEmit (EmitContext ec)
923 if (label == null)
924 throw new InternalErrorException ("goto emitted before target resolved");
925 Label l = label.LabelTarget (ec);
926 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
929 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
934 public class LabeledStatement : Statement {
935 string name;
936 bool defined;
937 bool referenced;
938 Label label;
939 ILGenerator ig;
941 FlowBranching.UsageVector vectors;
943 public LabeledStatement (string name, Location l)
945 this.name = name;
946 this.loc = l;
949 public Label LabelTarget (EmitContext ec)
951 if (defined)
952 return label;
953 ig = ec.ig;
954 label = ec.ig.DefineLabel ();
955 defined = true;
957 return label;
960 public string Name {
961 get { return name; }
964 public bool IsDefined {
965 get { return defined; }
968 public bool HasBeenReferenced {
969 get { return referenced; }
972 public FlowBranching.UsageVector JumpOrigins {
973 get { return vectors; }
976 public void AddUsageVector (FlowBranching.UsageVector vector)
978 vector = vector.Clone ();
979 vector.Next = vectors;
980 vectors = vector;
983 protected override void CloneTo (CloneContext clonectx, Statement target)
985 // nothing to clone
988 public override bool Resolve (EmitContext ec)
990 // this flow-branching will be terminated when the surrounding block ends
991 ec.StartFlowBranching (this);
992 return true;
995 protected override void DoEmit (EmitContext ec)
997 if (ig != null && ig != ec.ig)
998 throw new InternalErrorException ("cannot happen");
999 LabelTarget (ec);
1000 ec.ig.MarkLabel (label);
1003 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1007 public void AddReference ()
1009 referenced = true;
1014 /// <summary>
1015 /// `goto default' statement
1016 /// </summary>
1017 public class GotoDefault : Statement {
1019 public GotoDefault (Location l)
1021 loc = l;
1024 protected override void CloneTo (CloneContext clonectx, Statement target)
1026 // nothing to clone
1029 public override bool Resolve (EmitContext ec)
1031 ec.CurrentBranching.CurrentUsageVector.Goto ();
1032 return true;
1035 protected override void DoEmit (EmitContext ec)
1037 if (ec.Switch == null){
1038 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1039 return;
1042 if (!ec.Switch.GotDefault){
1043 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1044 return;
1046 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1049 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1054 /// <summary>
1055 /// `goto case' statement
1056 /// </summary>
1057 public class GotoCase : Statement {
1058 Expression expr;
1059 SwitchLabel sl;
1061 public GotoCase (Expression e, Location l)
1063 expr = e;
1064 loc = l;
1067 public override bool Resolve (EmitContext ec)
1069 if (ec.Switch == null){
1070 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1071 return false;
1074 ec.CurrentBranching.CurrentUsageVector.Goto ();
1076 expr = expr.Resolve (ec);
1077 if (expr == null)
1078 return false;
1080 Constant c = expr as Constant;
1081 if (c == null) {
1082 Error (150, "A constant value is expected");
1083 return false;
1086 Type type = ec.Switch.SwitchType;
1087 Constant res = c.TryReduce (ec, type, c.Location);
1088 if (res == null) {
1089 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1090 return false;
1093 if (!Convert.ImplicitStandardConversionExists (c, type))
1094 Report.Warning (469, 2, loc,
1095 "The `goto case' value is not implicitly convertible to type `{0}'",
1096 TypeManager.CSharpName (type));
1098 object val = res.GetValue ();
1099 if (val == null)
1100 val = SwitchLabel.NullStringCase;
1102 sl = (SwitchLabel) ec.Switch.Elements [val];
1104 if (sl == null){
1105 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1106 (c.GetValue () == null ? "null" : val.ToString ()));
1107 return false;
1110 return true;
1113 protected override void DoEmit (EmitContext ec)
1115 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1118 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1120 expr.MutateHoistedGenericType (storey);
1123 protected override void CloneTo (CloneContext clonectx, Statement t)
1125 GotoCase target = (GotoCase) t;
1127 target.expr = expr.Clone (clonectx);
1131 public class Throw : Statement {
1132 Expression expr;
1134 public Throw (Expression expr, Location l)
1136 this.expr = expr;
1137 loc = l;
1140 public override bool Resolve (EmitContext ec)
1142 if (expr == null) {
1143 ec.CurrentBranching.CurrentUsageVector.Goto ();
1144 return ec.CurrentBranching.CheckRethrow (loc);
1147 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1148 ec.CurrentBranching.CurrentUsageVector.Goto ();
1150 if (expr == null)
1151 return false;
1153 Type t = expr.Type;
1155 if ((t != TypeManager.exception_type) &&
1156 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1157 t != TypeManager.null_type) {
1158 Error (155, "The type caught or thrown must be derived from System.Exception");
1159 return false;
1161 return true;
1164 protected override void DoEmit (EmitContext ec)
1166 if (expr == null)
1167 ec.ig.Emit (OpCodes.Rethrow);
1168 else {
1169 expr.Emit (ec);
1171 ec.ig.Emit (OpCodes.Throw);
1175 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1177 if (expr != null)
1178 expr.MutateHoistedGenericType (storey);
1181 protected override void CloneTo (CloneContext clonectx, Statement t)
1183 Throw target = (Throw) t;
1185 if (expr != null)
1186 target.expr = expr.Clone (clonectx);
1190 public class Break : Statement {
1192 public Break (Location l)
1194 loc = l;
1197 bool unwind_protect;
1199 public override bool Resolve (EmitContext ec)
1201 int errors = Report.Errors;
1202 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1203 ec.CurrentBranching.CurrentUsageVector.Goto ();
1204 return errors == Report.Errors;
1207 protected override void DoEmit (EmitContext ec)
1209 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1212 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1216 protected override void CloneTo (CloneContext clonectx, Statement t)
1218 // nothing needed
1222 public class Continue : Statement {
1224 public Continue (Location l)
1226 loc = l;
1229 bool unwind_protect;
1231 public override bool Resolve (EmitContext ec)
1233 int errors = Report.Errors;
1234 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1235 ec.CurrentBranching.CurrentUsageVector.Goto ();
1236 return errors == Report.Errors;
1239 protected override void DoEmit (EmitContext ec)
1241 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1244 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1248 protected override void CloneTo (CloneContext clonectx, Statement t)
1250 // nothing needed.
1254 public interface ILocalVariable
1256 void Emit (EmitContext ec);
1257 void EmitAssign (EmitContext ec);
1258 void EmitAddressOf (EmitContext ec);
1261 public interface IKnownVariable {
1262 Block Block { get; }
1263 Location Location { get; }
1267 // The information about a user-perceived local variable
1269 public class LocalInfo : IKnownVariable, ILocalVariable {
1270 public readonly FullNamedExpression Type;
1272 public Type VariableType;
1273 public readonly string Name;
1274 public readonly Location Location;
1275 public readonly Block Block;
1277 public VariableInfo VariableInfo;
1278 public HoistedVariable HoistedVariableReference;
1280 [Flags]
1281 enum Flags : byte {
1282 Used = 1,
1283 ReadOnly = 2,
1284 Pinned = 4,
1285 IsThis = 8,
1286 AddressTaken = 32,
1287 CompilerGenerated = 64,
1288 IsConstant = 128
1291 public enum ReadOnlyContext: byte {
1292 Using,
1293 Foreach,
1294 Fixed
1297 Flags flags;
1298 ReadOnlyContext ro_context;
1299 LocalBuilder builder;
1301 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1303 Type = type;
1304 Name = name;
1305 Block = block;
1306 Location = l;
1309 public LocalInfo (DeclSpace ds, Block block, Location l)
1311 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1312 Block = block;
1313 Location = l;
1316 public void ResolveVariable (EmitContext ec)
1318 if (HoistedVariableReference != null)
1319 return;
1321 if (builder == null) {
1322 if (Pinned)
1324 // This is needed to compile on both .NET 1.x and .NET 2.x
1325 // the later introduced `DeclareLocal (Type t, bool pinned)'
1327 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1328 else
1329 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1333 public void Emit (EmitContext ec)
1335 ec.ig.Emit (OpCodes.Ldloc, builder);
1338 public void EmitAssign (EmitContext ec)
1340 ec.ig.Emit (OpCodes.Stloc, builder);
1343 public void EmitAddressOf (EmitContext ec)
1345 ec.ig.Emit (OpCodes.Ldloca, builder);
1348 public void EmitSymbolInfo (EmitContext ec)
1350 if (builder != null)
1351 ec.DefineLocalVariable (Name, builder);
1354 public bool IsThisAssigned (EmitContext ec)
1356 if (VariableInfo == null)
1357 throw new Exception ();
1359 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1360 return true;
1362 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1365 public bool IsAssigned (EmitContext ec)
1367 if (VariableInfo == null)
1368 throw new Exception ();
1370 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1373 public bool Resolve (EmitContext ec)
1375 if (VariableType != null)
1376 return true;
1378 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1379 if (texpr == null)
1380 return false;
1382 VariableType = texpr.Type;
1384 if (TypeManager.IsGenericParameter (VariableType))
1385 return true;
1387 if (VariableType.IsAbstract && VariableType.IsSealed) {
1388 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1389 return false;
1392 if (VariableType.IsPointer && !ec.InUnsafe)
1393 Expression.UnsafeError (Location);
1395 return true;
1398 public bool IsConstant {
1399 get { return (flags & Flags.IsConstant) != 0; }
1400 set { flags |= Flags.IsConstant; }
1403 public bool AddressTaken {
1404 get { return (flags & Flags.AddressTaken) != 0; }
1405 set { flags |= Flags.AddressTaken; }
1408 public bool CompilerGenerated {
1409 get { return (flags & Flags.CompilerGenerated) != 0; }
1410 set { flags |= Flags.CompilerGenerated; }
1413 public override string ToString ()
1415 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1416 Name, Type, VariableInfo, Location);
1419 public bool Used {
1420 get { return (flags & Flags.Used) != 0; }
1421 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1424 public bool ReadOnly {
1425 get { return (flags & Flags.ReadOnly) != 0; }
1428 public void SetReadOnlyContext (ReadOnlyContext context)
1430 flags |= Flags.ReadOnly;
1431 ro_context = context;
1434 public string GetReadOnlyContext ()
1436 if (!ReadOnly)
1437 throw new InternalErrorException ("Variable is not readonly");
1439 switch (ro_context) {
1440 case ReadOnlyContext.Fixed:
1441 return "fixed variable";
1442 case ReadOnlyContext.Foreach:
1443 return "foreach iteration variable";
1444 case ReadOnlyContext.Using:
1445 return "using variable";
1447 throw new NotImplementedException ();
1451 // Whether the variable is pinned, if Pinned the variable has been
1452 // allocated in a pinned slot with DeclareLocal.
1454 public bool Pinned {
1455 get { return (flags & Flags.Pinned) != 0; }
1456 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1459 public bool IsThis {
1460 get { return (flags & Flags.IsThis) != 0; }
1461 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1464 Block IKnownVariable.Block {
1465 get { return Block; }
1468 Location IKnownVariable.Location {
1469 get { return Location; }
1472 public LocalInfo Clone (CloneContext clonectx)
1475 // Variables in anonymous block are not resolved yet
1477 if (VariableType == null)
1478 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1481 // Variables in method block are resolved
1483 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1484 li.VariableType = VariableType;
1485 return li;
1489 /// <summary>
1490 /// Block represents a C# block.
1491 /// </summary>
1493 /// <remarks>
1494 /// This class is used in a number of places: either to represent
1495 /// explicit blocks that the programmer places or implicit blocks.
1497 /// Implicit blocks are used as labels or to introduce variable
1498 /// declarations.
1500 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1501 /// they contain extra information that is not necessary on normal blocks.
1502 /// </remarks>
1503 public class Block : Statement {
1504 public Block Parent;
1505 public Location StartLocation;
1506 public Location EndLocation = Location.Null;
1508 public ExplicitBlock Explicit;
1509 public ToplevelBlock Toplevel; // TODO: Use Explicit
1511 [Flags]
1512 public enum Flags : byte {
1513 Unchecked = 1,
1514 BlockUsed = 2,
1515 VariablesInitialized = 4,
1516 HasRet = 8,
1517 Unsafe = 16,
1518 IsIterator = 32,
1519 HasCapturedVariable = 64,
1520 HasCapturedThis = 128
1522 protected Flags flags;
1524 public bool Unchecked {
1525 get { return (flags & Flags.Unchecked) != 0; }
1526 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1529 public bool Unsafe {
1530 get { return (flags & Flags.Unsafe) != 0; }
1531 set { flags |= Flags.Unsafe; }
1535 // The statements in this block
1537 protected ArrayList statements;
1540 // An array of Blocks. We keep track of children just
1541 // to generate the local variable declarations.
1543 // Statements and child statements are handled through the
1544 // statements.
1546 ArrayList children;
1549 // Labels. (label, block) pairs.
1551 protected HybridDictionary labels;
1554 // Keeps track of (name, type) pairs
1556 IDictionary variables;
1559 // Keeps track of constants
1560 HybridDictionary constants;
1563 // Temporary variables.
1565 ArrayList temporary_variables;
1568 // If this is a switch section, the enclosing switch block.
1570 Block switch_block;
1572 protected ArrayList scope_initializers;
1574 ArrayList anonymous_children;
1576 protected static int id;
1578 int this_id;
1580 int assignable_slots;
1581 bool unreachable_shown;
1582 bool unreachable;
1584 public Block (Block parent)
1585 : this (parent, (Flags) 0, Location.Null, Location.Null)
1588 public Block (Block parent, Flags flags)
1589 : this (parent, flags, Location.Null, Location.Null)
1592 public Block (Block parent, Location start, Location end)
1593 : this (parent, (Flags) 0, start, end)
1597 // Useful when TopLevel block is downgraded to normal block
1599 public Block (ToplevelBlock parent, ToplevelBlock source)
1600 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1602 statements = source.statements;
1603 children = source.children;
1604 labels = source.labels;
1605 variables = source.variables;
1606 constants = source.constants;
1607 switch_block = source.switch_block;
1610 public Block (Block parent, Flags flags, Location start, Location end)
1612 if (parent != null) {
1613 parent.AddChild (this);
1615 // the appropriate constructors will fixup these fields
1616 Toplevel = parent.Toplevel;
1617 Explicit = parent.Explicit;
1620 this.Parent = parent;
1621 this.flags = flags;
1622 this.StartLocation = start;
1623 this.EndLocation = end;
1624 this.loc = start;
1625 this_id = id++;
1626 statements = new ArrayList (4);
1629 public Block CreateSwitchBlock (Location start)
1631 // FIXME: should this be implicit?
1632 Block new_block = new ExplicitBlock (this, start, start);
1633 new_block.switch_block = this;
1634 return new_block;
1637 public int ID {
1638 get { return this_id; }
1641 public IDictionary Variables {
1642 get {
1643 if (variables == null)
1644 variables = new ListDictionary ();
1645 return variables;
1649 void AddChild (Block b)
1651 if (children == null)
1652 children = new ArrayList (1);
1654 children.Add (b);
1657 public void SetEndLocation (Location loc)
1659 EndLocation = loc;
1662 protected static void Error_158 (string name, Location loc)
1664 Report.Error (158, loc, "The label `{0}' shadows another label " +
1665 "by the same name in a contained scope", name);
1668 /// <summary>
1669 /// Adds a label to the current block.
1670 /// </summary>
1672 /// <returns>
1673 /// false if the name already exists in this block. true
1674 /// otherwise.
1675 /// </returns>
1677 public bool AddLabel (LabeledStatement target)
1679 if (switch_block != null)
1680 return switch_block.AddLabel (target);
1682 string name = target.Name;
1684 Block cur = this;
1685 while (cur != null) {
1686 LabeledStatement s = cur.DoLookupLabel (name);
1687 if (s != null) {
1688 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1689 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1690 return false;
1693 if (this == Explicit)
1694 break;
1696 cur = cur.Parent;
1699 while (cur != null) {
1700 if (cur.DoLookupLabel (name) != null) {
1701 Error_158 (name, target.loc);
1702 return false;
1705 if (children != null) {
1706 foreach (Block b in children) {
1707 LabeledStatement s = b.DoLookupLabel (name);
1708 if (s == null)
1709 continue;
1711 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1712 Error_158 (name, target.loc);
1713 return false;
1717 cur = cur.Parent;
1720 Toplevel.CheckError158 (name, target.loc);
1722 if (labels == null)
1723 labels = new HybridDictionary();
1725 labels.Add (name, target);
1726 return true;
1729 public LabeledStatement LookupLabel (string name)
1731 LabeledStatement s = DoLookupLabel (name);
1732 if (s != null)
1733 return s;
1735 if (children == null)
1736 return null;
1738 foreach (Block child in children) {
1739 if (Explicit != child.Explicit)
1740 continue;
1742 s = child.LookupLabel (name);
1743 if (s != null)
1744 return s;
1747 return null;
1750 LabeledStatement DoLookupLabel (string name)
1752 if (switch_block != null)
1753 return switch_block.LookupLabel (name);
1755 if (labels != null)
1756 if (labels.Contains (name))
1757 return ((LabeledStatement) labels [name]);
1759 return null;
1762 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1764 Block b = this;
1765 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1766 while (kvi == null) {
1767 b = b.Explicit.Parent;
1768 if (b == null)
1769 return true;
1770 kvi = b.Explicit.GetKnownVariable (name);
1773 if (kvi.Block == b)
1774 return true;
1776 // Is kvi.Block nested inside 'b'
1777 if (b.Explicit != kvi.Block.Explicit) {
1779 // If a variable by the same name it defined in a nested block of this
1780 // block, we violate the invariant meaning in a block.
1782 if (b == this) {
1783 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1784 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1785 return false;
1789 // It's ok if the definition is in a nested subblock of b, but not
1790 // nested inside this block -- a definition in a sibling block
1791 // should not affect us.
1793 return true;
1797 // Block 'b' and kvi.Block are the same textual block.
1798 // However, different variables are extant.
1800 // Check if the variable is in scope in both blocks. We use
1801 // an indirect check that depends on AddVariable doing its
1802 // part in maintaining the invariant-meaning-in-block property.
1804 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1805 return true;
1807 if (this is ToplevelBlock) {
1808 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1809 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1810 return false;
1814 // Even though we detected the error when the name is used, we
1815 // treat it as if the variable declaration was in error.
1817 Report.SymbolRelatedToPreviousError (loc, name);
1818 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1819 return false;
1822 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1824 LocalInfo vi = GetLocalInfo (name);
1825 if (vi != null) {
1826 Report.SymbolRelatedToPreviousError (vi.Location, name);
1827 if (Explicit == vi.Block.Explicit) {
1828 Error_AlreadyDeclared (l, name, null);
1829 } else {
1830 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1831 "parent or current" : "parent");
1833 return false;
1836 if (block != null) {
1837 Expression e = block.GetParameterReference (name, Location.Null);
1838 if (e != null) {
1839 ParameterReference pr = e as ParameterReference;
1840 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1841 Error_AlreadyDeclared (loc, name);
1842 else
1843 Error_AlreadyDeclared (loc, name, "parent or current");
1844 return false;
1848 return true;
1851 public LocalInfo AddVariable (Expression type, string name, Location l)
1853 if (!CheckParentConflictName (Toplevel, name, l))
1854 return null;
1856 if (Toplevel.GenericMethod != null) {
1857 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1858 if (tp.Name == name) {
1859 Report.SymbolRelatedToPreviousError (tp);
1860 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1861 return null;
1866 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1867 if (kvi != null) {
1868 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1869 Error_AlreadyDeclared (l, name, "child");
1870 return null;
1873 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1874 AddVariable (vi);
1876 if ((flags & Flags.VariablesInitialized) != 0)
1877 throw new InternalErrorException ("block has already been resolved");
1879 return vi;
1882 protected virtual void AddVariable (LocalInfo li)
1884 Variables.Add (li.Name, li);
1885 Explicit.AddKnownVariable (li.Name, li);
1888 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1890 if (reason == null) {
1891 Error_AlreadyDeclared (loc, var);
1892 return;
1895 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1896 "in this scope because it would give a different meaning " +
1897 "to `{0}', which is already used in a `{1}' scope " +
1898 "to denote something else", var, reason);
1901 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1903 Report.Error (128, loc,
1904 "A local variable named `{0}' is already defined in this scope", name);
1907 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1909 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1910 name, conflict);
1913 public bool AddConstant (Expression type, string name, Expression value, Location l)
1915 if (AddVariable (type, name, l) == null)
1916 return false;
1918 if (constants == null)
1919 constants = new HybridDictionary();
1921 constants.Add (name, value);
1923 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1924 Use ();
1925 return true;
1928 static int next_temp_id = 0;
1930 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1932 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1934 if (temporary_variables == null)
1935 temporary_variables = new ArrayList ();
1937 int id = ++next_temp_id;
1938 string name = "$s_" + id.ToString ();
1940 LocalInfo li = new LocalInfo (te, name, this, loc);
1941 li.CompilerGenerated = true;
1942 temporary_variables.Add (li);
1943 return li;
1946 public LocalInfo GetLocalInfo (string name)
1948 LocalInfo ret;
1949 for (Block b = this; b != null; b = b.Parent) {
1950 if (b.variables != null) {
1951 ret = (LocalInfo) b.variables [name];
1952 if (ret != null)
1953 return ret;
1957 return null;
1960 public Expression GetVariableType (string name)
1962 LocalInfo vi = GetLocalInfo (name);
1963 return vi == null ? null : vi.Type;
1966 public Expression GetConstantExpression (string name)
1968 for (Block b = this; b != null; b = b.Parent) {
1969 if (b.constants != null) {
1970 Expression ret = b.constants [name] as Expression;
1971 if (ret != null)
1972 return ret;
1975 return null;
1979 // It should be used by expressions which require to
1980 // register a statement during resolve process.
1982 public void AddScopeStatement (Statement s)
1984 if (scope_initializers == null)
1985 scope_initializers = new ArrayList ();
1987 scope_initializers.Add (s);
1990 public void AddStatement (Statement s)
1992 statements.Add (s);
1993 flags |= Flags.BlockUsed;
1996 public bool Used {
1997 get { return (flags & Flags.BlockUsed) != 0; }
2000 public void Use ()
2002 flags |= Flags.BlockUsed;
2005 public bool HasRet {
2006 get { return (flags & Flags.HasRet) != 0; }
2009 public int AssignableSlots {
2010 get {
2011 // TODO: Re-enable
2012 // if ((flags & Flags.VariablesInitialized) == 0)
2013 // throw new Exception ("Variables have not been initialized yet");
2014 return assignable_slots;
2018 public ArrayList AnonymousChildren {
2019 get { return anonymous_children; }
2022 public void AddAnonymousChild (ToplevelBlock b)
2024 if (anonymous_children == null)
2025 anonymous_children = new ArrayList ();
2027 anonymous_children.Add (b);
2030 void DoResolveConstants (EmitContext ec)
2032 if (constants == null)
2033 return;
2035 if (variables == null)
2036 throw new InternalErrorException ("cannot happen");
2038 foreach (DictionaryEntry de in variables) {
2039 string name = (string) de.Key;
2040 LocalInfo vi = (LocalInfo) de.Value;
2041 Type variable_type = vi.VariableType;
2043 if (variable_type == null) {
2044 if (vi.Type is VarExpr)
2045 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2047 continue;
2050 Expression cv = (Expression) constants [name];
2051 if (cv == null)
2052 continue;
2054 // Don't let 'const int Foo = Foo;' succeed.
2055 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2056 // which in turn causes the 'must be constant' error to be triggered.
2057 constants.Remove (name);
2059 if (!Const.IsConstantTypeValid (variable_type)) {
2060 Const.Error_InvalidConstantType (variable_type, loc);
2061 continue;
2064 ec.CurrentBlock = this;
2065 Expression e;
2066 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2067 e = cv.Resolve (ec);
2069 if (e == null)
2070 continue;
2072 Constant ce = e as Constant;
2073 if (ce == null) {
2074 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2075 continue;
2078 e = ce.ConvertImplicitly (variable_type);
2079 if (e == null) {
2080 if (TypeManager.IsReferenceType (variable_type))
2081 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2082 else
2083 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2084 continue;
2087 constants.Add (name, e);
2088 vi.IsConstant = true;
2092 protected void ResolveMeta (EmitContext ec, int offset)
2094 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2096 // If some parent block was unsafe, we remain unsafe even if this block
2097 // isn't explicitly marked as such.
2098 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2099 flags |= Flags.VariablesInitialized;
2101 if (variables != null) {
2102 foreach (LocalInfo li in variables.Values) {
2103 if (!li.Resolve (ec))
2104 continue;
2105 li.VariableInfo = new VariableInfo (li, offset);
2106 offset += li.VariableInfo.Length;
2109 assignable_slots = offset;
2111 DoResolveConstants (ec);
2113 if (children == null)
2114 return;
2115 foreach (Block b in children)
2116 b.ResolveMeta (ec, offset);
2121 // Emits the local variable declarations for a block
2123 public virtual void EmitMeta (EmitContext ec)
2125 if (variables != null){
2126 foreach (LocalInfo vi in variables.Values)
2127 vi.ResolveVariable (ec);
2130 if (temporary_variables != null) {
2131 for (int i = 0; i < temporary_variables.Count; i++)
2132 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2135 if (children != null) {
2136 for (int i = 0; i < children.Count; i++)
2137 ((Block)children[i]).EmitMeta(ec);
2141 void UsageWarning ()
2143 if (variables == null || Report.WarningLevel < 3)
2144 return;
2146 foreach (DictionaryEntry de in variables) {
2147 LocalInfo vi = (LocalInfo) de.Value;
2149 if (!vi.Used) {
2150 string name = (string) de.Key;
2152 // vi.VariableInfo can be null for 'catch' variables
2153 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2154 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2155 else
2156 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2161 static void CheckPossibleMistakenEmptyStatement (Statement s)
2163 Statement body;
2165 // Some statements are wrapped by a Block. Since
2166 // others' internal could be changed, here I treat
2167 // them as possibly wrapped by Block equally.
2168 Block b = s as Block;
2169 if (b != null && b.statements.Count == 1)
2170 s = (Statement) b.statements [0];
2172 if (s is Lock)
2173 body = ((Lock) s).Statement;
2174 else if (s is For)
2175 body = ((For) s).Statement;
2176 else if (s is Foreach)
2177 body = ((Foreach) s).Statement;
2178 else if (s is While)
2179 body = ((While) s).Statement;
2180 else if (s is Fixed)
2181 body = ((Fixed) s).Statement;
2182 else if (s is Using)
2183 body = ((Using) s).EmbeddedStatement;
2184 else if (s is UsingTemporary)
2185 body = ((UsingTemporary) s).Statement;
2186 else
2187 return;
2189 if (body == null || body is EmptyStatement)
2190 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2193 public override bool Resolve (EmitContext ec)
2195 Block prev_block = ec.CurrentBlock;
2196 bool ok = true;
2198 int errors = Report.Errors;
2200 ec.CurrentBlock = this;
2201 ec.StartFlowBranching (this);
2203 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2206 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2207 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2208 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2209 // responsible for handling the situation.
2211 int statement_count = statements.Count;
2212 for (int ix = 0; ix < statement_count; ix++){
2213 Statement s = (Statement) statements [ix];
2214 // Check possible empty statement (CS0642)
2215 if (Report.WarningLevel >= 3 &&
2216 ix + 1 < statement_count &&
2217 statements [ix + 1] is ExplicitBlock)
2218 CheckPossibleMistakenEmptyStatement (s);
2221 // Warn if we detect unreachable code.
2223 if (unreachable) {
2224 if (s is EmptyStatement)
2225 continue;
2227 if (!unreachable_shown && !(s is LabeledStatement)) {
2228 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2229 unreachable_shown = true;
2232 Block c_block = s as Block;
2233 if (c_block != null)
2234 c_block.unreachable = c_block.unreachable_shown = true;
2238 // Note that we're not using ResolveUnreachable() for unreachable
2239 // statements here. ResolveUnreachable() creates a temporary
2240 // flow branching and kills it afterwards. This leads to problems
2241 // if you have two unreachable statements where the first one
2242 // assigns a variable and the second one tries to access it.
2245 if (!s.Resolve (ec)) {
2246 ok = false;
2247 if (ec.IsInProbingMode)
2248 break;
2250 statements [ix] = EmptyStatement.Value;
2251 continue;
2254 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2255 statements [ix] = EmptyStatement.Value;
2257 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2258 if (unreachable && s is LabeledStatement)
2259 throw new InternalErrorException ("should not happen");
2262 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2263 ec.CurrentBranching, statement_count);
2265 while (ec.CurrentBranching is FlowBranchingLabeled)
2266 ec.EndFlowBranching ();
2268 bool flow_unreachable = ec.EndFlowBranching ();
2270 ec.CurrentBlock = prev_block;
2272 if (flow_unreachable)
2273 flags |= Flags.HasRet;
2275 // If we're a non-static `struct' constructor which doesn't have an
2276 // initializer, then we must initialize all of the struct's fields.
2277 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2278 ok = false;
2280 if ((labels != null) && (Report.WarningLevel >= 2)) {
2281 foreach (LabeledStatement label in labels.Values)
2282 if (!label.HasBeenReferenced)
2283 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2286 if (ok && errors == Report.Errors)
2287 UsageWarning ();
2289 return ok;
2292 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2294 unreachable_shown = true;
2295 unreachable = true;
2297 if (warn)
2298 Report.Warning (162, 2, loc, "Unreachable code detected");
2300 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2301 bool ok = Resolve (ec);
2302 ec.KillFlowBranching ();
2304 return ok;
2307 protected override void DoEmit (EmitContext ec)
2309 for (int ix = 0; ix < statements.Count; ix++){
2310 Statement s = (Statement) statements [ix];
2311 s.Emit (ec);
2315 public override void Emit (EmitContext ec)
2317 Block prev_block = ec.CurrentBlock;
2318 ec.CurrentBlock = this;
2320 if (scope_initializers != null)
2321 EmitScopeInitializers (ec);
2323 ec.Mark (StartLocation);
2324 DoEmit (ec);
2326 if (SymbolWriter.HasSymbolWriter)
2327 EmitSymbolInfo (ec);
2329 ec.CurrentBlock = prev_block;
2332 protected void EmitScopeInitializers (EmitContext ec)
2334 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2336 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2337 foreach (Statement s in scope_initializers)
2338 s.Emit (ec);
2341 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2344 protected virtual void EmitSymbolInfo (EmitContext ec)
2346 if (variables != null) {
2347 foreach (LocalInfo vi in variables.Values) {
2348 vi.EmitSymbolInfo (ec);
2353 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2355 MutateVariables (storey);
2357 if (scope_initializers != null) {
2358 foreach (Statement s in scope_initializers)
2359 s.MutateHoistedGenericType (storey);
2362 foreach (Statement s in statements)
2363 s.MutateHoistedGenericType (storey);
2366 void MutateVariables (AnonymousMethodStorey storey)
2368 if (variables != null) {
2369 foreach (LocalInfo vi in variables.Values) {
2370 vi.VariableType = storey.MutateType (vi.VariableType);
2374 if (temporary_variables != null) {
2375 foreach (LocalInfo vi in temporary_variables)
2376 vi.VariableType = storey.MutateType (vi.VariableType);
2380 public override string ToString ()
2382 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2385 protected override void CloneTo (CloneContext clonectx, Statement t)
2387 Block target = (Block) t;
2389 clonectx.AddBlockMap (this, target);
2391 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2392 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2393 if (Parent != null)
2394 target.Parent = clonectx.RemapBlockCopy (Parent);
2396 if (variables != null){
2397 target.variables = new Hashtable ();
2399 foreach (DictionaryEntry de in variables){
2400 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2401 target.variables [de.Key] = newlocal;
2402 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2406 target.statements = new ArrayList (statements.Count);
2407 foreach (Statement s in statements)
2408 target.statements.Add (s.Clone (clonectx));
2410 if (target.children != null){
2411 target.children = new ArrayList (children.Count);
2412 foreach (Block b in children){
2413 target.children.Add (clonectx.LookupBlock (b));
2418 // TODO: labels, switch_block, constants (?), anonymous_children
2423 public class ExplicitBlock : Block {
2424 HybridDictionary known_variables;
2425 protected AnonymousMethodStorey am_storey;
2427 public ExplicitBlock (Block parent, Location start, Location end)
2428 : this (parent, (Flags) 0, start, end)
2432 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2433 : base (parent, flags, start, end)
2435 this.Explicit = this;
2438 // <summary>
2439 // Marks a variable with name @name as being used in this or a child block.
2440 // If a variable name has been used in a child block, it's illegal to
2441 // declare a variable with the same name in the current block.
2442 // </summary>
2443 internal void AddKnownVariable (string name, IKnownVariable info)
2445 if (known_variables == null)
2446 known_variables = new HybridDictionary();
2448 known_variables [name] = info;
2450 if (Parent != null)
2451 Parent.Explicit.AddKnownVariable (name, info);
2454 public AnonymousMethodStorey AnonymousMethodStorey {
2455 get { return am_storey; }
2459 // Creates anonymous method storey in current block
2461 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2464 // When referencing a variable in iterator storey from children anonymous method
2466 if (Toplevel.am_storey is IteratorStorey) {
2467 return Toplevel.am_storey;
2471 // An iterator has only 1 storey block
2473 if (ec.CurrentIterator != null)
2474 return ec.CurrentIterator.Storey;
2476 if (am_storey == null) {
2477 MemberBase mc = ec.ResolveContext as MemberBase;
2478 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2481 // Creates anonymous method storey for this block
2483 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2486 return am_storey;
2489 public override void Emit (EmitContext ec)
2491 if (am_storey != null)
2492 am_storey.EmitStoreyInstantiation (ec);
2494 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2495 if (emit_debug_info)
2496 ec.BeginScope ();
2498 base.Emit (ec);
2500 if (emit_debug_info)
2501 ec.EndScope ();
2504 public override void EmitMeta (EmitContext ec)
2507 // Creates anonymous method storey
2509 if (am_storey != null) {
2510 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2512 // Creates parent storey reference when hoisted this is accessible
2514 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2515 ExplicitBlock parent = Toplevel.Parent.Explicit;
2518 // Hoisted this exists in top-level parent storey only
2520 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2521 parent = parent.Parent.Explicit;
2523 am_storey.AddParentStoreyReference (parent.am_storey);
2526 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2529 am_storey.DefineType ();
2530 am_storey.ResolveType ();
2531 am_storey.Define ();
2532 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2534 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2535 if (ref_blocks != null) {
2536 foreach (ExplicitBlock ref_block in ref_blocks) {
2537 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2538 if (b.am_storey != null) {
2539 b.am_storey.AddParentStoreyReference (am_storey);
2541 // Stop propagation inside same top block
2542 if (b.Toplevel == Toplevel)
2543 break;
2545 b = b.Toplevel;
2547 b.HasCapturedVariable = true;
2553 base.EmitMeta (ec);
2556 internal IKnownVariable GetKnownVariable (string name)
2558 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2561 public bool HasCapturedThis
2563 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2564 get { return (flags & Flags.HasCapturedThis) != 0; }
2567 public bool HasCapturedVariable
2569 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2570 get { return (flags & Flags.HasCapturedVariable) != 0; }
2573 protected override void CloneTo (CloneContext clonectx, Statement t)
2575 ExplicitBlock target = (ExplicitBlock) t;
2576 target.known_variables = null;
2577 base.CloneTo (clonectx, t);
2581 public class ToplevelParameterInfo : IKnownVariable {
2582 public readonly ToplevelBlock Block;
2583 public readonly int Index;
2584 public VariableInfo VariableInfo;
2586 Block IKnownVariable.Block {
2587 get { return Block; }
2589 public Parameter Parameter {
2590 get { return Block.Parameters [Index]; }
2593 public Type ParameterType {
2594 get { return Block.Parameters.Types [Index]; }
2597 public Location Location {
2598 get { return Parameter.Location; }
2601 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2603 this.Block = block;
2604 this.Index = idx;
2609 // A toplevel block contains extra information, the split is done
2610 // only to separate information that would otherwise bloat the more
2611 // lightweight Block.
2613 // In particular, this was introduced when the support for Anonymous
2614 // Methods was implemented.
2616 public class ToplevelBlock : ExplicitBlock
2619 // Block is converted to an expression
2621 sealed class BlockScopeExpression : Expression
2623 Expression child;
2624 readonly ToplevelBlock block;
2626 public BlockScopeExpression (Expression child, ToplevelBlock block)
2628 this.child = child;
2629 this.block = block;
2632 public override Expression CreateExpressionTree (EmitContext ec)
2634 throw new NotSupportedException ();
2637 public override Expression DoResolve (EmitContext ec)
2639 if (child == null)
2640 return null;
2642 block.ResolveMeta (ec, ParametersCompiled.EmptyReadOnlyParameters);
2643 child = child.Resolve (ec);
2644 if (child == null)
2645 return null;
2647 eclass = child.eclass;
2648 type = child.Type;
2649 return this;
2652 public override void Emit (EmitContext ec)
2654 block.EmitMeta (ec);
2655 block.EmitScopeInitializers (ec);
2656 child.Emit (ec);
2659 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2661 type = storey.MutateType (type);
2662 child.MutateHoistedGenericType (storey);
2663 block.MutateHoistedGenericType (storey);
2667 GenericMethod generic;
2668 FlowBranchingToplevel top_level_branching;
2669 protected ParametersCompiled parameters;
2670 ToplevelParameterInfo[] parameter_info;
2671 LocalInfo this_variable;
2673 public HoistedVariable HoistedThisVariable;
2676 // The parameters for the block.
2678 public ParametersCompiled Parameters {
2679 get { return parameters; }
2682 public GenericMethod GenericMethod {
2683 get { return generic; }
2686 public ToplevelBlock Container {
2687 get { return Parent == null ? null : Parent.Toplevel; }
2690 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2691 this (parent, (Flags) 0, parameters, start)
2695 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2696 this (parent, parameters, start)
2698 this.generic = generic;
2701 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2702 this (null, (Flags) 0, parameters, start)
2706 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2707 this (null, flags, parameters, start)
2711 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2712 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2713 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2714 base (null, flags, start, Location.Null)
2716 this.Toplevel = this;
2718 this.parameters = parameters;
2719 this.Parent = parent;
2720 if (parent != null)
2721 parent.AddAnonymousChild (this);
2723 if (!this.parameters.IsEmpty)
2724 ProcessParameters ();
2727 public ToplevelBlock (Location loc)
2728 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2732 protected override void CloneTo (CloneContext clonectx, Statement t)
2734 ToplevelBlock target = (ToplevelBlock) t;
2735 base.CloneTo (clonectx, t);
2737 if (parameters.Count != 0)
2738 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2739 for (int i = 0; i < parameters.Count; ++i)
2740 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2743 public bool CheckError158 (string name, Location loc)
2745 if (AnonymousChildren != null) {
2746 foreach (ToplevelBlock child in AnonymousChildren) {
2747 if (!child.CheckError158 (name, loc))
2748 return false;
2752 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2753 if (!c.DoCheckError158 (name, loc))
2754 return false;
2757 return true;
2760 void ProcessParameters ()
2762 int n = parameters.Count;
2763 parameter_info = new ToplevelParameterInfo [n];
2764 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2765 for (int i = 0; i < n; ++i) {
2766 parameter_info [i] = new ToplevelParameterInfo (this, i);
2768 Parameter p = parameters [i];
2769 if (p == null)
2770 continue;
2772 string name = p.Name;
2773 if (CheckParentConflictName (top_parent, name, loc))
2774 AddKnownVariable (name, parameter_info [i]);
2777 // mark this block as "used" so that we create local declarations in a sub-block
2778 // FIXME: This appears to uncover a lot of bugs
2779 //this.Use ();
2782 bool DoCheckError158 (string name, Location loc)
2784 LabeledStatement s = LookupLabel (name);
2785 if (s != null) {
2786 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2787 Error_158 (name, loc);
2788 return false;
2791 return true;
2794 public override Expression CreateExpressionTree (EmitContext ec)
2796 if (statements.Count == 1) {
2797 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2798 if (scope_initializers != null)
2799 expr = new BlockScopeExpression (expr, this);
2801 return expr;
2804 return base.CreateExpressionTree (ec);
2808 // Reformats this block to be top-level iterator block
2810 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2812 IsIterator = true;
2814 // Creates block with original statements
2815 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2817 source.statements = new ArrayList (1);
2818 source.AddStatement (new Return (iterator, iterator.Location));
2819 source.IsIterator = false;
2821 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2822 source.am_storey = iterator_storey;
2823 return iterator_storey;
2826 public FlowBranchingToplevel TopLevelBranching {
2827 get { return top_level_branching; }
2831 // Returns a parameter reference expression for the given name,
2832 // or null if there is no such parameter
2834 public Expression GetParameterReference (string name, Location loc)
2836 for (ToplevelBlock t = this; t != null; t = t.Container) {
2837 Expression expr = t.GetParameterReferenceExpression (name, loc);
2838 if (expr != null)
2839 return expr;
2842 return null;
2845 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2847 int idx = parameters.GetParameterIndexByName (name);
2848 return idx < 0 ?
2849 null : new ParameterReference (parameter_info [idx], loc);
2852 // <summary>
2853 // Returns the "this" instance variable of this block.
2854 // See AddThisVariable() for more information.
2855 // </summary>
2856 public LocalInfo ThisVariable {
2857 get { return this_variable; }
2860 // <summary>
2861 // This is used by non-static `struct' constructors which do not have an
2862 // initializer - in this case, the constructor must initialize all of the
2863 // struct's fields. To do this, we add a "this" variable and use the flow
2864 // analysis code to ensure that it's been fully initialized before control
2865 // leaves the constructor.
2866 // </summary>
2867 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2869 if (this_variable == null) {
2870 this_variable = new LocalInfo (ds, this, l);
2871 this_variable.Used = true;
2872 this_variable.IsThis = true;
2874 Variables.Add ("this", this_variable);
2877 return this_variable;
2880 public bool IsIterator {
2881 get { return (flags & Flags.IsIterator) != 0; }
2882 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2885 public bool IsThisAssigned (EmitContext ec)
2887 return this_variable == null || this_variable.IsThisAssigned (ec);
2890 public bool ResolveMeta (EmitContext ec, ParametersCompiled ip)
2892 int errors = Report.Errors;
2893 int orig_count = parameters.Count;
2895 if (top_level_branching != null)
2896 return true;
2898 if (ip != null)
2899 parameters = ip;
2901 // Assert: orig_count != parameter.Count => orig_count == 0
2902 if (orig_count != 0 && orig_count != parameters.Count)
2903 throw new InternalErrorException ("parameter information mismatch");
2905 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2907 for (int i = 0; i < orig_count; ++i) {
2908 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2910 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2911 continue;
2913 VariableInfo vi = new VariableInfo (ip, i, offset);
2914 parameter_info [i].VariableInfo = vi;
2915 offset += vi.Length;
2918 ResolveMeta (ec, offset);
2920 top_level_branching = ec.StartFlowBranching (this);
2922 return Report.Errors == errors;
2925 // <summary>
2926 // Check whether all `out' parameters have been assigned.
2927 // </summary>
2928 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2930 if (vector.IsUnreachable)
2931 return;
2933 int n = parameter_info == null ? 0 : parameter_info.Length;
2935 for (int i = 0; i < n; i++) {
2936 VariableInfo var = parameter_info [i].VariableInfo;
2938 if (var == null)
2939 continue;
2941 if (vector.IsAssigned (var, false))
2942 continue;
2944 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2945 var.Name);
2949 public override void EmitMeta (EmitContext ec)
2951 parameters.ResolveVariable ();
2953 // Avoid declaring an IL variable for this_variable since it is not accessed
2954 // from the generated IL
2955 if (this_variable != null)
2956 Variables.Remove ("this");
2957 base.EmitMeta (ec);
2960 protected override void EmitSymbolInfo (EmitContext ec)
2962 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2963 if ((ae != null) && (ae.Storey != null))
2964 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2966 base.EmitSymbolInfo (ec);
2969 public override void Emit (EmitContext ec)
2971 base.Emit (ec);
2972 ec.Mark (EndLocation);
2976 public class SwitchLabel {
2977 Expression label;
2978 object converted;
2979 Location loc;
2981 Label il_label;
2982 bool il_label_set;
2983 Label il_label_code;
2984 bool il_label_code_set;
2986 public static readonly object NullStringCase = new object ();
2989 // if expr == null, then it is the default case.
2991 public SwitchLabel (Expression expr, Location l)
2993 label = expr;
2994 loc = l;
2997 public Expression Label {
2998 get {
2999 return label;
3003 public Location Location {
3004 get { return loc; }
3007 public object Converted {
3008 get {
3009 return converted;
3013 public Label GetILLabel (EmitContext ec)
3015 if (!il_label_set){
3016 il_label = ec.ig.DefineLabel ();
3017 il_label_set = true;
3019 return il_label;
3022 public Label GetILLabelCode (EmitContext ec)
3024 if (!il_label_code_set){
3025 il_label_code = ec.ig.DefineLabel ();
3026 il_label_code_set = true;
3028 return il_label_code;
3032 // Resolves the expression, reduces it to a literal if possible
3033 // and then converts it to the requested type.
3035 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
3037 Expression e = label.Resolve (ec);
3039 if (e == null)
3040 return false;
3042 Constant c = e as Constant;
3043 if (c == null){
3044 Report.Error (150, loc, "A constant value is expected");
3045 return false;
3048 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3049 converted = NullStringCase;
3050 return true;
3053 if (allow_nullable && c.GetValue () == null) {
3054 converted = NullStringCase;
3055 return true;
3058 c = c.ImplicitConversionRequired (ec, required_type, loc);
3059 if (c == null)
3060 return false;
3062 converted = c.GetValue ();
3063 return true;
3066 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3068 string label;
3069 if (converted == null)
3070 label = "default";
3071 else if (converted == NullStringCase)
3072 label = "null";
3073 else
3074 label = converted.ToString ();
3076 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3077 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3080 public SwitchLabel Clone (CloneContext clonectx)
3082 return new SwitchLabel (label.Clone (clonectx), loc);
3086 public class SwitchSection {
3087 // An array of SwitchLabels.
3088 public readonly ArrayList Labels;
3089 public readonly Block Block;
3091 public SwitchSection (ArrayList labels, Block block)
3093 Labels = labels;
3094 Block = block;
3097 public SwitchSection Clone (CloneContext clonectx)
3099 ArrayList cloned_labels = new ArrayList ();
3101 foreach (SwitchLabel sl in cloned_labels)
3102 cloned_labels.Add (sl.Clone (clonectx));
3104 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3108 public class Switch : Statement {
3109 public ArrayList Sections;
3110 public Expression Expr;
3112 /// <summary>
3113 /// Maps constants whose type type SwitchType to their SwitchLabels.
3114 /// </summary>
3115 public IDictionary Elements;
3117 /// <summary>
3118 /// The governing switch type
3119 /// </summary>
3120 public Type SwitchType;
3123 // Computed
3125 Label default_target;
3126 Label null_target;
3127 Expression new_expr;
3128 bool is_constant;
3129 bool has_null_case;
3130 SwitchSection constant_section;
3131 SwitchSection default_section;
3133 ExpressionStatement string_dictionary;
3134 FieldExpr switch_cache_field;
3135 static int unique_counter;
3138 // Nullable Types support
3140 Nullable.Unwrap unwrap;
3142 protected bool HaveUnwrap {
3143 get { return unwrap != null; }
3147 // The types allowed to be implicitly cast from
3148 // on the governing type
3150 static Type [] allowed_types;
3152 public Switch (Expression e, ArrayList sects, Location l)
3154 Expr = e;
3155 Sections = sects;
3156 loc = l;
3159 public bool GotDefault {
3160 get {
3161 return default_section != null;
3165 public Label DefaultTarget {
3166 get {
3167 return default_target;
3172 // Determines the governing type for a switch. The returned
3173 // expression might be the expression from the switch, or an
3174 // expression that includes any potential conversions to the
3175 // integral types or to string.
3177 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3179 Type t = expr.Type;
3181 if (t == TypeManager.byte_type ||
3182 t == TypeManager.sbyte_type ||
3183 t == TypeManager.ushort_type ||
3184 t == TypeManager.short_type ||
3185 t == TypeManager.uint32_type ||
3186 t == TypeManager.int32_type ||
3187 t == TypeManager.uint64_type ||
3188 t == TypeManager.int64_type ||
3189 t == TypeManager.char_type ||
3190 t == TypeManager.string_type ||
3191 t == TypeManager.bool_type ||
3192 TypeManager.IsEnumType (t))
3193 return expr;
3195 if (allowed_types == null){
3196 allowed_types = new Type [] {
3197 TypeManager.sbyte_type,
3198 TypeManager.byte_type,
3199 TypeManager.short_type,
3200 TypeManager.ushort_type,
3201 TypeManager.int32_type,
3202 TypeManager.uint32_type,
3203 TypeManager.int64_type,
3204 TypeManager.uint64_type,
3205 TypeManager.char_type,
3206 TypeManager.string_type
3211 // Try to find a *user* defined implicit conversion.
3213 // If there is no implicit conversion, or if there are multiple
3214 // conversions, we have to report an error
3216 Expression converted = null;
3217 foreach (Type tt in allowed_types){
3218 Expression e;
3220 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3221 if (e == null)
3222 continue;
3225 // Ignore over-worked ImplicitUserConversions that do
3226 // an implicit conversion in addition to the user conversion.
3228 if (!(e is UserCast))
3229 continue;
3231 if (converted != null){
3232 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3233 return null;
3236 converted = e;
3238 return converted;
3242 // Performs the basic sanity checks on the switch statement
3243 // (looks for duplicate keys and non-constant expressions).
3245 // It also returns a hashtable with the keys that we will later
3246 // use to compute the switch tables
3248 bool CheckSwitch (EmitContext ec)
3250 bool error = false;
3251 Elements = Sections.Count > 10 ?
3252 (IDictionary)new Hashtable () :
3253 (IDictionary)new ListDictionary ();
3255 foreach (SwitchSection ss in Sections){
3256 foreach (SwitchLabel sl in ss.Labels){
3257 if (sl.Label == null){
3258 if (default_section != null){
3259 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3260 error = true;
3262 default_section = ss;
3263 continue;
3266 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3267 error = true;
3268 continue;
3271 object key = sl.Converted;
3272 if (key == SwitchLabel.NullStringCase)
3273 has_null_case = true;
3275 try {
3276 Elements.Add (key, sl);
3277 } catch (ArgumentException) {
3278 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3279 error = true;
3283 return !error;
3286 void EmitObjectInteger (ILGenerator ig, object k)
3288 if (k is int)
3289 IntConstant.EmitInt (ig, (int) k);
3290 else if (k is Constant) {
3291 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3293 else if (k is uint)
3294 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3295 else if (k is long)
3297 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3299 IntConstant.EmitInt (ig, (int) (long) k);
3300 ig.Emit (OpCodes.Conv_I8);
3302 else
3303 LongConstant.EmitLong (ig, (long) k);
3305 else if (k is ulong)
3307 ulong ul = (ulong) k;
3308 if (ul < (1L<<32))
3310 IntConstant.EmitInt (ig, unchecked ((int) ul));
3311 ig.Emit (OpCodes.Conv_U8);
3313 else
3315 LongConstant.EmitLong (ig, unchecked ((long) ul));
3318 else if (k is char)
3319 IntConstant.EmitInt (ig, (int) ((char) k));
3320 else if (k is sbyte)
3321 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3322 else if (k is byte)
3323 IntConstant.EmitInt (ig, (int) ((byte) k));
3324 else if (k is short)
3325 IntConstant.EmitInt (ig, (int) ((short) k));
3326 else if (k is ushort)
3327 IntConstant.EmitInt (ig, (int) ((ushort) k));
3328 else if (k is bool)
3329 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3330 else
3331 throw new Exception ("Unhandled case");
3334 // structure used to hold blocks of keys while calculating table switch
3335 class KeyBlock : IComparable
3337 public KeyBlock (long _first)
3339 first = last = _first;
3341 public long first;
3342 public long last;
3343 public ArrayList element_keys = null;
3344 // how many items are in the bucket
3345 public int Size = 1;
3346 public int Length
3348 get { return (int) (last - first + 1); }
3350 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3352 return kb_last.last - kb_first.first + 1;
3354 public int CompareTo (object obj)
3356 KeyBlock kb = (KeyBlock) obj;
3357 int nLength = Length;
3358 int nLengthOther = kb.Length;
3359 if (nLengthOther == nLength)
3360 return (int) (kb.first - first);
3361 return nLength - nLengthOther;
3365 /// <summary>
3366 /// This method emits code for a lookup-based switch statement (non-string)
3367 /// Basically it groups the cases into blocks that are at least half full,
3368 /// and then spits out individual lookup opcodes for each block.
3369 /// It emits the longest blocks first, and short blocks are just
3370 /// handled with direct compares.
3371 /// </summary>
3372 /// <param name="ec"></param>
3373 /// <param name="val"></param>
3374 /// <returns></returns>
3375 void TableSwitchEmit (EmitContext ec, Expression val)
3377 int element_count = Elements.Count;
3378 object [] element_keys = new object [element_count];
3379 Elements.Keys.CopyTo (element_keys, 0);
3380 Array.Sort (element_keys);
3382 // initialize the block list with one element per key
3383 ArrayList key_blocks = new ArrayList (element_count);
3384 foreach (object key in element_keys)
3385 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3387 KeyBlock current_kb;
3388 // iteratively merge the blocks while they are at least half full
3389 // there's probably a really cool way to do this with a tree...
3390 while (key_blocks.Count > 1)
3392 ArrayList key_blocks_new = new ArrayList ();
3393 current_kb = (KeyBlock) key_blocks [0];
3394 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3396 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3397 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3399 // merge blocks
3400 current_kb.last = kb.last;
3401 current_kb.Size += kb.Size;
3403 else
3405 // start a new block
3406 key_blocks_new.Add (current_kb);
3407 current_kb = kb;
3410 key_blocks_new.Add (current_kb);
3411 if (key_blocks.Count == key_blocks_new.Count)
3412 break;
3413 key_blocks = key_blocks_new;
3416 // initialize the key lists
3417 foreach (KeyBlock kb in key_blocks)
3418 kb.element_keys = new ArrayList ();
3420 // fill the key lists
3421 int iBlockCurr = 0;
3422 if (key_blocks.Count > 0) {
3423 current_kb = (KeyBlock) key_blocks [0];
3424 foreach (object key in element_keys)
3426 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3427 System.Convert.ToInt64 (key) > current_kb.last;
3428 if (next_block)
3429 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3430 current_kb.element_keys.Add (key);
3434 // sort the blocks so we can tackle the largest ones first
3435 key_blocks.Sort ();
3437 // okay now we can start...
3438 ILGenerator ig = ec.ig;
3439 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3440 Label lbl_default = default_target;
3442 Type type_keys = null;
3443 if (element_keys.Length > 0)
3444 type_keys = element_keys [0].GetType (); // used for conversions
3446 Type compare_type;
3448 if (TypeManager.IsEnumType (SwitchType))
3449 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3450 else
3451 compare_type = SwitchType;
3453 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3455 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3456 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3457 if (kb.Length <= 2)
3459 foreach (object key in kb.element_keys) {
3460 SwitchLabel sl = (SwitchLabel) Elements [key];
3461 if (key is int && (int) key == 0) {
3462 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3463 } else {
3464 val.Emit (ec);
3465 EmitObjectInteger (ig, key);
3466 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3470 else
3472 // TODO: if all the keys in the block are the same and there are
3473 // no gaps/defaults then just use a range-check.
3474 if (compare_type == TypeManager.int64_type ||
3475 compare_type == TypeManager.uint64_type)
3477 // TODO: optimize constant/I4 cases
3479 // check block range (could be > 2^31)
3480 val.Emit (ec);
3481 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3482 ig.Emit (OpCodes.Blt, lbl_default);
3483 val.Emit (ec);
3484 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3485 ig.Emit (OpCodes.Bgt, lbl_default);
3487 // normalize range
3488 val.Emit (ec);
3489 if (kb.first != 0)
3491 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3492 ig.Emit (OpCodes.Sub);
3494 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3496 else
3498 // normalize range
3499 val.Emit (ec);
3500 int first = (int) kb.first;
3501 if (first > 0)
3503 IntConstant.EmitInt (ig, first);
3504 ig.Emit (OpCodes.Sub);
3506 else if (first < 0)
3508 IntConstant.EmitInt (ig, -first);
3509 ig.Emit (OpCodes.Add);
3513 // first, build the list of labels for the switch
3514 int iKey = 0;
3515 int cJumps = kb.Length;
3516 Label [] switch_labels = new Label [cJumps];
3517 for (int iJump = 0; iJump < cJumps; iJump++)
3519 object key = kb.element_keys [iKey];
3520 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3522 SwitchLabel sl = (SwitchLabel) Elements [key];
3523 switch_labels [iJump] = sl.GetILLabel (ec);
3524 iKey++;
3526 else
3527 switch_labels [iJump] = lbl_default;
3529 // emit the switch opcode
3530 ig.Emit (OpCodes.Switch, switch_labels);
3533 // mark the default for this block
3534 if (iBlock != 0)
3535 ig.MarkLabel (lbl_default);
3538 // TODO: find the default case and emit it here,
3539 // to prevent having to do the following jump.
3540 // make sure to mark other labels in the default section
3542 // the last default just goes to the end
3543 if (element_keys.Length > 0)
3544 ig.Emit (OpCodes.Br, lbl_default);
3546 // now emit the code for the sections
3547 bool found_default = false;
3549 foreach (SwitchSection ss in Sections) {
3550 foreach (SwitchLabel sl in ss.Labels) {
3551 if (sl.Converted == SwitchLabel.NullStringCase) {
3552 ig.MarkLabel (null_target);
3553 } else if (sl.Label == null) {
3554 ig.MarkLabel (lbl_default);
3555 found_default = true;
3556 if (!has_null_case)
3557 ig.MarkLabel (null_target);
3559 ig.MarkLabel (sl.GetILLabel (ec));
3560 ig.MarkLabel (sl.GetILLabelCode (ec));
3562 ss.Block.Emit (ec);
3565 if (!found_default) {
3566 ig.MarkLabel (lbl_default);
3567 if (!has_null_case) {
3568 ig.MarkLabel (null_target);
3572 ig.MarkLabel (lbl_end);
3575 SwitchSection FindSection (SwitchLabel label)
3577 foreach (SwitchSection ss in Sections){
3578 foreach (SwitchLabel sl in ss.Labels){
3579 if (label == sl)
3580 return ss;
3584 return null;
3587 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3589 foreach (SwitchSection ss in Sections)
3590 ss.Block.MutateHoistedGenericType (storey);
3593 public static void Reset ()
3595 unique_counter = 0;
3596 allowed_types = null;
3599 public override bool Resolve (EmitContext ec)
3601 Expr = Expr.Resolve (ec);
3602 if (Expr == null)
3603 return false;
3605 new_expr = SwitchGoverningType (ec, Expr);
3607 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3608 unwrap = Nullable.Unwrap.Create (Expr, false);
3609 if (unwrap == null)
3610 return false;
3612 new_expr = SwitchGoverningType (ec, unwrap);
3615 if (new_expr == null){
3616 Report.Error (151, loc,
3617 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3618 TypeManager.CSharpName (Expr.Type));
3619 return false;
3622 // Validate switch.
3623 SwitchType = new_expr.Type;
3625 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3626 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3627 return false;
3630 if (!CheckSwitch (ec))
3631 return false;
3633 if (HaveUnwrap)
3634 Elements.Remove (SwitchLabel.NullStringCase);
3636 Switch old_switch = ec.Switch;
3637 ec.Switch = this;
3638 ec.Switch.SwitchType = SwitchType;
3640 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3641 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3643 is_constant = new_expr is Constant;
3644 if (is_constant) {
3645 object key = ((Constant) new_expr).GetValue ();
3646 SwitchLabel label = (SwitchLabel) Elements [key];
3648 constant_section = FindSection (label);
3649 if (constant_section == null)
3650 constant_section = default_section;
3653 bool first = true;
3654 bool ok = true;
3655 foreach (SwitchSection ss in Sections){
3656 if (!first)
3657 ec.CurrentBranching.CreateSibling (
3658 null, FlowBranching.SiblingType.SwitchSection);
3659 else
3660 first = false;
3662 if (is_constant && (ss != constant_section)) {
3663 // If we're a constant switch, we're only emitting
3664 // one single section - mark all the others as
3665 // unreachable.
3666 ec.CurrentBranching.CurrentUsageVector.Goto ();
3667 if (!ss.Block.ResolveUnreachable (ec, true)) {
3668 ok = false;
3670 } else {
3671 if (!ss.Block.Resolve (ec))
3672 ok = false;
3676 if (default_section == null)
3677 ec.CurrentBranching.CreateSibling (
3678 null, FlowBranching.SiblingType.SwitchSection);
3680 ec.EndFlowBranching ();
3681 ec.Switch = old_switch;
3683 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3685 if (!ok)
3686 return false;
3688 if (SwitchType == TypeManager.string_type && !is_constant) {
3689 // TODO: Optimize single case, and single+default case
3690 ResolveStringSwitchMap (ec);
3693 return true;
3696 void ResolveStringSwitchMap (EmitContext ec)
3698 FullNamedExpression string_dictionary_type;
3699 if (TypeManager.generic_ienumerable_type != null) {
3700 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3701 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3703 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3704 new TypeArguments (
3705 new TypeExpression (TypeManager.string_type, loc),
3706 new TypeExpression (TypeManager.int32_type, loc)), loc);
3707 } else {
3708 MemberAccess system_collections_generic = new MemberAccess (
3709 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3711 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3714 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3715 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3716 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3717 if (!field.Define ())
3718 return;
3719 ec.TypeContainer.PartialContainer.AddField (field);
3721 ArrayList init = new ArrayList ();
3722 int counter = 0;
3723 Elements.Clear ();
3724 string value = null;
3725 foreach (SwitchSection section in Sections) {
3726 int last_count = init.Count;
3727 foreach (SwitchLabel sl in section.Labels) {
3728 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3729 continue;
3731 value = (string) sl.Converted;
3732 ArrayList init_args = new ArrayList (2);
3733 init_args.Add (new StringLiteral (value, sl.Location));
3734 init_args.Add (new IntConstant (counter, loc));
3735 init.Add (new CollectionElementInitializer (init_args, loc));
3739 // Don't add empty sections
3741 if (last_count == init.Count)
3742 continue;
3744 Elements.Add (counter, section.Labels [0]);
3745 ++counter;
3748 Arguments args = new Arguments (1);
3749 args.Add (new Argument (new IntConstant (init.Count, loc)));
3750 Expression initializer = new NewInitialize (string_dictionary_type, args,
3751 new CollectionOrObjectInitializers (init, loc), loc);
3753 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3754 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3757 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3759 ILGenerator ig = ec.ig;
3760 Label l_initialized = ig.DefineLabel ();
3763 // Skip initialization when value is null
3765 value.EmitBranchable (ec, null_target, false);
3768 // Check if string dictionary is initialized and initialize
3770 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3771 string_dictionary.EmitStatement (ec);
3772 ig.MarkLabel (l_initialized);
3774 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3776 if (TypeManager.generic_ienumerable_type != null) {
3777 Arguments get_value_args = new Arguments (2);
3778 get_value_args.Add (new Argument (value));
3779 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3780 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3781 if (get_item == null)
3782 return;
3785 // A value was not found, go to default case
3787 get_item.EmitBranchable (ec, default_target, false);
3788 } else {
3789 Arguments get_value_args = new Arguments (1);
3790 get_value_args.Add (new Argument (value));
3792 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3793 if (get_item == null)
3794 return;
3796 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3797 get_item_object.EmitAssign (ec, get_item, true, false);
3798 ec.ig.Emit (OpCodes.Brfalse, default_target);
3800 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3801 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3803 get_item_int.EmitStatement (ec);
3804 get_item_object.Release (ec);
3807 TableSwitchEmit (ec, string_switch_variable);
3808 string_switch_variable.Release (ec);
3811 protected override void DoEmit (EmitContext ec)
3813 ILGenerator ig = ec.ig;
3815 default_target = ig.DefineLabel ();
3816 null_target = ig.DefineLabel ();
3818 // Store variable for comparission purposes
3819 // TODO: Don't duplicate non-captured VariableReference
3820 LocalTemporary value;
3821 if (HaveUnwrap) {
3822 value = new LocalTemporary (SwitchType);
3823 unwrap.EmitCheck (ec);
3824 ig.Emit (OpCodes.Brfalse, null_target);
3825 new_expr.Emit (ec);
3826 value.Store (ec);
3827 } else if (!is_constant) {
3828 value = new LocalTemporary (SwitchType);
3829 new_expr.Emit (ec);
3830 value.Store (ec);
3831 } else
3832 value = null;
3835 // Setup the codegen context
3837 Label old_end = ec.LoopEnd;
3838 Switch old_switch = ec.Switch;
3840 ec.LoopEnd = ig.DefineLabel ();
3841 ec.Switch = this;
3843 // Emit Code.
3844 if (is_constant) {
3845 if (constant_section != null)
3846 constant_section.Block.Emit (ec);
3847 } else if (string_dictionary != null) {
3848 DoEmitStringSwitch (value, ec);
3849 } else {
3850 TableSwitchEmit (ec, value);
3853 if (value != null)
3854 value.Release (ec);
3856 // Restore context state.
3857 ig.MarkLabel (ec.LoopEnd);
3860 // Restore the previous context
3862 ec.LoopEnd = old_end;
3863 ec.Switch = old_switch;
3866 protected override void CloneTo (CloneContext clonectx, Statement t)
3868 Switch target = (Switch) t;
3870 target.Expr = Expr.Clone (clonectx);
3871 target.Sections = new ArrayList ();
3872 foreach (SwitchSection ss in Sections){
3873 target.Sections.Add (ss.Clone (clonectx));
3878 // A place where execution can restart in an iterator
3879 public abstract class ResumableStatement : Statement
3881 bool prepared;
3882 protected Label resume_point;
3884 public Label PrepareForEmit (EmitContext ec)
3886 if (!prepared) {
3887 prepared = true;
3888 resume_point = ec.ig.DefineLabel ();
3890 return resume_point;
3893 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3895 return end;
3897 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3902 // Base class for statements that are implemented in terms of try...finally
3903 public abstract class ExceptionStatement : ResumableStatement
3905 bool code_follows;
3907 protected abstract void EmitPreTryBody (EmitContext ec);
3908 protected abstract void EmitTryBody (EmitContext ec);
3909 protected abstract void EmitFinallyBody (EmitContext ec);
3911 protected sealed override void DoEmit (EmitContext ec)
3913 ILGenerator ig = ec.ig;
3915 EmitPreTryBody (ec);
3917 if (resume_points != null) {
3918 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3919 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3922 ig.BeginExceptionBlock ();
3924 if (resume_points != null) {
3925 ig.MarkLabel (resume_point);
3927 // For normal control flow, we want to fall-through the Switch
3928 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3929 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3930 IntConstant.EmitInt (ig, first_resume_pc);
3931 ig.Emit (OpCodes.Sub);
3933 Label [] labels = new Label [resume_points.Count];
3934 for (int i = 0; i < resume_points.Count; ++i)
3935 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3936 ig.Emit (OpCodes.Switch, labels);
3939 EmitTryBody (ec);
3941 ig.BeginFinallyBlock ();
3943 Label start_finally = ec.ig.DefineLabel ();
3944 if (resume_points != null) {
3945 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3946 ig.Emit (OpCodes.Brfalse_S, start_finally);
3947 ig.Emit (OpCodes.Endfinally);
3950 ig.MarkLabel (start_finally);
3951 EmitFinallyBody (ec);
3953 ig.EndExceptionBlock ();
3956 public void SomeCodeFollows ()
3958 code_follows = true;
3961 protected void ResolveReachability (EmitContext ec)
3963 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3964 // So, ensure there's some IL code after this statement.
3965 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3966 ec.NeedReturnLabel ();
3970 ArrayList resume_points;
3971 int first_resume_pc;
3972 public void AddResumePoint (ResumableStatement stmt, int pc)
3974 if (resume_points == null) {
3975 resume_points = new ArrayList ();
3976 first_resume_pc = pc;
3979 if (pc != first_resume_pc + resume_points.Count)
3980 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3982 resume_points.Add (stmt);
3985 Label dispose_try_block;
3986 bool prepared_for_dispose, emitted_dispose;
3987 public override Label PrepareForDispose (EmitContext ec, Label end)
3989 if (!prepared_for_dispose) {
3990 prepared_for_dispose = true;
3991 dispose_try_block = ec.ig.DefineLabel ();
3993 return dispose_try_block;
3996 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3998 if (emitted_dispose)
3999 return;
4001 emitted_dispose = true;
4003 ILGenerator ig = ec.ig;
4005 Label end_of_try = ig.DefineLabel ();
4007 // Ensure that the only way we can get into this code is through a dispatcher
4008 if (have_dispatcher)
4009 ig.Emit (OpCodes.Br, end);
4011 ig.BeginExceptionBlock ();
4013 ig.MarkLabel (dispose_try_block);
4015 Label [] labels = null;
4016 for (int i = 0; i < resume_points.Count; ++i) {
4017 ResumableStatement s = (ResumableStatement) resume_points [i];
4018 Label ret = s.PrepareForDispose (ec, end_of_try);
4019 if (ret.Equals (end_of_try) && labels == null)
4020 continue;
4021 if (labels == null) {
4022 labels = new Label [resume_points.Count];
4023 for (int j = 0; j < i; ++j)
4024 labels [j] = end_of_try;
4026 labels [i] = ret;
4029 if (labels != null) {
4030 int j;
4031 for (j = 1; j < labels.Length; ++j)
4032 if (!labels [0].Equals (labels [j]))
4033 break;
4034 bool emit_dispatcher = j < labels.Length;
4036 if (emit_dispatcher) {
4037 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4038 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4039 IntConstant.EmitInt (ig, first_resume_pc);
4040 ig.Emit (OpCodes.Sub);
4041 ig.Emit (OpCodes.Switch, labels);
4042 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4045 foreach (ResumableStatement s in resume_points)
4046 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4049 ig.MarkLabel (end_of_try);
4051 ig.BeginFinallyBlock ();
4053 EmitFinallyBody (ec);
4055 ig.EndExceptionBlock ();
4059 public class Lock : ExceptionStatement {
4060 Expression expr;
4061 public Statement Statement;
4062 TemporaryVariable temp;
4064 public Lock (Expression expr, Statement stmt, Location l)
4066 this.expr = expr;
4067 Statement = stmt;
4068 loc = l;
4071 public override bool Resolve (EmitContext ec)
4073 expr = expr.Resolve (ec);
4074 if (expr == null)
4075 return false;
4077 if (!TypeManager.IsReferenceType (expr.Type)){
4078 Report.Error (185, loc,
4079 "`{0}' is not a reference type as required by the lock statement",
4080 TypeManager.CSharpName (expr.Type));
4081 return false;
4084 ec.StartFlowBranching (this);
4085 bool ok = Statement.Resolve (ec);
4086 ec.EndFlowBranching ();
4088 ResolveReachability (ec);
4090 // Avoid creating libraries that reference the internal
4091 // mcs NullType:
4092 Type t = expr.Type;
4093 if (t == TypeManager.null_type)
4094 t = TypeManager.object_type;
4096 temp = new TemporaryVariable (t, loc);
4097 temp.Resolve (ec);
4099 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4100 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4101 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4102 monitor_type, "Enter", loc, TypeManager.object_type);
4103 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4104 monitor_type, "Exit", loc, TypeManager.object_type);
4107 return ok;
4110 protected override void EmitPreTryBody (EmitContext ec)
4112 ILGenerator ig = ec.ig;
4114 temp.EmitAssign (ec, expr);
4115 temp.Emit (ec);
4116 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4119 protected override void EmitTryBody (EmitContext ec)
4121 Statement.Emit (ec);
4124 protected override void EmitFinallyBody (EmitContext ec)
4126 temp.Emit (ec);
4127 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4130 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4132 expr.MutateHoistedGenericType (storey);
4133 temp.MutateHoistedGenericType (storey);
4134 Statement.MutateHoistedGenericType (storey);
4137 protected override void CloneTo (CloneContext clonectx, Statement t)
4139 Lock target = (Lock) t;
4141 target.expr = expr.Clone (clonectx);
4142 target.Statement = Statement.Clone (clonectx);
4146 public class Unchecked : Statement {
4147 public Block Block;
4149 public Unchecked (Block b)
4151 Block = b;
4152 b.Unchecked = true;
4155 public override bool Resolve (EmitContext ec)
4157 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4158 return Block.Resolve (ec);
4161 protected override void DoEmit (EmitContext ec)
4163 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4164 Block.Emit (ec);
4167 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4169 Block.MutateHoistedGenericType (storey);
4172 protected override void CloneTo (CloneContext clonectx, Statement t)
4174 Unchecked target = (Unchecked) t;
4176 target.Block = clonectx.LookupBlock (Block);
4180 public class Checked : Statement {
4181 public Block Block;
4183 public Checked (Block b)
4185 Block = b;
4186 b.Unchecked = false;
4189 public override bool Resolve (EmitContext ec)
4191 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4192 return Block.Resolve (ec);
4195 protected override void DoEmit (EmitContext ec)
4197 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4198 Block.Emit (ec);
4201 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4203 Block.MutateHoistedGenericType (storey);
4206 protected override void CloneTo (CloneContext clonectx, Statement t)
4208 Checked target = (Checked) t;
4210 target.Block = clonectx.LookupBlock (Block);
4214 public class Unsafe : Statement {
4215 public Block Block;
4217 public Unsafe (Block b)
4219 Block = b;
4220 Block.Unsafe = true;
4223 public override bool Resolve (EmitContext ec)
4225 using (ec.With (EmitContext.Flags.InUnsafe, true))
4226 return Block.Resolve (ec);
4229 protected override void DoEmit (EmitContext ec)
4231 using (ec.With (EmitContext.Flags.InUnsafe, true))
4232 Block.Emit (ec);
4235 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4237 Block.MutateHoistedGenericType (storey);
4240 protected override void CloneTo (CloneContext clonectx, Statement t)
4242 Unsafe target = (Unsafe) t;
4244 target.Block = clonectx.LookupBlock (Block);
4249 // Fixed statement
4251 public class Fixed : Statement {
4252 Expression type;
4253 ArrayList declarators;
4254 Statement statement;
4255 Type expr_type;
4256 Emitter[] data;
4257 bool has_ret;
4259 abstract class Emitter
4261 protected LocalInfo vi;
4262 protected Expression converted;
4264 protected Emitter (Expression expr, LocalInfo li)
4266 converted = expr;
4267 vi = li;
4270 public abstract void Emit (EmitContext ec);
4271 public abstract void EmitExit (EmitContext ec);
4274 class ExpressionEmitter : Emitter {
4275 public ExpressionEmitter (Expression converted, LocalInfo li) :
4276 base (converted, li)
4280 public override void Emit (EmitContext ec) {
4282 // Store pointer in pinned location
4284 converted.Emit (ec);
4285 vi.EmitAssign (ec);
4288 public override void EmitExit (EmitContext ec)
4290 ec.ig.Emit (OpCodes.Ldc_I4_0);
4291 ec.ig.Emit (OpCodes.Conv_U);
4292 vi.EmitAssign (ec);
4296 class StringEmitter : Emitter
4298 LocalInfo pinned_string;
4300 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4301 base (expr, li)
4303 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4304 pinned_string.Pinned = true;
4307 public override void Emit (EmitContext ec)
4309 pinned_string.Resolve (ec);
4310 pinned_string.ResolveVariable (ec);
4312 converted.Emit (ec);
4313 pinned_string.EmitAssign (ec);
4315 PropertyInfo p = TypeManager.int_get_offset_to_string_data;
4316 if (p == null) {
4317 // TODO: Move to resolve
4318 p = TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4319 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4321 if (p == null)
4322 return;
4325 // TODO: Should use Binary::Add
4326 pinned_string.Emit (ec);
4327 ec.ig.Emit (OpCodes.Conv_I);
4329 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, p, pinned_string.Location);
4330 //pe.InstanceExpression = pinned_string;
4331 pe.Resolve (ec).Emit (ec);
4333 ec.ig.Emit (OpCodes.Add);
4334 vi.EmitAssign (ec);
4337 public override void EmitExit (EmitContext ec)
4339 ec.ig.Emit (OpCodes.Ldnull);
4340 pinned_string.EmitAssign (ec);
4344 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4346 this.type = type;
4347 declarators = decls;
4348 statement = stmt;
4349 loc = l;
4352 public Statement Statement {
4353 get { return statement; }
4356 public override bool Resolve (EmitContext ec)
4358 if (!ec.InUnsafe){
4359 Expression.UnsafeError (loc);
4360 return false;
4363 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4364 if (texpr == null) {
4365 if (type is VarExpr)
4366 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4368 return false;
4371 expr_type = texpr.Type;
4373 data = new Emitter [declarators.Count];
4375 if (!expr_type.IsPointer){
4376 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4377 return false;
4380 int i = 0;
4381 foreach (Pair p in declarators){
4382 LocalInfo vi = (LocalInfo) p.First;
4383 Expression e = (Expression) p.Second;
4385 vi.VariableInfo.SetAssigned (ec);
4386 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4389 // The rules for the possible declarators are pretty wise,
4390 // but the production on the grammar is more concise.
4392 // So we have to enforce these rules here.
4394 // We do not resolve before doing the case 1 test,
4395 // because the grammar is explicit in that the token &
4396 // is present, so we need to test for this particular case.
4399 if (e is Cast){
4400 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4401 return false;
4404 ec.InFixedInitializer = true;
4405 e = e.Resolve (ec);
4406 ec.InFixedInitializer = false;
4407 if (e == null)
4408 return false;
4411 // Case 2: Array
4413 if (e.Type.IsArray){
4414 Type array_type = TypeManager.GetElementType (e.Type);
4417 // Provided that array_type is unmanaged,
4419 if (!TypeManager.VerifyUnManaged (array_type, loc))
4420 return false;
4423 // and T* is implicitly convertible to the
4424 // pointer type given in the fixed statement.
4426 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4428 Expression converted = Convert.ImplicitConversionRequired (
4429 ec, array_ptr, vi.VariableType, loc);
4430 if (converted == null)
4431 return false;
4434 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4436 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4437 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4438 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4439 new NullPointer (loc),
4440 converted);
4442 converted = converted.Resolve (ec);
4444 data [i] = new ExpressionEmitter (converted, vi);
4445 i++;
4447 continue;
4451 // Case 3: string
4453 if (e.Type == TypeManager.string_type){
4454 data [i] = new StringEmitter (e, vi, loc);
4455 i++;
4456 continue;
4459 // Case 4: fixed buffer
4460 if (e is FixedBufferPtr) {
4461 data [i++] = new ExpressionEmitter (e, vi);
4462 continue;
4466 // Case 1: & object.
4468 Unary u = e as Unary;
4469 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4470 IVariableReference vr = u.Expr as IVariableReference;
4471 if (vr == null || !vr.IsFixed) {
4472 data [i] = new ExpressionEmitter (e, vi);
4476 if (data [i++] == null)
4477 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4479 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4482 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4483 bool ok = statement.Resolve (ec);
4484 bool flow_unreachable = ec.EndFlowBranching ();
4485 has_ret = flow_unreachable;
4487 return ok;
4490 protected override void DoEmit (EmitContext ec)
4492 for (int i = 0; i < data.Length; i++) {
4493 data [i].Emit (ec);
4496 statement.Emit (ec);
4498 if (has_ret)
4499 return;
4502 // Clear the pinned variable
4504 for (int i = 0; i < data.Length; i++) {
4505 data [i].EmitExit (ec);
4509 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4511 // Fixed statement cannot be used inside anonymous methods or lambdas
4512 throw new NotSupportedException ();
4515 protected override void CloneTo (CloneContext clonectx, Statement t)
4517 Fixed target = (Fixed) t;
4519 target.type = type.Clone (clonectx);
4520 target.declarators = new ArrayList (declarators.Count);
4521 foreach (Pair p in declarators) {
4522 LocalInfo vi = (LocalInfo) p.First;
4523 Expression e = (Expression) p.Second;
4525 target.declarators.Add (
4526 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4529 target.statement = statement.Clone (clonectx);
4533 public class Catch : Statement {
4534 public readonly string Name;
4535 public Block Block;
4536 public Block VarBlock;
4538 Expression type_expr;
4539 Type type;
4541 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4543 type_expr = type;
4544 Name = name;
4545 Block = block;
4546 VarBlock = var_block;
4547 loc = l;
4550 public Type CatchType {
4551 get {
4552 return type;
4556 public bool IsGeneral {
4557 get {
4558 return type_expr == null;
4562 protected override void DoEmit (EmitContext ec)
4564 ILGenerator ig = ec.ig;
4566 if (CatchType != null)
4567 ig.BeginCatchBlock (CatchType);
4568 else
4569 ig.BeginCatchBlock (TypeManager.object_type);
4571 if (VarBlock != null)
4572 VarBlock.Emit (ec);
4574 if (Name != null) {
4575 // TODO: Move to resolve
4576 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4577 lvr.Resolve (ec);
4579 #if GMCS_SOURCE
4580 // Only to make verifier happy
4581 if (TypeManager.IsGenericParameter (lvr.Type))
4582 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4583 #endif
4585 Expression source;
4586 if (lvr.IsHoisted) {
4587 LocalTemporary lt = new LocalTemporary (lvr.Type);
4588 lt.Store (ec);
4589 source = lt;
4590 } else {
4591 // Variable is at the top of the stack
4592 source = EmptyExpression.Null;
4595 lvr.EmitAssign (ec, source, false, false);
4596 } else
4597 ig.Emit (OpCodes.Pop);
4599 Block.Emit (ec);
4602 public override bool Resolve (EmitContext ec)
4604 using (ec.With (EmitContext.Flags.InCatch, true)) {
4605 if (type_expr != null) {
4606 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4607 if (te == null)
4608 return false;
4610 type = te.Type;
4612 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4613 Error (155, "The type caught or thrown must be derived from System.Exception");
4614 return false;
4616 } else
4617 type = null;
4619 if (!Block.Resolve (ec))
4620 return false;
4622 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4623 // emit the "unused variable" warnings.
4624 if (VarBlock != null)
4625 return VarBlock.Resolve (ec);
4627 return true;
4631 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4633 if (type != null)
4634 type = storey.MutateType (type);
4635 if (VarBlock != null)
4636 VarBlock.MutateHoistedGenericType (storey);
4637 Block.MutateHoistedGenericType (storey);
4640 protected override void CloneTo (CloneContext clonectx, Statement t)
4642 Catch target = (Catch) t;
4644 if (type_expr != null)
4645 target.type_expr = type_expr.Clone (clonectx);
4646 if (VarBlock != null)
4647 target.VarBlock = clonectx.LookupBlock (VarBlock);
4648 target.Block = clonectx.LookupBlock (Block);
4652 public class TryFinally : ExceptionStatement {
4653 Statement stmt;
4654 Block fini;
4656 public TryFinally (Statement stmt, Block fini, Location l)
4658 this.stmt = stmt;
4659 this.fini = fini;
4660 loc = l;
4663 public override bool Resolve (EmitContext ec)
4665 bool ok = true;
4667 ec.StartFlowBranching (this);
4669 if (!stmt.Resolve (ec))
4670 ok = false;
4672 if (ok)
4673 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4674 using (ec.With (EmitContext.Flags.InFinally, true)) {
4675 if (!fini.Resolve (ec))
4676 ok = false;
4679 ec.EndFlowBranching ();
4681 ResolveReachability (ec);
4683 return ok;
4686 protected override void EmitPreTryBody (EmitContext ec)
4690 protected override void EmitTryBody (EmitContext ec)
4692 stmt.Emit (ec);
4695 protected override void EmitFinallyBody (EmitContext ec)
4697 fini.Emit (ec);
4700 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4702 stmt.MutateHoistedGenericType (storey);
4703 fini.MutateHoistedGenericType (storey);
4706 protected override void CloneTo (CloneContext clonectx, Statement t)
4708 TryFinally target = (TryFinally) t;
4710 target.stmt = (Statement) stmt.Clone (clonectx);
4711 if (fini != null)
4712 target.fini = clonectx.LookupBlock (fini);
4716 public class TryCatch : Statement {
4717 public Block Block;
4718 public ArrayList Specific;
4719 public Catch General;
4720 bool inside_try_finally, code_follows;
4722 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4724 this.Block = block;
4725 this.Specific = catch_clauses;
4726 this.General = null;
4727 this.inside_try_finally = inside_try_finally;
4729 for (int i = 0; i < catch_clauses.Count; ++i) {
4730 Catch c = (Catch) catch_clauses [i];
4731 if (c.IsGeneral) {
4732 if (i != catch_clauses.Count - 1)
4733 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4734 this.General = c;
4735 catch_clauses.RemoveAt (i);
4736 i--;
4740 loc = l;
4743 public override bool Resolve (EmitContext ec)
4745 bool ok = true;
4747 ec.StartFlowBranching (this);
4749 if (!Block.Resolve (ec))
4750 ok = false;
4752 Type[] prev_catches = new Type [Specific.Count];
4753 int last_index = 0;
4754 foreach (Catch c in Specific){
4755 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4757 if (c.Name != null) {
4758 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4759 if (vi == null)
4760 throw new Exception ();
4762 vi.VariableInfo = null;
4765 if (!c.Resolve (ec)) {
4766 ok = false;
4767 continue;
4770 Type resolved_type = c.CatchType;
4771 for (int ii = 0; ii < last_index; ++ii) {
4772 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4773 Report.Error (160, c.loc,
4774 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4775 TypeManager.CSharpName (prev_catches [ii]));
4776 ok = false;
4780 prev_catches [last_index++] = resolved_type;
4783 if (General != null) {
4784 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4785 foreach (Catch c in Specific){
4786 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4787 Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4792 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4794 if (!General.Resolve (ec))
4795 ok = false;
4798 ec.EndFlowBranching ();
4800 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4801 // So, ensure there's some IL code after this statement
4802 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4803 ec.NeedReturnLabel ();
4805 return ok;
4808 public void SomeCodeFollows ()
4810 code_follows = true;
4813 protected override void DoEmit (EmitContext ec)
4815 ILGenerator ig = ec.ig;
4817 if (!inside_try_finally)
4818 ig.BeginExceptionBlock ();
4820 Block.Emit (ec);
4822 foreach (Catch c in Specific)
4823 c.Emit (ec);
4825 if (General != null)
4826 General.Emit (ec);
4828 if (!inside_try_finally)
4829 ig.EndExceptionBlock ();
4832 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4834 Block.MutateHoistedGenericType (storey);
4836 if (General != null)
4837 General.MutateHoistedGenericType (storey);
4838 if (Specific != null) {
4839 foreach (Catch c in Specific)
4840 c.MutateHoistedGenericType (storey);
4844 protected override void CloneTo (CloneContext clonectx, Statement t)
4846 TryCatch target = (TryCatch) t;
4848 target.Block = clonectx.LookupBlock (Block);
4849 if (General != null)
4850 target.General = (Catch) General.Clone (clonectx);
4851 if (Specific != null){
4852 target.Specific = new ArrayList ();
4853 foreach (Catch c in Specific)
4854 target.Specific.Add (c.Clone (clonectx));
4859 // FIXME: Why is it almost exact copy of Using ??
4860 public class UsingTemporary : ExceptionStatement {
4861 TemporaryVariable local_copy;
4862 public Statement Statement;
4863 Expression expr;
4864 Type expr_type;
4866 public UsingTemporary (Expression expr, Statement stmt, Location l)
4868 this.expr = expr;
4869 Statement = stmt;
4870 loc = l;
4873 public override bool Resolve (EmitContext ec)
4875 expr = expr.Resolve (ec);
4876 if (expr == null)
4877 return false;
4879 expr_type = expr.Type;
4881 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4882 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4883 Using.Error_IsNotConvertibleToIDisposable (expr);
4884 return false;
4888 local_copy = new TemporaryVariable (expr_type, loc);
4889 local_copy.Resolve (ec);
4891 ec.StartFlowBranching (this);
4893 bool ok = Statement.Resolve (ec);
4895 ec.EndFlowBranching ();
4897 ResolveReachability (ec);
4899 if (TypeManager.void_dispose_void == null) {
4900 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4901 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4904 return ok;
4907 protected override void EmitPreTryBody (EmitContext ec)
4909 local_copy.EmitAssign (ec, expr);
4912 protected override void EmitTryBody (EmitContext ec)
4914 Statement.Emit (ec);
4917 protected override void EmitFinallyBody (EmitContext ec)
4919 ILGenerator ig = ec.ig;
4920 if (!TypeManager.IsStruct (expr_type)) {
4921 Label skip = ig.DefineLabel ();
4922 local_copy.Emit (ec);
4923 ig.Emit (OpCodes.Brfalse, skip);
4924 local_copy.Emit (ec);
4925 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4926 ig.MarkLabel (skip);
4927 return;
4930 Expression ml = Expression.MemberLookup (
4931 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4932 "Dispose", Location.Null);
4934 if (!(ml is MethodGroupExpr)) {
4935 local_copy.Emit (ec);
4936 ig.Emit (OpCodes.Box, expr_type);
4937 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4938 return;
4941 MethodInfo mi = null;
4943 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4944 if (TypeManager.GetParameterData (mk).Count == 0) {
4945 mi = mk;
4946 break;
4950 if (mi == null) {
4951 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4952 return;
4955 local_copy.AddressOf (ec, AddressOp.Load);
4956 ig.Emit (OpCodes.Call, mi);
4959 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4961 expr_type = storey.MutateType (expr_type);
4962 local_copy.MutateHoistedGenericType (storey);
4963 Statement.MutateHoistedGenericType (storey);
4966 protected override void CloneTo (CloneContext clonectx, Statement t)
4968 UsingTemporary target = (UsingTemporary) t;
4970 target.expr = expr.Clone (clonectx);
4971 target.Statement = Statement.Clone (clonectx);
4975 public class Using : ExceptionStatement {
4976 Statement stmt;
4977 public Statement EmbeddedStatement {
4978 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4981 Expression var;
4982 Expression init;
4984 ExpressionStatement assign;
4986 public Using (Expression var, Expression init, Statement stmt, Location l)
4988 this.var = var;
4989 this.init = init;
4990 this.stmt = stmt;
4991 loc = l;
4994 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4996 Report.SymbolRelatedToPreviousError (expr.Type);
4997 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4998 expr.GetSignatureForError ());
5001 protected override void EmitPreTryBody (EmitContext ec)
5003 assign.EmitStatement (ec);
5006 protected override void EmitTryBody (EmitContext ec)
5008 stmt.Emit (ec);
5011 protected override void EmitFinallyBody (EmitContext ec)
5013 ILGenerator ig = ec.ig;
5014 Label skip = ig.DefineLabel ();
5016 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5017 if (emit_null_check) {
5018 var.Emit (ec);
5019 ig.Emit (OpCodes.Brfalse, skip);
5022 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5024 if (emit_null_check)
5025 ig.MarkLabel (skip);
5028 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5030 assign.MutateHoistedGenericType (storey);
5031 var.MutateHoistedGenericType (storey);
5032 stmt.MutateHoistedGenericType (storey);
5035 public override bool Resolve (EmitContext ec)
5037 if (!ResolveVariable (ec))
5038 return false;
5040 ec.StartFlowBranching (this);
5042 bool ok = stmt.Resolve (ec);
5044 ec.EndFlowBranching ();
5046 ResolveReachability (ec);
5048 if (TypeManager.void_dispose_void == null) {
5049 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5050 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5053 return ok;
5056 bool ResolveVariable (EmitContext ec)
5058 assign = new SimpleAssign (var, init, loc);
5059 assign = assign.ResolveStatement (ec);
5060 if (assign == null)
5061 return false;
5063 if (assign.Type == TypeManager.idisposable_type ||
5064 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5065 return true;
5068 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5069 if (e == null) {
5070 Error_IsNotConvertibleToIDisposable (var);
5071 return false;
5074 throw new NotImplementedException ("covariance?");
5077 protected override void CloneTo (CloneContext clonectx, Statement t)
5079 Using target = (Using) t;
5081 target.var = var.Clone (clonectx);
5082 target.init = init.Clone (clonectx);
5083 target.stmt = stmt.Clone (clonectx);
5087 /// <summary>
5088 /// Implementation of the foreach C# statement
5089 /// </summary>
5090 public class Foreach : Statement {
5092 sealed class ArrayForeach : Statement
5094 class ArrayCounter : TemporaryVariable
5096 StatementExpression increment;
5098 public ArrayCounter (Location loc)
5099 : base (TypeManager.int32_type, loc)
5103 public void ResolveIncrement (EmitContext ec)
5105 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5106 increment.Resolve (ec);
5109 public void EmitIncrement (EmitContext ec)
5111 increment.Emit (ec);
5115 readonly Foreach for_each;
5116 readonly Statement statement;
5118 Expression conv;
5119 TemporaryVariable[] lengths;
5120 Expression [] length_exprs;
5121 ArrayCounter[] counter;
5123 TemporaryVariable copy;
5124 Expression access;
5126 public ArrayForeach (Foreach @foreach, int rank)
5128 for_each = @foreach;
5129 statement = for_each.statement;
5130 loc = @foreach.loc;
5132 counter = new ArrayCounter [rank];
5133 length_exprs = new Expression [rank];
5136 // Only use temporary length variables when dealing with
5137 // multi-dimensional arrays
5139 if (rank > 1)
5140 lengths = new TemporaryVariable [rank];
5143 protected override void CloneTo (CloneContext clonectx, Statement target)
5145 throw new NotImplementedException ();
5148 public override bool Resolve (EmitContext ec)
5150 copy = new TemporaryVariable (for_each.expr.Type, loc);
5151 copy.Resolve (ec);
5153 int rank = length_exprs.Length;
5154 Arguments list = new Arguments (rank);
5155 for (int i = 0; i < rank; i++) {
5156 counter [i] = new ArrayCounter (loc);
5157 counter [i].ResolveIncrement (ec);
5159 if (rank == 1) {
5160 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5161 } else {
5162 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5163 lengths [i].Resolve (ec);
5165 Arguments args = new Arguments (1);
5166 args.Add (new Argument (new IntConstant (i, loc)));
5167 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5170 list.Add (new Argument (counter [i]));
5173 access = new ElementAccess (copy, list).Resolve (ec);
5174 if (access == null)
5175 return false;
5177 Expression var_type = for_each.type;
5178 VarExpr ve = var_type as VarExpr;
5179 if (ve != null) {
5180 // Infer implicitly typed local variable from foreach array type
5181 var_type = new TypeExpression (access.Type, ve.Location);
5184 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5185 if (var_type == null)
5186 return false;
5188 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5189 if (conv == null)
5190 return false;
5192 bool ok = true;
5194 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5195 ec.CurrentBranching.CreateSibling ();
5197 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5198 if (for_each.variable == null)
5199 ok = false;
5201 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5202 if (!statement.Resolve (ec))
5203 ok = false;
5204 ec.EndFlowBranching ();
5206 // There's no direct control flow from the end of the embedded statement to the end of the loop
5207 ec.CurrentBranching.CurrentUsageVector.Goto ();
5209 ec.EndFlowBranching ();
5211 return ok;
5214 protected override void DoEmit (EmitContext ec)
5216 ILGenerator ig = ec.ig;
5218 copy.EmitAssign (ec, for_each.expr);
5220 int rank = length_exprs.Length;
5221 Label[] test = new Label [rank];
5222 Label[] loop = new Label [rank];
5224 for (int i = 0; i < rank; i++) {
5225 test [i] = ig.DefineLabel ();
5226 loop [i] = ig.DefineLabel ();
5228 if (lengths != null)
5229 lengths [i].EmitAssign (ec, length_exprs [i]);
5232 IntConstant zero = new IntConstant (0, loc);
5233 for (int i = 0; i < rank; i++) {
5234 counter [i].EmitAssign (ec, zero);
5236 ig.Emit (OpCodes.Br, test [i]);
5237 ig.MarkLabel (loop [i]);
5240 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5242 statement.Emit (ec);
5244 ig.MarkLabel (ec.LoopBegin);
5246 for (int i = rank - 1; i >= 0; i--){
5247 counter [i].EmitIncrement (ec);
5249 ig.MarkLabel (test [i]);
5250 counter [i].Emit (ec);
5252 if (lengths != null)
5253 lengths [i].Emit (ec);
5254 else
5255 length_exprs [i].Emit (ec);
5257 ig.Emit (OpCodes.Blt, loop [i]);
5260 ig.MarkLabel (ec.LoopEnd);
5263 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5265 for_each.expr.MutateHoistedGenericType (storey);
5267 copy.MutateHoistedGenericType (storey);
5268 conv.MutateHoistedGenericType (storey);
5269 statement.MutateHoistedGenericType (storey);
5271 for (int i = 0; i < counter.Length; i++) {
5272 counter [i].MutateHoistedGenericType (storey);
5273 if (lengths != null)
5274 lengths [i].MutateHoistedGenericType (storey);
5279 sealed class CollectionForeach : Statement
5281 class CollectionForeachStatement : Statement
5283 Type type;
5284 Expression variable, current, conv;
5285 Statement statement;
5286 Assign assign;
5288 public CollectionForeachStatement (Type type, Expression variable,
5289 Expression current, Statement statement,
5290 Location loc)
5292 this.type = type;
5293 this.variable = variable;
5294 this.current = current;
5295 this.statement = statement;
5296 this.loc = loc;
5299 protected override void CloneTo (CloneContext clonectx, Statement target)
5301 throw new NotImplementedException ();
5304 public override bool Resolve (EmitContext ec)
5306 current = current.Resolve (ec);
5307 if (current == null)
5308 return false;
5310 conv = Convert.ExplicitConversion (ec, current, type, loc);
5311 if (conv == null)
5312 return false;
5314 assign = new SimpleAssign (variable, conv, loc);
5315 if (assign.Resolve (ec) == null)
5316 return false;
5318 if (!statement.Resolve (ec))
5319 return false;
5321 return true;
5324 protected override void DoEmit (EmitContext ec)
5326 assign.EmitStatement (ec);
5327 statement.Emit (ec);
5330 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5332 assign.MutateHoistedGenericType (storey);
5333 statement.MutateHoistedGenericType (storey);
5337 Expression variable, expr;
5338 Statement statement;
5340 TemporaryVariable enumerator;
5341 Expression init;
5342 Statement loop;
5343 Statement wrapper;
5345 MethodGroupExpr get_enumerator;
5346 PropertyExpr get_current;
5347 MethodInfo move_next;
5348 Expression var_type;
5349 Type enumerator_type;
5350 bool enumerator_found;
5352 public CollectionForeach (Expression var_type, Expression var,
5353 Expression expr, Statement stmt, Location l)
5355 this.var_type = var_type;
5356 this.variable = var;
5357 this.expr = expr;
5358 statement = stmt;
5359 loc = l;
5362 protected override void CloneTo (CloneContext clonectx, Statement target)
5364 throw new NotImplementedException ();
5367 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5369 Type return_type = mi.ReturnType;
5372 // Ok, we can access it, now make sure that we can do something
5373 // with this `GetEnumerator'
5376 if (return_type == TypeManager.ienumerator_type ||
5377 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5379 // If it is not an interface, lets try to find the methods ourselves.
5380 // For example, if we have:
5381 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5382 // We can avoid the iface call. This is a runtime perf boost.
5383 // even bigger if we have a ValueType, because we avoid the cost
5384 // of boxing.
5386 // We have to make sure that both methods exist for us to take
5387 // this path. If one of the methods does not exist, we will just
5388 // use the interface. Sadly, this complex if statement is the only
5389 // way I could do this without a goto
5392 if (TypeManager.bool_movenext_void == null) {
5393 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5394 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5397 if (TypeManager.ienumerator_getcurrent == null) {
5398 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5399 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5403 // Prefer a generic enumerator over a non-generic one.
5405 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5406 enumerator_type = return_type;
5407 if (!FetchGetCurrent (ec, return_type))
5408 get_current = new PropertyExpr (
5409 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5410 if (!FetchMoveNext (return_type))
5411 move_next = TypeManager.bool_movenext_void;
5412 return true;
5415 if (return_type.IsInterface ||
5416 !FetchMoveNext (return_type) ||
5417 !FetchGetCurrent (ec, return_type)) {
5418 enumerator_type = return_type;
5419 move_next = TypeManager.bool_movenext_void;
5420 get_current = new PropertyExpr (
5421 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5422 return true;
5424 } else {
5426 // Ok, so they dont return an IEnumerable, we will have to
5427 // find if they support the GetEnumerator pattern.
5430 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5431 Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5432 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5433 return false;
5437 enumerator_type = return_type;
5439 return true;
5443 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5445 bool FetchMoveNext (Type t)
5447 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5448 MemberTypes.Method,
5449 BindingFlags.Public | BindingFlags.Instance,
5450 "MoveNext", null);
5452 foreach (MemberInfo m in move_next_list){
5453 MethodInfo mi = (MethodInfo) m;
5455 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5456 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5457 move_next = mi;
5458 return true;
5462 return false;
5466 // Retrieves a `public T get_Current ()' method from the Type `t'
5468 bool FetchGetCurrent (EmitContext ec, Type t)
5470 PropertyExpr pe = Expression.MemberLookup (
5471 ec.ContainerType, t, "Current", MemberTypes.Property,
5472 Expression.AllBindingFlags, loc) as PropertyExpr;
5473 if (pe == null)
5474 return false;
5476 get_current = pe;
5477 return true;
5480 void Error_Enumerator ()
5482 if (enumerator_found) {
5483 return;
5486 Report.Error (1579, loc,
5487 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5488 TypeManager.CSharpName (expr.Type));
5491 bool IsOverride (MethodInfo m)
5493 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5495 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5496 return false;
5497 if (m is MethodBuilder)
5498 return true;
5500 MethodInfo base_method = m.GetBaseDefinition ();
5501 return base_method != m;
5504 bool TryType (EmitContext ec, Type t)
5506 MethodGroupExpr mg = Expression.MemberLookup (
5507 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5508 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5509 if (mg == null)
5510 return false;
5512 MethodInfo result = null;
5513 MethodInfo tmp_move_next = null;
5514 PropertyExpr tmp_get_cur = null;
5515 Type tmp_enumerator_type = enumerator_type;
5516 foreach (MethodInfo mi in mg.Methods) {
5517 if (TypeManager.GetParameterData (mi).Count != 0)
5518 continue;
5520 // Check whether GetEnumerator is public
5521 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5522 continue;
5524 if (IsOverride (mi))
5525 continue;
5527 enumerator_found = true;
5529 if (!GetEnumeratorFilter (ec, mi))
5530 continue;
5532 if (result != null) {
5533 if (TypeManager.IsGenericType (result.ReturnType)) {
5534 if (!TypeManager.IsGenericType (mi.ReturnType))
5535 continue;
5537 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5538 Report.SymbolRelatedToPreviousError (t);
5539 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5540 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5541 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5542 return false;
5545 // Always prefer generics enumerators
5546 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5547 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5548 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5549 continue;
5551 Report.SymbolRelatedToPreviousError (result);
5552 Report.SymbolRelatedToPreviousError (mi);
5553 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5554 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5555 return false;
5558 result = mi;
5559 tmp_move_next = move_next;
5560 tmp_get_cur = get_current;
5561 tmp_enumerator_type = enumerator_type;
5562 if (mi.DeclaringType == t)
5563 break;
5566 if (result != null) {
5567 move_next = tmp_move_next;
5568 get_current = tmp_get_cur;
5569 enumerator_type = tmp_enumerator_type;
5570 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5571 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5573 if (t != expr.Type) {
5574 expr = Convert.ExplicitConversion (
5575 ec, expr, t, loc);
5576 if (expr == null)
5577 throw new InternalErrorException ();
5580 get_enumerator.InstanceExpression = expr;
5581 get_enumerator.IsBase = t != expr.Type;
5583 return true;
5586 return false;
5589 bool ProbeCollectionType (EmitContext ec, Type t)
5591 int errors = Report.Errors;
5592 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5593 if (TryType (ec, tt))
5594 return true;
5595 tt = tt.BaseType;
5598 if (Report.Errors > errors)
5599 return false;
5602 // Now try to find the method in the interfaces
5604 Type [] ifaces = TypeManager.GetInterfaces (t);
5605 foreach (Type i in ifaces){
5606 if (TryType (ec, i))
5607 return true;
5610 return false;
5613 public override bool Resolve (EmitContext ec)
5615 enumerator_type = TypeManager.ienumerator_type;
5617 if (!ProbeCollectionType (ec, expr.Type)) {
5618 Error_Enumerator ();
5619 return false;
5622 VarExpr ve = var_type as VarExpr;
5623 if (ve != null) {
5624 // Infer implicitly typed local variable from foreach enumerable type
5625 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5628 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5629 if (var_type == null)
5630 return false;
5632 enumerator = new TemporaryVariable (enumerator_type, loc);
5633 enumerator.Resolve (ec);
5635 init = new Invocation (get_enumerator, null);
5636 init = init.Resolve (ec);
5637 if (init == null)
5638 return false;
5640 Expression move_next_expr;
5642 MemberInfo[] mi = new MemberInfo[] { move_next };
5643 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5644 mg.InstanceExpression = enumerator;
5646 move_next_expr = new Invocation (mg, null);
5649 get_current.InstanceExpression = enumerator;
5651 Statement block = new CollectionForeachStatement (
5652 var_type.Type, variable, get_current, statement, loc);
5654 loop = new While (move_next_expr, block, loc);
5657 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5658 if (implements_idisposable || !enumerator_type.IsSealed) {
5659 wrapper = new DisposableWrapper (this, implements_idisposable);
5660 } else {
5661 wrapper = new NonDisposableWrapper (this);
5664 return wrapper.Resolve (ec);
5667 protected override void DoEmit (EmitContext ec)
5669 wrapper.Emit (ec);
5672 class NonDisposableWrapper : Statement {
5673 CollectionForeach parent;
5675 internal NonDisposableWrapper (CollectionForeach parent)
5677 this.parent = parent;
5680 protected override void CloneTo (CloneContext clonectx, Statement target)
5682 throw new NotSupportedException ();
5685 public override bool Resolve (EmitContext ec)
5687 return parent.ResolveLoop (ec);
5690 protected override void DoEmit (EmitContext ec)
5692 parent.EmitLoopInit (ec);
5693 parent.EmitLoopBody (ec);
5696 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5698 throw new NotSupportedException ();
5702 sealed class DisposableWrapper : ExceptionStatement
5704 CollectionForeach parent;
5705 bool implements_idisposable;
5707 internal DisposableWrapper (CollectionForeach parent, bool implements)
5709 this.parent = parent;
5710 this.implements_idisposable = implements;
5713 protected override void CloneTo (CloneContext clonectx, Statement target)
5715 throw new NotSupportedException ();
5718 public override bool Resolve (EmitContext ec)
5720 bool ok = true;
5722 ec.StartFlowBranching (this);
5724 if (!parent.ResolveLoop (ec))
5725 ok = false;
5727 ec.EndFlowBranching ();
5729 ResolveReachability (ec);
5731 if (TypeManager.void_dispose_void == null) {
5732 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5733 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5735 return ok;
5738 protected override void EmitPreTryBody (EmitContext ec)
5740 parent.EmitLoopInit (ec);
5743 protected override void EmitTryBody (EmitContext ec)
5745 parent.EmitLoopBody (ec);
5748 protected override void EmitFinallyBody (EmitContext ec)
5750 Expression instance = parent.enumerator;
5751 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5752 ILGenerator ig = ec.ig;
5754 parent.enumerator.Emit (ec);
5756 Label call_dispose = ig.DefineLabel ();
5758 if (!implements_idisposable) {
5759 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5760 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5761 temp.Store (ec);
5762 temp.Emit (ec);
5763 instance = temp;
5766 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5768 // using 'endfinally' to empty the evaluation stack
5769 ig.Emit (OpCodes.Endfinally);
5770 ig.MarkLabel (call_dispose);
5773 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5776 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5778 throw new NotSupportedException ();
5782 bool ResolveLoop (EmitContext ec)
5784 return loop.Resolve (ec);
5787 void EmitLoopInit (EmitContext ec)
5789 enumerator.EmitAssign (ec, init);
5792 void EmitLoopBody (EmitContext ec)
5794 loop.Emit (ec);
5797 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5799 enumerator_type = storey.MutateType (enumerator_type);
5800 init.MutateHoistedGenericType (storey);
5801 loop.MutateHoistedGenericType (storey);
5805 Expression type;
5806 Expression variable;
5807 Expression expr;
5808 Statement statement;
5810 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5811 Statement stmt, Location l)
5813 this.type = type;
5814 this.variable = var;
5815 this.expr = expr;
5816 statement = stmt;
5817 loc = l;
5820 public Statement Statement {
5821 get { return statement; }
5824 public override bool Resolve (EmitContext ec)
5826 expr = expr.Resolve (ec);
5827 if (expr == null)
5828 return false;
5830 if (expr.IsNull) {
5831 Report.Error (186, loc, "Use of null is not valid in this context");
5832 return false;
5835 if (expr.Type == TypeManager.string_type) {
5836 statement = new ArrayForeach (this, 1);
5837 } else if (expr.Type.IsArray) {
5838 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5839 } else {
5840 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5841 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5842 expr.ExprClassName);
5843 return false;
5846 statement = new CollectionForeach (type, variable, expr, statement, loc);
5849 return statement.Resolve (ec);
5852 protected override void DoEmit (EmitContext ec)
5854 ILGenerator ig = ec.ig;
5856 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5857 ec.LoopBegin = ig.DefineLabel ();
5858 ec.LoopEnd = ig.DefineLabel ();
5860 statement.Emit (ec);
5862 ec.LoopBegin = old_begin;
5863 ec.LoopEnd = old_end;
5866 protected override void CloneTo (CloneContext clonectx, Statement t)
5868 Foreach target = (Foreach) t;
5870 target.type = type.Clone (clonectx);
5871 target.variable = variable.Clone (clonectx);
5872 target.expr = expr.Clone (clonectx);
5873 target.statement = statement.Clone (clonectx);
5876 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5878 statement.MutateHoistedGenericType (storey);