In class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine:
[mcs.git] / mcs / statement.cs
blob880ec5670e0e9ce4d00a44c74f3bf91924097170
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 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1154 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1155 else
1156 Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1158 return true;
1161 protected override void DoEmit (EmitContext ec)
1163 if (expr == null)
1164 ec.ig.Emit (OpCodes.Rethrow);
1165 else {
1166 expr.Emit (ec);
1168 ec.ig.Emit (OpCodes.Throw);
1172 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1174 if (expr != null)
1175 expr.MutateHoistedGenericType (storey);
1178 protected override void CloneTo (CloneContext clonectx, Statement t)
1180 Throw target = (Throw) t;
1182 if (expr != null)
1183 target.expr = expr.Clone (clonectx);
1187 public class Break : Statement {
1189 public Break (Location l)
1191 loc = l;
1194 bool unwind_protect;
1196 public override bool Resolve (EmitContext ec)
1198 int errors = Report.Errors;
1199 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1200 ec.CurrentBranching.CurrentUsageVector.Goto ();
1201 return errors == Report.Errors;
1204 protected override void DoEmit (EmitContext ec)
1206 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1209 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1213 protected override void CloneTo (CloneContext clonectx, Statement t)
1215 // nothing needed
1219 public class Continue : Statement {
1221 public Continue (Location l)
1223 loc = l;
1226 bool unwind_protect;
1228 public override bool Resolve (EmitContext ec)
1230 int errors = Report.Errors;
1231 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1232 ec.CurrentBranching.CurrentUsageVector.Goto ();
1233 return errors == Report.Errors;
1236 protected override void DoEmit (EmitContext ec)
1238 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1241 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1245 protected override void CloneTo (CloneContext clonectx, Statement t)
1247 // nothing needed.
1251 public interface ILocalVariable
1253 void Emit (EmitContext ec);
1254 void EmitAssign (EmitContext ec);
1255 void EmitAddressOf (EmitContext ec);
1258 public interface IKnownVariable {
1259 Block Block { get; }
1260 Location Location { get; }
1264 // The information about a user-perceived local variable
1266 public class LocalInfo : IKnownVariable, ILocalVariable {
1267 public readonly FullNamedExpression Type;
1269 public Type VariableType;
1270 public readonly string Name;
1271 public readonly Location Location;
1272 public readonly Block Block;
1274 public VariableInfo VariableInfo;
1275 public HoistedVariable HoistedVariableReference;
1277 [Flags]
1278 enum Flags : byte {
1279 Used = 1,
1280 ReadOnly = 2,
1281 Pinned = 4,
1282 IsThis = 8,
1283 AddressTaken = 32,
1284 CompilerGenerated = 64,
1285 IsConstant = 128
1288 public enum ReadOnlyContext: byte {
1289 Using,
1290 Foreach,
1291 Fixed
1294 Flags flags;
1295 ReadOnlyContext ro_context;
1296 LocalBuilder builder;
1298 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1300 Type = type;
1301 Name = name;
1302 Block = block;
1303 Location = l;
1306 public LocalInfo (DeclSpace ds, Block block, Location l)
1308 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1309 Block = block;
1310 Location = l;
1313 public void ResolveVariable (EmitContext ec)
1315 if (HoistedVariableReference != null)
1316 return;
1318 if (builder == null) {
1319 if (Pinned)
1321 // This is needed to compile on both .NET 1.x and .NET 2.x
1322 // the later introduced `DeclareLocal (Type t, bool pinned)'
1324 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1325 else
1326 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1330 public void Emit (EmitContext ec)
1332 ec.ig.Emit (OpCodes.Ldloc, builder);
1335 public void EmitAssign (EmitContext ec)
1337 ec.ig.Emit (OpCodes.Stloc, builder);
1340 public void EmitAddressOf (EmitContext ec)
1342 ec.ig.Emit (OpCodes.Ldloca, builder);
1345 public void EmitSymbolInfo (EmitContext ec)
1347 if (builder != null)
1348 ec.DefineLocalVariable (Name, builder);
1351 public bool IsThisAssigned (EmitContext ec)
1353 if (VariableInfo == null)
1354 throw new Exception ();
1356 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1357 return true;
1359 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1362 public bool IsAssigned (EmitContext ec)
1364 if (VariableInfo == null)
1365 throw new Exception ();
1367 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1370 public bool Resolve (EmitContext ec)
1372 if (VariableType != null)
1373 return true;
1375 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1376 if (texpr == null)
1377 return false;
1379 VariableType = texpr.Type;
1381 if (TypeManager.IsGenericParameter (VariableType))
1382 return true;
1384 if (VariableType.IsAbstract && VariableType.IsSealed) {
1385 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1386 return false;
1389 if (VariableType.IsPointer && !ec.InUnsafe)
1390 Expression.UnsafeError (Location);
1392 return true;
1395 public bool IsConstant {
1396 get { return (flags & Flags.IsConstant) != 0; }
1397 set { flags |= Flags.IsConstant; }
1400 public bool AddressTaken {
1401 get { return (flags & Flags.AddressTaken) != 0; }
1402 set { flags |= Flags.AddressTaken; }
1405 public bool CompilerGenerated {
1406 get { return (flags & Flags.CompilerGenerated) != 0; }
1407 set { flags |= Flags.CompilerGenerated; }
1410 public override string ToString ()
1412 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1413 Name, Type, VariableInfo, Location);
1416 public bool Used {
1417 get { return (flags & Flags.Used) != 0; }
1418 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1421 public bool ReadOnly {
1422 get { return (flags & Flags.ReadOnly) != 0; }
1425 public void SetReadOnlyContext (ReadOnlyContext context)
1427 flags |= Flags.ReadOnly;
1428 ro_context = context;
1431 public string GetReadOnlyContext ()
1433 if (!ReadOnly)
1434 throw new InternalErrorException ("Variable is not readonly");
1436 switch (ro_context) {
1437 case ReadOnlyContext.Fixed:
1438 return "fixed variable";
1439 case ReadOnlyContext.Foreach:
1440 return "foreach iteration variable";
1441 case ReadOnlyContext.Using:
1442 return "using variable";
1444 throw new NotImplementedException ();
1448 // Whether the variable is pinned, if Pinned the variable has been
1449 // allocated in a pinned slot with DeclareLocal.
1451 public bool Pinned {
1452 get { return (flags & Flags.Pinned) != 0; }
1453 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1456 public bool IsThis {
1457 get { return (flags & Flags.IsThis) != 0; }
1458 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1461 Block IKnownVariable.Block {
1462 get { return Block; }
1465 Location IKnownVariable.Location {
1466 get { return Location; }
1469 public LocalInfo Clone (CloneContext clonectx)
1472 // Variables in anonymous block are not resolved yet
1474 if (VariableType == null)
1475 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1478 // Variables in method block are resolved
1480 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1481 li.VariableType = VariableType;
1482 return li;
1486 /// <summary>
1487 /// Block represents a C# block.
1488 /// </summary>
1490 /// <remarks>
1491 /// This class is used in a number of places: either to represent
1492 /// explicit blocks that the programmer places or implicit blocks.
1494 /// Implicit blocks are used as labels or to introduce variable
1495 /// declarations.
1497 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1498 /// they contain extra information that is not necessary on normal blocks.
1499 /// </remarks>
1500 public class Block : Statement {
1501 public Block Parent;
1502 public Location StartLocation;
1503 public Location EndLocation = Location.Null;
1505 public ExplicitBlock Explicit;
1506 public ToplevelBlock Toplevel; // TODO: Use Explicit
1508 [Flags]
1509 public enum Flags : byte {
1510 Unchecked = 1,
1511 BlockUsed = 2,
1512 VariablesInitialized = 4,
1513 HasRet = 8,
1514 Unsafe = 16,
1515 IsIterator = 32,
1516 HasCapturedVariable = 64,
1517 HasCapturedThis = 128
1519 protected Flags flags;
1521 public bool Unchecked {
1522 get { return (flags & Flags.Unchecked) != 0; }
1523 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1526 public bool Unsafe {
1527 get { return (flags & Flags.Unsafe) != 0; }
1528 set { flags |= Flags.Unsafe; }
1532 // The statements in this block
1534 protected ArrayList statements;
1537 // An array of Blocks. We keep track of children just
1538 // to generate the local variable declarations.
1540 // Statements and child statements are handled through the
1541 // statements.
1543 ArrayList children;
1546 // Labels. (label, block) pairs.
1548 protected HybridDictionary labels;
1551 // Keeps track of (name, type) pairs
1553 IDictionary variables;
1556 // Keeps track of constants
1557 HybridDictionary constants;
1560 // Temporary variables.
1562 ArrayList temporary_variables;
1565 // If this is a switch section, the enclosing switch block.
1567 Block switch_block;
1569 protected ArrayList scope_initializers;
1571 ArrayList anonymous_children;
1573 protected static int id;
1575 int this_id;
1577 int assignable_slots;
1578 bool unreachable_shown;
1579 bool unreachable;
1581 public Block (Block parent)
1582 : this (parent, (Flags) 0, Location.Null, Location.Null)
1585 public Block (Block parent, Flags flags)
1586 : this (parent, flags, Location.Null, Location.Null)
1589 public Block (Block parent, Location start, Location end)
1590 : this (parent, (Flags) 0, start, end)
1594 // Useful when TopLevel block is downgraded to normal block
1596 public Block (ToplevelBlock parent, ToplevelBlock source)
1597 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1599 statements = source.statements;
1600 children = source.children;
1601 labels = source.labels;
1602 variables = source.variables;
1603 constants = source.constants;
1604 switch_block = source.switch_block;
1607 public Block (Block parent, Flags flags, Location start, Location end)
1609 if (parent != null) {
1610 parent.AddChild (this);
1612 // the appropriate constructors will fixup these fields
1613 Toplevel = parent.Toplevel;
1614 Explicit = parent.Explicit;
1617 this.Parent = parent;
1618 this.flags = flags;
1619 this.StartLocation = start;
1620 this.EndLocation = end;
1621 this.loc = start;
1622 this_id = id++;
1623 statements = new ArrayList (4);
1626 public Block CreateSwitchBlock (Location start)
1628 // FIXME: should this be implicit?
1629 Block new_block = new ExplicitBlock (this, start, start);
1630 new_block.switch_block = this;
1631 return new_block;
1634 public int ID {
1635 get { return this_id; }
1638 public IDictionary Variables {
1639 get {
1640 if (variables == null)
1641 variables = new ListDictionary ();
1642 return variables;
1646 void AddChild (Block b)
1648 if (children == null)
1649 children = new ArrayList (1);
1651 children.Add (b);
1654 public void SetEndLocation (Location loc)
1656 EndLocation = loc;
1659 protected static void Error_158 (string name, Location loc)
1661 Report.Error (158, loc, "The label `{0}' shadows another label " +
1662 "by the same name in a contained scope", name);
1665 /// <summary>
1666 /// Adds a label to the current block.
1667 /// </summary>
1669 /// <returns>
1670 /// false if the name already exists in this block. true
1671 /// otherwise.
1672 /// </returns>
1674 public bool AddLabel (LabeledStatement target)
1676 if (switch_block != null)
1677 return switch_block.AddLabel (target);
1679 string name = target.Name;
1681 Block cur = this;
1682 while (cur != null) {
1683 LabeledStatement s = cur.DoLookupLabel (name);
1684 if (s != null) {
1685 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1686 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1687 return false;
1690 if (this == Explicit)
1691 break;
1693 cur = cur.Parent;
1696 while (cur != null) {
1697 if (cur.DoLookupLabel (name) != null) {
1698 Error_158 (name, target.loc);
1699 return false;
1702 if (children != null) {
1703 foreach (Block b in children) {
1704 LabeledStatement s = b.DoLookupLabel (name);
1705 if (s == null)
1706 continue;
1708 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1709 Error_158 (name, target.loc);
1710 return false;
1714 cur = cur.Parent;
1717 Toplevel.CheckError158 (name, target.loc);
1719 if (labels == null)
1720 labels = new HybridDictionary();
1722 labels.Add (name, target);
1723 return true;
1726 public LabeledStatement LookupLabel (string name)
1728 LabeledStatement s = DoLookupLabel (name);
1729 if (s != null)
1730 return s;
1732 if (children == null)
1733 return null;
1735 foreach (Block child in children) {
1736 if (Explicit != child.Explicit)
1737 continue;
1739 s = child.LookupLabel (name);
1740 if (s != null)
1741 return s;
1744 return null;
1747 LabeledStatement DoLookupLabel (string name)
1749 if (switch_block != null)
1750 return switch_block.LookupLabel (name);
1752 if (labels != null)
1753 if (labels.Contains (name))
1754 return ((LabeledStatement) labels [name]);
1756 return null;
1759 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1761 Block b = this;
1762 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1763 while (kvi == null) {
1764 b = b.Explicit.Parent;
1765 if (b == null)
1766 return true;
1767 kvi = b.Explicit.GetKnownVariable (name);
1770 if (kvi.Block == b)
1771 return true;
1773 // Is kvi.Block nested inside 'b'
1774 if (b.Explicit != kvi.Block.Explicit) {
1776 // If a variable by the same name it defined in a nested block of this
1777 // block, we violate the invariant meaning in a block.
1779 if (b == this) {
1780 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1781 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1782 return false;
1786 // It's ok if the definition is in a nested subblock of b, but not
1787 // nested inside this block -- a definition in a sibling block
1788 // should not affect us.
1790 return true;
1794 // Block 'b' and kvi.Block are the same textual block.
1795 // However, different variables are extant.
1797 // Check if the variable is in scope in both blocks. We use
1798 // an indirect check that depends on AddVariable doing its
1799 // part in maintaining the invariant-meaning-in-block property.
1801 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1802 return true;
1804 if (this is ToplevelBlock) {
1805 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1806 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1807 return false;
1811 // Even though we detected the error when the name is used, we
1812 // treat it as if the variable declaration was in error.
1814 Report.SymbolRelatedToPreviousError (loc, name);
1815 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1816 return false;
1819 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1821 LocalInfo vi = GetLocalInfo (name);
1822 if (vi != null) {
1823 Report.SymbolRelatedToPreviousError (vi.Location, name);
1824 if (Explicit == vi.Block.Explicit) {
1825 Error_AlreadyDeclared (l, name, null);
1826 } else {
1827 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1828 "parent or current" : "parent");
1830 return false;
1833 if (block != null) {
1834 Expression e = block.GetParameterReference (name, Location.Null);
1835 if (e != null) {
1836 ParameterReference pr = e as ParameterReference;
1837 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1838 Error_AlreadyDeclared (loc, name);
1839 else
1840 Error_AlreadyDeclared (loc, name, "parent or current");
1841 return false;
1845 return true;
1848 public LocalInfo AddVariable (Expression type, string name, Location l)
1850 if (!CheckParentConflictName (Toplevel, name, l))
1851 return null;
1853 if (Toplevel.GenericMethod != null) {
1854 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1855 if (tp.Name == name) {
1856 Report.SymbolRelatedToPreviousError (tp);
1857 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1858 return null;
1863 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1864 if (kvi != null) {
1865 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1866 Error_AlreadyDeclared (l, name, "child");
1867 return null;
1870 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1871 AddVariable (vi);
1873 if ((flags & Flags.VariablesInitialized) != 0)
1874 throw new InternalErrorException ("block has already been resolved");
1876 return vi;
1879 protected virtual void AddVariable (LocalInfo li)
1881 Variables.Add (li.Name, li);
1882 Explicit.AddKnownVariable (li.Name, li);
1885 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1887 if (reason == null) {
1888 Error_AlreadyDeclared (loc, var);
1889 return;
1892 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1893 "in this scope because it would give a different meaning " +
1894 "to `{0}', which is already used in a `{1}' scope " +
1895 "to denote something else", var, reason);
1898 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1900 Report.Error (128, loc,
1901 "A local variable named `{0}' is already defined in this scope", name);
1904 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1906 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1907 name, conflict);
1910 public bool AddConstant (Expression type, string name, Expression value, Location l)
1912 if (AddVariable (type, name, l) == null)
1913 return false;
1915 if (constants == null)
1916 constants = new HybridDictionary();
1918 constants.Add (name, value);
1920 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1921 Use ();
1922 return true;
1925 static int next_temp_id = 0;
1927 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1929 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1931 if (temporary_variables == null)
1932 temporary_variables = new ArrayList ();
1934 int id = ++next_temp_id;
1935 string name = "$s_" + id.ToString ();
1937 LocalInfo li = new LocalInfo (te, name, this, loc);
1938 li.CompilerGenerated = true;
1939 temporary_variables.Add (li);
1940 return li;
1943 public LocalInfo GetLocalInfo (string name)
1945 LocalInfo ret;
1946 for (Block b = this; b != null; b = b.Parent) {
1947 if (b.variables != null) {
1948 ret = (LocalInfo) b.variables [name];
1949 if (ret != null)
1950 return ret;
1954 return null;
1957 public Expression GetVariableType (string name)
1959 LocalInfo vi = GetLocalInfo (name);
1960 return vi == null ? null : vi.Type;
1963 public Expression GetConstantExpression (string name)
1965 for (Block b = this; b != null; b = b.Parent) {
1966 if (b.constants != null) {
1967 Expression ret = b.constants [name] as Expression;
1968 if (ret != null)
1969 return ret;
1972 return null;
1976 // It should be used by expressions which require to
1977 // register a statement during resolve process.
1979 public void AddScopeStatement (Statement s)
1981 if (scope_initializers == null)
1982 scope_initializers = new ArrayList ();
1984 scope_initializers.Add (s);
1987 public void AddStatement (Statement s)
1989 statements.Add (s);
1990 flags |= Flags.BlockUsed;
1993 public bool Used {
1994 get { return (flags & Flags.BlockUsed) != 0; }
1997 public void Use ()
1999 flags |= Flags.BlockUsed;
2002 public bool HasRet {
2003 get { return (flags & Flags.HasRet) != 0; }
2006 public int AssignableSlots {
2007 get {
2008 // TODO: Re-enable
2009 // if ((flags & Flags.VariablesInitialized) == 0)
2010 // throw new Exception ("Variables have not been initialized yet");
2011 return assignable_slots;
2015 public ArrayList AnonymousChildren {
2016 get { return anonymous_children; }
2019 public void AddAnonymousChild (ToplevelBlock b)
2021 if (anonymous_children == null)
2022 anonymous_children = new ArrayList ();
2024 anonymous_children.Add (b);
2027 void DoResolveConstants (EmitContext ec)
2029 if (constants == null)
2030 return;
2032 if (variables == null)
2033 throw new InternalErrorException ("cannot happen");
2035 foreach (DictionaryEntry de in variables) {
2036 string name = (string) de.Key;
2037 LocalInfo vi = (LocalInfo) de.Value;
2038 Type variable_type = vi.VariableType;
2040 if (variable_type == null) {
2041 if (vi.Type is VarExpr)
2042 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2044 continue;
2047 Expression cv = (Expression) constants [name];
2048 if (cv == null)
2049 continue;
2051 // Don't let 'const int Foo = Foo;' succeed.
2052 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2053 // which in turn causes the 'must be constant' error to be triggered.
2054 constants.Remove (name);
2056 if (!Const.IsConstantTypeValid (variable_type)) {
2057 Const.Error_InvalidConstantType (variable_type, loc);
2058 continue;
2061 ec.CurrentBlock = this;
2062 Expression e;
2063 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2064 e = cv.Resolve (ec);
2066 if (e == null)
2067 continue;
2069 Constant ce = e as Constant;
2070 if (ce == null) {
2071 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2072 continue;
2075 e = ce.ConvertImplicitly (variable_type);
2076 if (e == null) {
2077 if (TypeManager.IsReferenceType (variable_type))
2078 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2079 else
2080 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2081 continue;
2084 constants.Add (name, e);
2085 vi.IsConstant = true;
2089 protected void ResolveMeta (EmitContext ec, int offset)
2091 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2093 // If some parent block was unsafe, we remain unsafe even if this block
2094 // isn't explicitly marked as such.
2095 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2096 flags |= Flags.VariablesInitialized;
2098 if (variables != null) {
2099 foreach (LocalInfo li in variables.Values) {
2100 if (!li.Resolve (ec))
2101 continue;
2102 li.VariableInfo = new VariableInfo (li, offset);
2103 offset += li.VariableInfo.Length;
2106 assignable_slots = offset;
2108 DoResolveConstants (ec);
2110 if (children == null)
2111 return;
2112 foreach (Block b in children)
2113 b.ResolveMeta (ec, offset);
2118 // Emits the local variable declarations for a block
2120 public virtual void EmitMeta (EmitContext ec)
2122 if (variables != null){
2123 foreach (LocalInfo vi in variables.Values)
2124 vi.ResolveVariable (ec);
2127 if (temporary_variables != null) {
2128 for (int i = 0; i < temporary_variables.Count; i++)
2129 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2132 if (children != null) {
2133 for (int i = 0; i < children.Count; i++)
2134 ((Block)children[i]).EmitMeta(ec);
2138 void UsageWarning ()
2140 if (variables == null || Report.WarningLevel < 3)
2141 return;
2143 foreach (DictionaryEntry de in variables) {
2144 LocalInfo vi = (LocalInfo) de.Value;
2146 if (!vi.Used) {
2147 string name = (string) de.Key;
2149 // vi.VariableInfo can be null for 'catch' variables
2150 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2151 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2152 else
2153 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2158 static void CheckPossibleMistakenEmptyStatement (Statement s)
2160 Statement body;
2162 // Some statements are wrapped by a Block. Since
2163 // others' internal could be changed, here I treat
2164 // them as possibly wrapped by Block equally.
2165 Block b = s as Block;
2166 if (b != null && b.statements.Count == 1)
2167 s = (Statement) b.statements [0];
2169 if (s is Lock)
2170 body = ((Lock) s).Statement;
2171 else if (s is For)
2172 body = ((For) s).Statement;
2173 else if (s is Foreach)
2174 body = ((Foreach) s).Statement;
2175 else if (s is While)
2176 body = ((While) s).Statement;
2177 else if (s is Fixed)
2178 body = ((Fixed) s).Statement;
2179 else if (s is Using)
2180 body = ((Using) s).EmbeddedStatement;
2181 else if (s is UsingTemporary)
2182 body = ((UsingTemporary) s).Statement;
2183 else
2184 return;
2186 if (body == null || body is EmptyStatement)
2187 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2190 public override bool Resolve (EmitContext ec)
2192 Block prev_block = ec.CurrentBlock;
2193 bool ok = true;
2195 int errors = Report.Errors;
2197 ec.CurrentBlock = this;
2198 ec.StartFlowBranching (this);
2200 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2203 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2204 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2205 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2206 // responsible for handling the situation.
2208 int statement_count = statements.Count;
2209 for (int ix = 0; ix < statement_count; ix++){
2210 Statement s = (Statement) statements [ix];
2211 // Check possible empty statement (CS0642)
2212 if (Report.WarningLevel >= 3 &&
2213 ix + 1 < statement_count &&
2214 statements [ix + 1] is ExplicitBlock)
2215 CheckPossibleMistakenEmptyStatement (s);
2218 // Warn if we detect unreachable code.
2220 if (unreachable) {
2221 if (s is EmptyStatement)
2222 continue;
2224 if (!unreachable_shown && !(s is LabeledStatement)) {
2225 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2226 unreachable_shown = true;
2229 Block c_block = s as Block;
2230 if (c_block != null)
2231 c_block.unreachable = c_block.unreachable_shown = true;
2235 // Note that we're not using ResolveUnreachable() for unreachable
2236 // statements here. ResolveUnreachable() creates a temporary
2237 // flow branching and kills it afterwards. This leads to problems
2238 // if you have two unreachable statements where the first one
2239 // assigns a variable and the second one tries to access it.
2242 if (!s.Resolve (ec)) {
2243 ok = false;
2244 if (ec.IsInProbingMode)
2245 break;
2247 statements [ix] = EmptyStatement.Value;
2248 continue;
2251 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2252 statements [ix] = EmptyStatement.Value;
2254 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2255 if (unreachable && s is LabeledStatement)
2256 throw new InternalErrorException ("should not happen");
2259 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2260 ec.CurrentBranching, statement_count);
2262 while (ec.CurrentBranching is FlowBranchingLabeled)
2263 ec.EndFlowBranching ();
2265 bool flow_unreachable = ec.EndFlowBranching ();
2267 ec.CurrentBlock = prev_block;
2269 if (flow_unreachable)
2270 flags |= Flags.HasRet;
2272 // If we're a non-static `struct' constructor which doesn't have an
2273 // initializer, then we must initialize all of the struct's fields.
2274 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2275 ok = false;
2277 if ((labels != null) && (Report.WarningLevel >= 2)) {
2278 foreach (LabeledStatement label in labels.Values)
2279 if (!label.HasBeenReferenced)
2280 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2283 if (ok && errors == Report.Errors)
2284 UsageWarning ();
2286 return ok;
2289 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2291 unreachable_shown = true;
2292 unreachable = true;
2294 if (warn)
2295 Report.Warning (162, 2, loc, "Unreachable code detected");
2297 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2298 bool ok = Resolve (ec);
2299 ec.KillFlowBranching ();
2301 return ok;
2304 protected override void DoEmit (EmitContext ec)
2306 for (int ix = 0; ix < statements.Count; ix++){
2307 Statement s = (Statement) statements [ix];
2308 s.Emit (ec);
2312 public override void Emit (EmitContext ec)
2314 Block prev_block = ec.CurrentBlock;
2315 ec.CurrentBlock = this;
2317 if (scope_initializers != null)
2318 EmitScopeInitializers (ec);
2320 ec.Mark (StartLocation);
2321 DoEmit (ec);
2323 if (SymbolWriter.HasSymbolWriter)
2324 EmitSymbolInfo (ec);
2326 ec.CurrentBlock = prev_block;
2329 protected void EmitScopeInitializers (EmitContext ec)
2331 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2333 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2334 foreach (Statement s in scope_initializers)
2335 s.Emit (ec);
2338 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2341 protected virtual void EmitSymbolInfo (EmitContext ec)
2343 if (variables != null) {
2344 foreach (LocalInfo vi in variables.Values) {
2345 vi.EmitSymbolInfo (ec);
2350 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2352 MutateVariables (storey);
2354 if (scope_initializers != null) {
2355 foreach (Statement s in scope_initializers)
2356 s.MutateHoistedGenericType (storey);
2359 foreach (Statement s in statements)
2360 s.MutateHoistedGenericType (storey);
2363 void MutateVariables (AnonymousMethodStorey storey)
2365 if (variables != null) {
2366 foreach (LocalInfo vi in variables.Values) {
2367 vi.VariableType = storey.MutateType (vi.VariableType);
2371 if (temporary_variables != null) {
2372 foreach (LocalInfo vi in temporary_variables)
2373 vi.VariableType = storey.MutateType (vi.VariableType);
2377 public override string ToString ()
2379 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2382 protected override void CloneTo (CloneContext clonectx, Statement t)
2384 Block target = (Block) t;
2386 clonectx.AddBlockMap (this, target);
2388 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2389 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2390 if (Parent != null)
2391 target.Parent = clonectx.RemapBlockCopy (Parent);
2393 if (variables != null){
2394 target.variables = new Hashtable ();
2396 foreach (DictionaryEntry de in variables){
2397 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2398 target.variables [de.Key] = newlocal;
2399 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2403 target.statements = new ArrayList (statements.Count);
2404 foreach (Statement s in statements)
2405 target.statements.Add (s.Clone (clonectx));
2407 if (target.children != null){
2408 target.children = new ArrayList (children.Count);
2409 foreach (Block b in children){
2410 target.children.Add (clonectx.LookupBlock (b));
2415 // TODO: labels, switch_block, constants (?), anonymous_children
2420 public class ExplicitBlock : Block {
2421 HybridDictionary known_variables;
2422 protected AnonymousMethodStorey am_storey;
2424 public ExplicitBlock (Block parent, Location start, Location end)
2425 : this (parent, (Flags) 0, start, end)
2429 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2430 : base (parent, flags, start, end)
2432 this.Explicit = this;
2435 // <summary>
2436 // Marks a variable with name @name as being used in this or a child block.
2437 // If a variable name has been used in a child block, it's illegal to
2438 // declare a variable with the same name in the current block.
2439 // </summary>
2440 internal void AddKnownVariable (string name, IKnownVariable info)
2442 if (known_variables == null)
2443 known_variables = new HybridDictionary();
2445 known_variables [name] = info;
2447 if (Parent != null)
2448 Parent.Explicit.AddKnownVariable (name, info);
2451 public AnonymousMethodStorey AnonymousMethodStorey {
2452 get { return am_storey; }
2456 // Creates anonymous method storey in current block
2458 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2461 // When referencing a variable in iterator storey from children anonymous method
2463 if (Toplevel.am_storey is IteratorStorey) {
2464 return Toplevel.am_storey;
2468 // An iterator has only 1 storey block
2470 if (ec.CurrentIterator != null)
2471 return ec.CurrentIterator.Storey;
2473 if (am_storey == null) {
2474 MemberBase mc = ec.ResolveContext as MemberBase;
2475 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2478 // Creates anonymous method storey for this block
2480 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2483 return am_storey;
2486 public override void Emit (EmitContext ec)
2488 if (am_storey != null)
2489 am_storey.EmitStoreyInstantiation (ec);
2491 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2492 if (emit_debug_info)
2493 ec.BeginScope ();
2495 base.Emit (ec);
2497 if (emit_debug_info)
2498 ec.EndScope ();
2501 public override void EmitMeta (EmitContext ec)
2504 // Creates anonymous method storey
2506 if (am_storey != null) {
2507 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2509 // Creates parent storey reference when hoisted this is accessible
2511 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2512 ExplicitBlock parent = Toplevel.Parent.Explicit;
2515 // Hoisted this exists in top-level parent storey only
2517 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2518 parent = parent.Parent.Explicit;
2520 am_storey.AddParentStoreyReference (parent.am_storey);
2523 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2526 am_storey.DefineType ();
2527 am_storey.ResolveType ();
2528 am_storey.Define ();
2529 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2531 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2532 if (ref_blocks != null) {
2533 foreach (ExplicitBlock ref_block in ref_blocks) {
2534 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2535 if (b.am_storey != null) {
2536 b.am_storey.AddParentStoreyReference (am_storey);
2538 // Stop propagation inside same top block
2539 if (b.Toplevel == Toplevel)
2540 break;
2542 b = b.Toplevel;
2544 b.HasCapturedVariable = true;
2550 base.EmitMeta (ec);
2553 internal IKnownVariable GetKnownVariable (string name)
2555 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2558 public bool HasCapturedThis
2560 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2561 get { return (flags & Flags.HasCapturedThis) != 0; }
2564 public bool HasCapturedVariable
2566 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2567 get { return (flags & Flags.HasCapturedVariable) != 0; }
2570 protected override void CloneTo (CloneContext clonectx, Statement t)
2572 ExplicitBlock target = (ExplicitBlock) t;
2573 target.known_variables = null;
2574 base.CloneTo (clonectx, t);
2578 public class ToplevelParameterInfo : IKnownVariable {
2579 public readonly ToplevelBlock Block;
2580 public readonly int Index;
2581 public VariableInfo VariableInfo;
2583 Block IKnownVariable.Block {
2584 get { return Block; }
2586 public Parameter Parameter {
2587 get { return Block.Parameters [Index]; }
2590 public Type ParameterType {
2591 get { return Block.Parameters.Types [Index]; }
2594 public Location Location {
2595 get { return Parameter.Location; }
2598 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2600 this.Block = block;
2601 this.Index = idx;
2606 // A toplevel block contains extra information, the split is done
2607 // only to separate information that would otherwise bloat the more
2608 // lightweight Block.
2610 // In particular, this was introduced when the support for Anonymous
2611 // Methods was implemented.
2613 public class ToplevelBlock : ExplicitBlock
2616 // Block is converted to an expression
2618 sealed class BlockScopeExpression : Expression
2620 Expression child;
2621 readonly ToplevelBlock block;
2623 public BlockScopeExpression (Expression child, ToplevelBlock block)
2625 this.child = child;
2626 this.block = block;
2629 public override Expression CreateExpressionTree (EmitContext ec)
2631 throw new NotSupportedException ();
2634 public override Expression DoResolve (EmitContext ec)
2636 if (child == null)
2637 return null;
2639 block.ResolveMeta (ec, ParametersCompiled.EmptyReadOnlyParameters);
2640 child = child.Resolve (ec);
2641 if (child == null)
2642 return null;
2644 eclass = child.eclass;
2645 type = child.Type;
2646 return this;
2649 public override void Emit (EmitContext ec)
2651 block.EmitMeta (ec);
2652 block.EmitScopeInitializers (ec);
2653 child.Emit (ec);
2656 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2658 type = storey.MutateType (type);
2659 child.MutateHoistedGenericType (storey);
2660 block.MutateHoistedGenericType (storey);
2664 GenericMethod generic;
2665 FlowBranchingToplevel top_level_branching;
2666 protected ParametersCompiled parameters;
2667 ToplevelParameterInfo[] parameter_info;
2668 LocalInfo this_variable;
2670 public HoistedVariable HoistedThisVariable;
2673 // The parameters for the block.
2675 public ParametersCompiled Parameters {
2676 get { return parameters; }
2679 public GenericMethod GenericMethod {
2680 get { return generic; }
2683 public ToplevelBlock Container {
2684 get { return Parent == null ? null : Parent.Toplevel; }
2687 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2688 this (parent, (Flags) 0, parameters, start)
2692 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2693 this (parent, parameters, start)
2695 this.generic = generic;
2698 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2699 this (null, (Flags) 0, parameters, start)
2703 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2704 this (null, flags, parameters, start)
2708 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2709 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2710 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2711 base (null, flags, start, Location.Null)
2713 this.Toplevel = this;
2715 this.parameters = parameters;
2716 this.Parent = parent;
2717 if (parent != null)
2718 parent.AddAnonymousChild (this);
2720 if (!this.parameters.IsEmpty)
2721 ProcessParameters ();
2724 public ToplevelBlock (Location loc)
2725 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2729 protected override void CloneTo (CloneContext clonectx, Statement t)
2731 ToplevelBlock target = (ToplevelBlock) t;
2732 base.CloneTo (clonectx, t);
2734 if (parameters.Count != 0)
2735 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2736 for (int i = 0; i < parameters.Count; ++i)
2737 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2740 public bool CheckError158 (string name, Location loc)
2742 if (AnonymousChildren != null) {
2743 foreach (ToplevelBlock child in AnonymousChildren) {
2744 if (!child.CheckError158 (name, loc))
2745 return false;
2749 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2750 if (!c.DoCheckError158 (name, loc))
2751 return false;
2754 return true;
2757 void ProcessParameters ()
2759 int n = parameters.Count;
2760 parameter_info = new ToplevelParameterInfo [n];
2761 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2762 for (int i = 0; i < n; ++i) {
2763 parameter_info [i] = new ToplevelParameterInfo (this, i);
2765 Parameter p = parameters [i];
2766 if (p == null)
2767 continue;
2769 string name = p.Name;
2770 if (CheckParentConflictName (top_parent, name, loc))
2771 AddKnownVariable (name, parameter_info [i]);
2774 // mark this block as "used" so that we create local declarations in a sub-block
2775 // FIXME: This appears to uncover a lot of bugs
2776 //this.Use ();
2779 bool DoCheckError158 (string name, Location loc)
2781 LabeledStatement s = LookupLabel (name);
2782 if (s != null) {
2783 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2784 Error_158 (name, loc);
2785 return false;
2788 return true;
2791 public override Expression CreateExpressionTree (EmitContext ec)
2793 if (statements.Count == 1) {
2794 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2795 if (scope_initializers != null)
2796 expr = new BlockScopeExpression (expr, this);
2798 return expr;
2801 return base.CreateExpressionTree (ec);
2805 // Reformats this block to be top-level iterator block
2807 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2809 IsIterator = true;
2811 // Creates block with original statements
2812 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2814 source.statements = new ArrayList (1);
2815 source.AddStatement (new Return (iterator, iterator.Location));
2816 source.IsIterator = false;
2818 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2819 source.am_storey = iterator_storey;
2820 return iterator_storey;
2823 public FlowBranchingToplevel TopLevelBranching {
2824 get { return top_level_branching; }
2828 // Returns a parameter reference expression for the given name,
2829 // or null if there is no such parameter
2831 public Expression GetParameterReference (string name, Location loc)
2833 for (ToplevelBlock t = this; t != null; t = t.Container) {
2834 Expression expr = t.GetParameterReferenceExpression (name, loc);
2835 if (expr != null)
2836 return expr;
2839 return null;
2842 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2844 int idx = parameters.GetParameterIndexByName (name);
2845 return idx < 0 ?
2846 null : new ParameterReference (parameter_info [idx], loc);
2849 // <summary>
2850 // Returns the "this" instance variable of this block.
2851 // See AddThisVariable() for more information.
2852 // </summary>
2853 public LocalInfo ThisVariable {
2854 get { return this_variable; }
2857 // <summary>
2858 // This is used by non-static `struct' constructors which do not have an
2859 // initializer - in this case, the constructor must initialize all of the
2860 // struct's fields. To do this, we add a "this" variable and use the flow
2861 // analysis code to ensure that it's been fully initialized before control
2862 // leaves the constructor.
2863 // </summary>
2864 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2866 if (this_variable == null) {
2867 this_variable = new LocalInfo (ds, this, l);
2868 this_variable.Used = true;
2869 this_variable.IsThis = true;
2871 Variables.Add ("this", this_variable);
2874 return this_variable;
2877 public bool IsIterator {
2878 get { return (flags & Flags.IsIterator) != 0; }
2879 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2882 public bool IsThisAssigned (EmitContext ec)
2884 return this_variable == null || this_variable.IsThisAssigned (ec);
2887 public bool ResolveMeta (EmitContext ec, ParametersCompiled ip)
2889 int errors = Report.Errors;
2890 int orig_count = parameters.Count;
2892 if (top_level_branching != null)
2893 return true;
2895 if (ip != null)
2896 parameters = ip;
2898 // Assert: orig_count != parameter.Count => orig_count == 0
2899 if (orig_count != 0 && orig_count != parameters.Count)
2900 throw new InternalErrorException ("parameter information mismatch");
2902 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2904 for (int i = 0; i < orig_count; ++i) {
2905 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2907 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2908 continue;
2910 VariableInfo vi = new VariableInfo (ip, i, offset);
2911 parameter_info [i].VariableInfo = vi;
2912 offset += vi.Length;
2915 ResolveMeta (ec, offset);
2917 top_level_branching = ec.StartFlowBranching (this);
2919 return Report.Errors == errors;
2922 // <summary>
2923 // Check whether all `out' parameters have been assigned.
2924 // </summary>
2925 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2927 if (vector.IsUnreachable)
2928 return;
2930 int n = parameter_info == null ? 0 : parameter_info.Length;
2932 for (int i = 0; i < n; i++) {
2933 VariableInfo var = parameter_info [i].VariableInfo;
2935 if (var == null)
2936 continue;
2938 if (vector.IsAssigned (var, false))
2939 continue;
2941 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2942 var.Name);
2946 public override void EmitMeta (EmitContext ec)
2948 parameters.ResolveVariable ();
2950 // Avoid declaring an IL variable for this_variable since it is not accessed
2951 // from the generated IL
2952 if (this_variable != null)
2953 Variables.Remove ("this");
2954 base.EmitMeta (ec);
2957 protected override void EmitSymbolInfo (EmitContext ec)
2959 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2960 if ((ae != null) && (ae.Storey != null))
2961 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2963 base.EmitSymbolInfo (ec);
2966 public override void Emit (EmitContext ec)
2968 base.Emit (ec);
2969 ec.Mark (EndLocation);
2973 public class SwitchLabel {
2974 Expression label;
2975 object converted;
2976 Location loc;
2978 Label il_label;
2979 bool il_label_set;
2980 Label il_label_code;
2981 bool il_label_code_set;
2983 public static readonly object NullStringCase = new object ();
2986 // if expr == null, then it is the default case.
2988 public SwitchLabel (Expression expr, Location l)
2990 label = expr;
2991 loc = l;
2994 public Expression Label {
2995 get {
2996 return label;
3000 public Location Location {
3001 get { return loc; }
3004 public object Converted {
3005 get {
3006 return converted;
3010 public Label GetILLabel (EmitContext ec)
3012 if (!il_label_set){
3013 il_label = ec.ig.DefineLabel ();
3014 il_label_set = true;
3016 return il_label;
3019 public Label GetILLabelCode (EmitContext ec)
3021 if (!il_label_code_set){
3022 il_label_code = ec.ig.DefineLabel ();
3023 il_label_code_set = true;
3025 return il_label_code;
3029 // Resolves the expression, reduces it to a literal if possible
3030 // and then converts it to the requested type.
3032 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
3034 Expression e = label.Resolve (ec);
3036 if (e == null)
3037 return false;
3039 Constant c = e as Constant;
3040 if (c == null){
3041 Report.Error (150, loc, "A constant value is expected");
3042 return false;
3045 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3046 converted = NullStringCase;
3047 return true;
3050 if (allow_nullable && c.GetValue () == null) {
3051 converted = NullStringCase;
3052 return true;
3055 c = c.ImplicitConversionRequired (ec, required_type, loc);
3056 if (c == null)
3057 return false;
3059 converted = c.GetValue ();
3060 return true;
3063 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3065 string label;
3066 if (converted == null)
3067 label = "default";
3068 else if (converted == NullStringCase)
3069 label = "null";
3070 else
3071 label = converted.ToString ();
3073 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3074 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3077 public SwitchLabel Clone (CloneContext clonectx)
3079 return new SwitchLabel (label.Clone (clonectx), loc);
3083 public class SwitchSection {
3084 // An array of SwitchLabels.
3085 public readonly ArrayList Labels;
3086 public readonly Block Block;
3088 public SwitchSection (ArrayList labels, Block block)
3090 Labels = labels;
3091 Block = block;
3094 public SwitchSection Clone (CloneContext clonectx)
3096 ArrayList cloned_labels = new ArrayList ();
3098 foreach (SwitchLabel sl in cloned_labels)
3099 cloned_labels.Add (sl.Clone (clonectx));
3101 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3105 public class Switch : Statement {
3106 public ArrayList Sections;
3107 public Expression Expr;
3109 /// <summary>
3110 /// Maps constants whose type type SwitchType to their SwitchLabels.
3111 /// </summary>
3112 public IDictionary Elements;
3114 /// <summary>
3115 /// The governing switch type
3116 /// </summary>
3117 public Type SwitchType;
3120 // Computed
3122 Label default_target;
3123 Label null_target;
3124 Expression new_expr;
3125 bool is_constant;
3126 bool has_null_case;
3127 SwitchSection constant_section;
3128 SwitchSection default_section;
3130 ExpressionStatement string_dictionary;
3131 FieldExpr switch_cache_field;
3132 static int unique_counter;
3135 // Nullable Types support
3137 Nullable.Unwrap unwrap;
3139 protected bool HaveUnwrap {
3140 get { return unwrap != null; }
3144 // The types allowed to be implicitly cast from
3145 // on the governing type
3147 static Type [] allowed_types;
3149 public Switch (Expression e, ArrayList sects, Location l)
3151 Expr = e;
3152 Sections = sects;
3153 loc = l;
3156 public bool GotDefault {
3157 get {
3158 return default_section != null;
3162 public Label DefaultTarget {
3163 get {
3164 return default_target;
3169 // Determines the governing type for a switch. The returned
3170 // expression might be the expression from the switch, or an
3171 // expression that includes any potential conversions to the
3172 // integral types or to string.
3174 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3176 Type t = expr.Type;
3178 if (t == TypeManager.byte_type ||
3179 t == TypeManager.sbyte_type ||
3180 t == TypeManager.ushort_type ||
3181 t == TypeManager.short_type ||
3182 t == TypeManager.uint32_type ||
3183 t == TypeManager.int32_type ||
3184 t == TypeManager.uint64_type ||
3185 t == TypeManager.int64_type ||
3186 t == TypeManager.char_type ||
3187 t == TypeManager.string_type ||
3188 t == TypeManager.bool_type ||
3189 TypeManager.IsEnumType (t))
3190 return expr;
3192 if (allowed_types == null){
3193 allowed_types = new Type [] {
3194 TypeManager.sbyte_type,
3195 TypeManager.byte_type,
3196 TypeManager.short_type,
3197 TypeManager.ushort_type,
3198 TypeManager.int32_type,
3199 TypeManager.uint32_type,
3200 TypeManager.int64_type,
3201 TypeManager.uint64_type,
3202 TypeManager.char_type,
3203 TypeManager.string_type
3208 // Try to find a *user* defined implicit conversion.
3210 // If there is no implicit conversion, or if there are multiple
3211 // conversions, we have to report an error
3213 Expression converted = null;
3214 foreach (Type tt in allowed_types){
3215 Expression e;
3217 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3218 if (e == null)
3219 continue;
3222 // Ignore over-worked ImplicitUserConversions that do
3223 // an implicit conversion in addition to the user conversion.
3225 if (!(e is UserCast))
3226 continue;
3228 if (converted != null){
3229 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3230 return null;
3233 converted = e;
3235 return converted;
3239 // Performs the basic sanity checks on the switch statement
3240 // (looks for duplicate keys and non-constant expressions).
3242 // It also returns a hashtable with the keys that we will later
3243 // use to compute the switch tables
3245 bool CheckSwitch (EmitContext ec)
3247 bool error = false;
3248 Elements = Sections.Count > 10 ?
3249 (IDictionary)new Hashtable () :
3250 (IDictionary)new ListDictionary ();
3252 foreach (SwitchSection ss in Sections){
3253 foreach (SwitchLabel sl in ss.Labels){
3254 if (sl.Label == null){
3255 if (default_section != null){
3256 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3257 error = true;
3259 default_section = ss;
3260 continue;
3263 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3264 error = true;
3265 continue;
3268 object key = sl.Converted;
3269 if (key == SwitchLabel.NullStringCase)
3270 has_null_case = true;
3272 try {
3273 Elements.Add (key, sl);
3274 } catch (ArgumentException) {
3275 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3276 error = true;
3280 return !error;
3283 void EmitObjectInteger (ILGenerator ig, object k)
3285 if (k is int)
3286 IntConstant.EmitInt (ig, (int) k);
3287 else if (k is Constant) {
3288 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3290 else if (k is uint)
3291 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3292 else if (k is long)
3294 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3296 IntConstant.EmitInt (ig, (int) (long) k);
3297 ig.Emit (OpCodes.Conv_I8);
3299 else
3300 LongConstant.EmitLong (ig, (long) k);
3302 else if (k is ulong)
3304 ulong ul = (ulong) k;
3305 if (ul < (1L<<32))
3307 IntConstant.EmitInt (ig, unchecked ((int) ul));
3308 ig.Emit (OpCodes.Conv_U8);
3310 else
3312 LongConstant.EmitLong (ig, unchecked ((long) ul));
3315 else if (k is char)
3316 IntConstant.EmitInt (ig, (int) ((char) k));
3317 else if (k is sbyte)
3318 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3319 else if (k is byte)
3320 IntConstant.EmitInt (ig, (int) ((byte) k));
3321 else if (k is short)
3322 IntConstant.EmitInt (ig, (int) ((short) k));
3323 else if (k is ushort)
3324 IntConstant.EmitInt (ig, (int) ((ushort) k));
3325 else if (k is bool)
3326 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3327 else
3328 throw new Exception ("Unhandled case");
3331 // structure used to hold blocks of keys while calculating table switch
3332 class KeyBlock : IComparable
3334 public KeyBlock (long _first)
3336 first = last = _first;
3338 public long first;
3339 public long last;
3340 public ArrayList element_keys = null;
3341 // how many items are in the bucket
3342 public int Size = 1;
3343 public int Length
3345 get { return (int) (last - first + 1); }
3347 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3349 return kb_last.last - kb_first.first + 1;
3351 public int CompareTo (object obj)
3353 KeyBlock kb = (KeyBlock) obj;
3354 int nLength = Length;
3355 int nLengthOther = kb.Length;
3356 if (nLengthOther == nLength)
3357 return (int) (kb.first - first);
3358 return nLength - nLengthOther;
3362 /// <summary>
3363 /// This method emits code for a lookup-based switch statement (non-string)
3364 /// Basically it groups the cases into blocks that are at least half full,
3365 /// and then spits out individual lookup opcodes for each block.
3366 /// It emits the longest blocks first, and short blocks are just
3367 /// handled with direct compares.
3368 /// </summary>
3369 /// <param name="ec"></param>
3370 /// <param name="val"></param>
3371 /// <returns></returns>
3372 void TableSwitchEmit (EmitContext ec, Expression val)
3374 int element_count = Elements.Count;
3375 object [] element_keys = new object [element_count];
3376 Elements.Keys.CopyTo (element_keys, 0);
3377 Array.Sort (element_keys);
3379 // initialize the block list with one element per key
3380 ArrayList key_blocks = new ArrayList (element_count);
3381 foreach (object key in element_keys)
3382 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3384 KeyBlock current_kb;
3385 // iteratively merge the blocks while they are at least half full
3386 // there's probably a really cool way to do this with a tree...
3387 while (key_blocks.Count > 1)
3389 ArrayList key_blocks_new = new ArrayList ();
3390 current_kb = (KeyBlock) key_blocks [0];
3391 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3393 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3394 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3396 // merge blocks
3397 current_kb.last = kb.last;
3398 current_kb.Size += kb.Size;
3400 else
3402 // start a new block
3403 key_blocks_new.Add (current_kb);
3404 current_kb = kb;
3407 key_blocks_new.Add (current_kb);
3408 if (key_blocks.Count == key_blocks_new.Count)
3409 break;
3410 key_blocks = key_blocks_new;
3413 // initialize the key lists
3414 foreach (KeyBlock kb in key_blocks)
3415 kb.element_keys = new ArrayList ();
3417 // fill the key lists
3418 int iBlockCurr = 0;
3419 if (key_blocks.Count > 0) {
3420 current_kb = (KeyBlock) key_blocks [0];
3421 foreach (object key in element_keys)
3423 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3424 System.Convert.ToInt64 (key) > current_kb.last;
3425 if (next_block)
3426 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3427 current_kb.element_keys.Add (key);
3431 // sort the blocks so we can tackle the largest ones first
3432 key_blocks.Sort ();
3434 // okay now we can start...
3435 ILGenerator ig = ec.ig;
3436 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3437 Label lbl_default = default_target;
3439 Type type_keys = null;
3440 if (element_keys.Length > 0)
3441 type_keys = element_keys [0].GetType (); // used for conversions
3443 Type compare_type;
3445 if (TypeManager.IsEnumType (SwitchType))
3446 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3447 else
3448 compare_type = SwitchType;
3450 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3452 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3453 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3454 if (kb.Length <= 2)
3456 foreach (object key in kb.element_keys) {
3457 SwitchLabel sl = (SwitchLabel) Elements [key];
3458 if (key is int && (int) key == 0) {
3459 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3460 } else {
3461 val.Emit (ec);
3462 EmitObjectInteger (ig, key);
3463 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3467 else
3469 // TODO: if all the keys in the block are the same and there are
3470 // no gaps/defaults then just use a range-check.
3471 if (compare_type == TypeManager.int64_type ||
3472 compare_type == TypeManager.uint64_type)
3474 // TODO: optimize constant/I4 cases
3476 // check block range (could be > 2^31)
3477 val.Emit (ec);
3478 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3479 ig.Emit (OpCodes.Blt, lbl_default);
3480 val.Emit (ec);
3481 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3482 ig.Emit (OpCodes.Bgt, lbl_default);
3484 // normalize range
3485 val.Emit (ec);
3486 if (kb.first != 0)
3488 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3489 ig.Emit (OpCodes.Sub);
3491 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3493 else
3495 // normalize range
3496 val.Emit (ec);
3497 int first = (int) kb.first;
3498 if (first > 0)
3500 IntConstant.EmitInt (ig, first);
3501 ig.Emit (OpCodes.Sub);
3503 else if (first < 0)
3505 IntConstant.EmitInt (ig, -first);
3506 ig.Emit (OpCodes.Add);
3510 // first, build the list of labels for the switch
3511 int iKey = 0;
3512 int cJumps = kb.Length;
3513 Label [] switch_labels = new Label [cJumps];
3514 for (int iJump = 0; iJump < cJumps; iJump++)
3516 object key = kb.element_keys [iKey];
3517 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3519 SwitchLabel sl = (SwitchLabel) Elements [key];
3520 switch_labels [iJump] = sl.GetILLabel (ec);
3521 iKey++;
3523 else
3524 switch_labels [iJump] = lbl_default;
3526 // emit the switch opcode
3527 ig.Emit (OpCodes.Switch, switch_labels);
3530 // mark the default for this block
3531 if (iBlock != 0)
3532 ig.MarkLabel (lbl_default);
3535 // TODO: find the default case and emit it here,
3536 // to prevent having to do the following jump.
3537 // make sure to mark other labels in the default section
3539 // the last default just goes to the end
3540 if (element_keys.Length > 0)
3541 ig.Emit (OpCodes.Br, lbl_default);
3543 // now emit the code for the sections
3544 bool found_default = false;
3546 foreach (SwitchSection ss in Sections) {
3547 foreach (SwitchLabel sl in ss.Labels) {
3548 if (sl.Converted == SwitchLabel.NullStringCase) {
3549 ig.MarkLabel (null_target);
3550 } else if (sl.Label == null) {
3551 ig.MarkLabel (lbl_default);
3552 found_default = true;
3553 if (!has_null_case)
3554 ig.MarkLabel (null_target);
3556 ig.MarkLabel (sl.GetILLabel (ec));
3557 ig.MarkLabel (sl.GetILLabelCode (ec));
3559 ss.Block.Emit (ec);
3562 if (!found_default) {
3563 ig.MarkLabel (lbl_default);
3564 if (!has_null_case) {
3565 ig.MarkLabel (null_target);
3569 ig.MarkLabel (lbl_end);
3572 SwitchSection FindSection (SwitchLabel label)
3574 foreach (SwitchSection ss in Sections){
3575 foreach (SwitchLabel sl in ss.Labels){
3576 if (label == sl)
3577 return ss;
3581 return null;
3584 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3586 foreach (SwitchSection ss in Sections)
3587 ss.Block.MutateHoistedGenericType (storey);
3590 public static void Reset ()
3592 unique_counter = 0;
3593 allowed_types = null;
3596 public override bool Resolve (EmitContext ec)
3598 Expr = Expr.Resolve (ec);
3599 if (Expr == null)
3600 return false;
3602 new_expr = SwitchGoverningType (ec, Expr);
3604 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3605 unwrap = Nullable.Unwrap.Create (Expr, false);
3606 if (unwrap == null)
3607 return false;
3609 new_expr = SwitchGoverningType (ec, unwrap);
3612 if (new_expr == null){
3613 Report.Error (151, loc,
3614 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3615 TypeManager.CSharpName (Expr.Type));
3616 return false;
3619 // Validate switch.
3620 SwitchType = new_expr.Type;
3622 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3623 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3624 return false;
3627 if (!CheckSwitch (ec))
3628 return false;
3630 if (HaveUnwrap)
3631 Elements.Remove (SwitchLabel.NullStringCase);
3633 Switch old_switch = ec.Switch;
3634 ec.Switch = this;
3635 ec.Switch.SwitchType = SwitchType;
3637 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3638 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3640 is_constant = new_expr is Constant;
3641 if (is_constant) {
3642 object key = ((Constant) new_expr).GetValue ();
3643 SwitchLabel label = (SwitchLabel) Elements [key];
3645 constant_section = FindSection (label);
3646 if (constant_section == null)
3647 constant_section = default_section;
3650 bool first = true;
3651 bool ok = true;
3652 foreach (SwitchSection ss in Sections){
3653 if (!first)
3654 ec.CurrentBranching.CreateSibling (
3655 null, FlowBranching.SiblingType.SwitchSection);
3656 else
3657 first = false;
3659 if (is_constant && (ss != constant_section)) {
3660 // If we're a constant switch, we're only emitting
3661 // one single section - mark all the others as
3662 // unreachable.
3663 ec.CurrentBranching.CurrentUsageVector.Goto ();
3664 if (!ss.Block.ResolveUnreachable (ec, true)) {
3665 ok = false;
3667 } else {
3668 if (!ss.Block.Resolve (ec))
3669 ok = false;
3673 if (default_section == null)
3674 ec.CurrentBranching.CreateSibling (
3675 null, FlowBranching.SiblingType.SwitchSection);
3677 ec.EndFlowBranching ();
3678 ec.Switch = old_switch;
3680 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3682 if (!ok)
3683 return false;
3685 if (SwitchType == TypeManager.string_type && !is_constant) {
3686 // TODO: Optimize single case, and single+default case
3687 ResolveStringSwitchMap (ec);
3690 return true;
3693 void ResolveStringSwitchMap (EmitContext ec)
3695 FullNamedExpression string_dictionary_type;
3696 if (TypeManager.generic_ienumerable_type != null) {
3697 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3698 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3700 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3701 new TypeArguments (
3702 new TypeExpression (TypeManager.string_type, loc),
3703 new TypeExpression (TypeManager.int32_type, loc)), loc);
3704 } else {
3705 MemberAccess system_collections_generic = new MemberAccess (
3706 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3708 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3711 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3712 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3713 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3714 if (!field.Define ())
3715 return;
3716 ec.TypeContainer.PartialContainer.AddField (field);
3718 ArrayList init = new ArrayList ();
3719 int counter = 0;
3720 Elements.Clear ();
3721 string value = null;
3722 foreach (SwitchSection section in Sections) {
3723 int last_count = init.Count;
3724 foreach (SwitchLabel sl in section.Labels) {
3725 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3726 continue;
3728 value = (string) sl.Converted;
3729 ArrayList init_args = new ArrayList (2);
3730 init_args.Add (new StringLiteral (value, sl.Location));
3731 init_args.Add (new IntConstant (counter, loc));
3732 init.Add (new CollectionElementInitializer (init_args, loc));
3736 // Don't add empty sections
3738 if (last_count == init.Count)
3739 continue;
3741 Elements.Add (counter, section.Labels [0]);
3742 ++counter;
3745 Arguments args = new Arguments (1);
3746 args.Add (new Argument (new IntConstant (init.Count, loc)));
3747 Expression initializer = new NewInitialize (string_dictionary_type, args,
3748 new CollectionOrObjectInitializers (init, loc), loc);
3750 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3751 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3754 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3756 ILGenerator ig = ec.ig;
3757 Label l_initialized = ig.DefineLabel ();
3760 // Skip initialization when value is null
3762 value.EmitBranchable (ec, null_target, false);
3765 // Check if string dictionary is initialized and initialize
3767 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3768 string_dictionary.EmitStatement (ec);
3769 ig.MarkLabel (l_initialized);
3771 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3773 if (TypeManager.generic_ienumerable_type != null) {
3774 Arguments get_value_args = new Arguments (2);
3775 get_value_args.Add (new Argument (value));
3776 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3777 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3778 if (get_item == null)
3779 return;
3782 // A value was not found, go to default case
3784 get_item.EmitBranchable (ec, default_target, false);
3785 } else {
3786 Arguments get_value_args = new Arguments (1);
3787 get_value_args.Add (new Argument (value));
3789 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3790 if (get_item == null)
3791 return;
3793 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3794 get_item_object.EmitAssign (ec, get_item, true, false);
3795 ec.ig.Emit (OpCodes.Brfalse, default_target);
3797 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3798 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3800 get_item_int.EmitStatement (ec);
3801 get_item_object.Release (ec);
3804 TableSwitchEmit (ec, string_switch_variable);
3805 string_switch_variable.Release (ec);
3808 protected override void DoEmit (EmitContext ec)
3810 ILGenerator ig = ec.ig;
3812 default_target = ig.DefineLabel ();
3813 null_target = ig.DefineLabel ();
3815 // Store variable for comparission purposes
3816 // TODO: Don't duplicate non-captured VariableReference
3817 LocalTemporary value;
3818 if (HaveUnwrap) {
3819 value = new LocalTemporary (SwitchType);
3820 unwrap.EmitCheck (ec);
3821 ig.Emit (OpCodes.Brfalse, null_target);
3822 new_expr.Emit (ec);
3823 value.Store (ec);
3824 } else if (!is_constant) {
3825 value = new LocalTemporary (SwitchType);
3826 new_expr.Emit (ec);
3827 value.Store (ec);
3828 } else
3829 value = null;
3832 // Setup the codegen context
3834 Label old_end = ec.LoopEnd;
3835 Switch old_switch = ec.Switch;
3837 ec.LoopEnd = ig.DefineLabel ();
3838 ec.Switch = this;
3840 // Emit Code.
3841 if (is_constant) {
3842 if (constant_section != null)
3843 constant_section.Block.Emit (ec);
3844 } else if (string_dictionary != null) {
3845 DoEmitStringSwitch (value, ec);
3846 } else {
3847 TableSwitchEmit (ec, value);
3850 if (value != null)
3851 value.Release (ec);
3853 // Restore context state.
3854 ig.MarkLabel (ec.LoopEnd);
3857 // Restore the previous context
3859 ec.LoopEnd = old_end;
3860 ec.Switch = old_switch;
3863 protected override void CloneTo (CloneContext clonectx, Statement t)
3865 Switch target = (Switch) t;
3867 target.Expr = Expr.Clone (clonectx);
3868 target.Sections = new ArrayList ();
3869 foreach (SwitchSection ss in Sections){
3870 target.Sections.Add (ss.Clone (clonectx));
3875 // A place where execution can restart in an iterator
3876 public abstract class ResumableStatement : Statement
3878 bool prepared;
3879 protected Label resume_point;
3881 public Label PrepareForEmit (EmitContext ec)
3883 if (!prepared) {
3884 prepared = true;
3885 resume_point = ec.ig.DefineLabel ();
3887 return resume_point;
3890 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3892 return end;
3894 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3899 // Base class for statements that are implemented in terms of try...finally
3900 public abstract class ExceptionStatement : ResumableStatement
3902 bool code_follows;
3904 protected abstract void EmitPreTryBody (EmitContext ec);
3905 protected abstract void EmitTryBody (EmitContext ec);
3906 protected abstract void EmitFinallyBody (EmitContext ec);
3908 protected sealed override void DoEmit (EmitContext ec)
3910 ILGenerator ig = ec.ig;
3912 EmitPreTryBody (ec);
3914 if (resume_points != null) {
3915 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3916 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3919 ig.BeginExceptionBlock ();
3921 if (resume_points != null) {
3922 ig.MarkLabel (resume_point);
3924 // For normal control flow, we want to fall-through the Switch
3925 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3926 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3927 IntConstant.EmitInt (ig, first_resume_pc);
3928 ig.Emit (OpCodes.Sub);
3930 Label [] labels = new Label [resume_points.Count];
3931 for (int i = 0; i < resume_points.Count; ++i)
3932 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3933 ig.Emit (OpCodes.Switch, labels);
3936 EmitTryBody (ec);
3938 ig.BeginFinallyBlock ();
3940 Label start_finally = ec.ig.DefineLabel ();
3941 if (resume_points != null) {
3942 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3943 ig.Emit (OpCodes.Brfalse_S, start_finally);
3944 ig.Emit (OpCodes.Endfinally);
3947 ig.MarkLabel (start_finally);
3948 EmitFinallyBody (ec);
3950 ig.EndExceptionBlock ();
3953 public void SomeCodeFollows ()
3955 code_follows = true;
3958 protected void ResolveReachability (EmitContext ec)
3960 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3961 // So, ensure there's some IL code after this statement.
3962 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3963 ec.NeedReturnLabel ();
3967 ArrayList resume_points;
3968 int first_resume_pc;
3969 public void AddResumePoint (ResumableStatement stmt, int pc)
3971 if (resume_points == null) {
3972 resume_points = new ArrayList ();
3973 first_resume_pc = pc;
3976 if (pc != first_resume_pc + resume_points.Count)
3977 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3979 resume_points.Add (stmt);
3982 Label dispose_try_block;
3983 bool prepared_for_dispose, emitted_dispose;
3984 public override Label PrepareForDispose (EmitContext ec, Label end)
3986 if (!prepared_for_dispose) {
3987 prepared_for_dispose = true;
3988 dispose_try_block = ec.ig.DefineLabel ();
3990 return dispose_try_block;
3993 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3995 if (emitted_dispose)
3996 return;
3998 emitted_dispose = true;
4000 ILGenerator ig = ec.ig;
4002 Label end_of_try = ig.DefineLabel ();
4004 // Ensure that the only way we can get into this code is through a dispatcher
4005 if (have_dispatcher)
4006 ig.Emit (OpCodes.Br, end);
4008 ig.BeginExceptionBlock ();
4010 ig.MarkLabel (dispose_try_block);
4012 Label [] labels = null;
4013 for (int i = 0; i < resume_points.Count; ++i) {
4014 ResumableStatement s = (ResumableStatement) resume_points [i];
4015 Label ret = s.PrepareForDispose (ec, end_of_try);
4016 if (ret.Equals (end_of_try) && labels == null)
4017 continue;
4018 if (labels == null) {
4019 labels = new Label [resume_points.Count];
4020 for (int j = 0; j < i; ++j)
4021 labels [j] = end_of_try;
4023 labels [i] = ret;
4026 if (labels != null) {
4027 int j;
4028 for (j = 1; j < labels.Length; ++j)
4029 if (!labels [0].Equals (labels [j]))
4030 break;
4031 bool emit_dispatcher = j < labels.Length;
4033 if (emit_dispatcher) {
4034 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4035 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4036 IntConstant.EmitInt (ig, first_resume_pc);
4037 ig.Emit (OpCodes.Sub);
4038 ig.Emit (OpCodes.Switch, labels);
4039 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4042 foreach (ResumableStatement s in resume_points)
4043 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4046 ig.MarkLabel (end_of_try);
4048 ig.BeginFinallyBlock ();
4050 EmitFinallyBody (ec);
4052 ig.EndExceptionBlock ();
4056 public class Lock : ExceptionStatement {
4057 Expression expr;
4058 public Statement Statement;
4059 TemporaryVariable temp;
4061 public Lock (Expression expr, Statement stmt, Location l)
4063 this.expr = expr;
4064 Statement = stmt;
4065 loc = l;
4068 public override bool Resolve (EmitContext ec)
4070 expr = expr.Resolve (ec);
4071 if (expr == null)
4072 return false;
4074 if (!TypeManager.IsReferenceType (expr.Type)){
4075 Report.Error (185, loc,
4076 "`{0}' is not a reference type as required by the lock statement",
4077 TypeManager.CSharpName (expr.Type));
4078 return false;
4081 ec.StartFlowBranching (this);
4082 bool ok = Statement.Resolve (ec);
4083 ec.EndFlowBranching ();
4085 ResolveReachability (ec);
4087 // Avoid creating libraries that reference the internal
4088 // mcs NullType:
4089 Type t = expr.Type;
4090 if (t == TypeManager.null_type)
4091 t = TypeManager.object_type;
4093 temp = new TemporaryVariable (t, loc);
4094 temp.Resolve (ec);
4096 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4097 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4098 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4099 monitor_type, "Enter", loc, TypeManager.object_type);
4100 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4101 monitor_type, "Exit", loc, TypeManager.object_type);
4104 return ok;
4107 protected override void EmitPreTryBody (EmitContext ec)
4109 ILGenerator ig = ec.ig;
4111 temp.EmitAssign (ec, expr);
4112 temp.Emit (ec);
4113 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4116 protected override void EmitTryBody (EmitContext ec)
4118 Statement.Emit (ec);
4121 protected override void EmitFinallyBody (EmitContext ec)
4123 temp.Emit (ec);
4124 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4127 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4129 expr.MutateHoistedGenericType (storey);
4130 temp.MutateHoistedGenericType (storey);
4131 Statement.MutateHoistedGenericType (storey);
4134 protected override void CloneTo (CloneContext clonectx, Statement t)
4136 Lock target = (Lock) t;
4138 target.expr = expr.Clone (clonectx);
4139 target.Statement = Statement.Clone (clonectx);
4143 public class Unchecked : Statement {
4144 public Block Block;
4146 public Unchecked (Block b)
4148 Block = b;
4149 b.Unchecked = true;
4152 public override bool Resolve (EmitContext ec)
4154 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4155 return Block.Resolve (ec);
4158 protected override void DoEmit (EmitContext ec)
4160 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4161 Block.Emit (ec);
4164 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4166 Block.MutateHoistedGenericType (storey);
4169 protected override void CloneTo (CloneContext clonectx, Statement t)
4171 Unchecked target = (Unchecked) t;
4173 target.Block = clonectx.LookupBlock (Block);
4177 public class Checked : Statement {
4178 public Block Block;
4180 public Checked (Block b)
4182 Block = b;
4183 b.Unchecked = false;
4186 public override bool Resolve (EmitContext ec)
4188 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4189 return Block.Resolve (ec);
4192 protected override void DoEmit (EmitContext ec)
4194 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4195 Block.Emit (ec);
4198 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4200 Block.MutateHoistedGenericType (storey);
4203 protected override void CloneTo (CloneContext clonectx, Statement t)
4205 Checked target = (Checked) t;
4207 target.Block = clonectx.LookupBlock (Block);
4211 public class Unsafe : Statement {
4212 public Block Block;
4214 public Unsafe (Block b)
4216 Block = b;
4217 Block.Unsafe = true;
4220 public override bool Resolve (EmitContext ec)
4222 using (ec.With (EmitContext.Flags.InUnsafe, true))
4223 return Block.Resolve (ec);
4226 protected override void DoEmit (EmitContext ec)
4228 using (ec.With (EmitContext.Flags.InUnsafe, true))
4229 Block.Emit (ec);
4232 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4234 Block.MutateHoistedGenericType (storey);
4237 protected override void CloneTo (CloneContext clonectx, Statement t)
4239 Unsafe target = (Unsafe) t;
4241 target.Block = clonectx.LookupBlock (Block);
4246 // Fixed statement
4248 public class Fixed : Statement {
4249 Expression type;
4250 ArrayList declarators;
4251 Statement statement;
4252 Type expr_type;
4253 Emitter[] data;
4254 bool has_ret;
4256 abstract class Emitter
4258 protected LocalInfo vi;
4259 protected Expression converted;
4261 protected Emitter (Expression expr, LocalInfo li)
4263 converted = expr;
4264 vi = li;
4267 public abstract void Emit (EmitContext ec);
4268 public abstract void EmitExit (EmitContext ec);
4271 class ExpressionEmitter : Emitter {
4272 public ExpressionEmitter (Expression converted, LocalInfo li) :
4273 base (converted, li)
4277 public override void Emit (EmitContext ec) {
4279 // Store pointer in pinned location
4281 converted.Emit (ec);
4282 vi.EmitAssign (ec);
4285 public override void EmitExit (EmitContext ec)
4287 ec.ig.Emit (OpCodes.Ldc_I4_0);
4288 ec.ig.Emit (OpCodes.Conv_U);
4289 vi.EmitAssign (ec);
4293 class StringEmitter : Emitter
4295 LocalInfo pinned_string;
4297 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4298 base (expr, li)
4300 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4301 pinned_string.Pinned = true;
4304 public override void Emit (EmitContext ec)
4306 pinned_string.Resolve (ec);
4307 pinned_string.ResolveVariable (ec);
4309 converted.Emit (ec);
4310 pinned_string.EmitAssign (ec);
4312 PropertyInfo p = TypeManager.int_get_offset_to_string_data;
4313 if (p == null) {
4314 // TODO: Move to resolve
4315 p = TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4316 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4318 if (p == null)
4319 return;
4322 // TODO: Should use Binary::Add
4323 pinned_string.Emit (ec);
4324 ec.ig.Emit (OpCodes.Conv_I);
4326 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, p, pinned_string.Location);
4327 //pe.InstanceExpression = pinned_string;
4328 pe.Resolve (ec).Emit (ec);
4330 ec.ig.Emit (OpCodes.Add);
4331 vi.EmitAssign (ec);
4334 public override void EmitExit (EmitContext ec)
4336 ec.ig.Emit (OpCodes.Ldnull);
4337 pinned_string.EmitAssign (ec);
4341 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4343 this.type = type;
4344 declarators = decls;
4345 statement = stmt;
4346 loc = l;
4349 public Statement Statement {
4350 get { return statement; }
4353 public override bool Resolve (EmitContext ec)
4355 if (!ec.InUnsafe){
4356 Expression.UnsafeError (loc);
4357 return false;
4360 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4361 if (texpr == null) {
4362 if (type is VarExpr)
4363 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4365 return false;
4368 expr_type = texpr.Type;
4370 data = new Emitter [declarators.Count];
4372 if (!expr_type.IsPointer){
4373 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4374 return false;
4377 int i = 0;
4378 foreach (Pair p in declarators){
4379 LocalInfo vi = (LocalInfo) p.First;
4380 Expression e = (Expression) p.Second;
4382 vi.VariableInfo.SetAssigned (ec);
4383 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4386 // The rules for the possible declarators are pretty wise,
4387 // but the production on the grammar is more concise.
4389 // So we have to enforce these rules here.
4391 // We do not resolve before doing the case 1 test,
4392 // because the grammar is explicit in that the token &
4393 // is present, so we need to test for this particular case.
4396 if (e is Cast){
4397 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4398 return false;
4401 ec.InFixedInitializer = true;
4402 e = e.Resolve (ec);
4403 ec.InFixedInitializer = false;
4404 if (e == null)
4405 return false;
4408 // Case 2: Array
4410 if (e.Type.IsArray){
4411 Type array_type = TypeManager.GetElementType (e.Type);
4414 // Provided that array_type is unmanaged,
4416 if (!TypeManager.VerifyUnManaged (array_type, loc))
4417 return false;
4420 // and T* is implicitly convertible to the
4421 // pointer type given in the fixed statement.
4423 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4425 Expression converted = Convert.ImplicitConversionRequired (
4426 ec, array_ptr, vi.VariableType, loc);
4427 if (converted == null)
4428 return false;
4431 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4433 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4434 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4435 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4436 new NullPointer (loc),
4437 converted);
4439 converted = converted.Resolve (ec);
4441 data [i] = new ExpressionEmitter (converted, vi);
4442 i++;
4444 continue;
4448 // Case 3: string
4450 if (e.Type == TypeManager.string_type){
4451 data [i] = new StringEmitter (e, vi, loc);
4452 i++;
4453 continue;
4456 // Case 4: fixed buffer
4457 if (e is FixedBufferPtr) {
4458 data [i++] = new ExpressionEmitter (e, vi);
4459 continue;
4463 // Case 1: & object.
4465 Unary u = e as Unary;
4466 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4467 IVariableReference vr = u.Expr as IVariableReference;
4468 if (vr == null || !vr.IsFixed) {
4469 data [i] = new ExpressionEmitter (e, vi);
4473 if (data [i++] == null)
4474 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4476 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4479 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4480 bool ok = statement.Resolve (ec);
4481 bool flow_unreachable = ec.EndFlowBranching ();
4482 has_ret = flow_unreachable;
4484 return ok;
4487 protected override void DoEmit (EmitContext ec)
4489 for (int i = 0; i < data.Length; i++) {
4490 data [i].Emit (ec);
4493 statement.Emit (ec);
4495 if (has_ret)
4496 return;
4499 // Clear the pinned variable
4501 for (int i = 0; i < data.Length; i++) {
4502 data [i].EmitExit (ec);
4506 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4508 // Fixed statement cannot be used inside anonymous methods or lambdas
4509 throw new NotSupportedException ();
4512 protected override void CloneTo (CloneContext clonectx, Statement t)
4514 Fixed target = (Fixed) t;
4516 target.type = type.Clone (clonectx);
4517 target.declarators = new ArrayList (declarators.Count);
4518 foreach (Pair p in declarators) {
4519 LocalInfo vi = (LocalInfo) p.First;
4520 Expression e = (Expression) p.Second;
4522 target.declarators.Add (
4523 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4526 target.statement = statement.Clone (clonectx);
4530 public class Catch : Statement {
4531 public readonly string Name;
4532 public Block Block;
4533 public Block VarBlock;
4535 Expression type_expr;
4536 Type type;
4538 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4540 type_expr = type;
4541 Name = name;
4542 Block = block;
4543 VarBlock = var_block;
4544 loc = l;
4547 public Type CatchType {
4548 get {
4549 return type;
4553 public bool IsGeneral {
4554 get {
4555 return type_expr == null;
4559 protected override void DoEmit (EmitContext ec)
4561 ILGenerator ig = ec.ig;
4563 if (CatchType != null)
4564 ig.BeginCatchBlock (CatchType);
4565 else
4566 ig.BeginCatchBlock (TypeManager.object_type);
4568 if (VarBlock != null)
4569 VarBlock.Emit (ec);
4571 if (Name != null) {
4572 // TODO: Move to resolve
4573 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4574 lvr.Resolve (ec);
4576 #if GMCS_SOURCE
4577 // Only to make verifier happy
4578 if (TypeManager.IsGenericParameter (lvr.Type))
4579 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4580 #endif
4582 Expression source;
4583 if (lvr.IsHoisted) {
4584 LocalTemporary lt = new LocalTemporary (lvr.Type);
4585 lt.Store (ec);
4586 source = lt;
4587 } else {
4588 // Variable is at the top of the stack
4589 source = EmptyExpression.Null;
4592 lvr.EmitAssign (ec, source, false, false);
4593 } else
4594 ig.Emit (OpCodes.Pop);
4596 Block.Emit (ec);
4599 public override bool Resolve (EmitContext ec)
4601 using (ec.With (EmitContext.Flags.InCatch, true)) {
4602 if (type_expr != null) {
4603 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4604 if (te == null)
4605 return false;
4607 type = te.Type;
4609 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4610 Error (155, "The type caught or thrown must be derived from System.Exception");
4611 return false;
4613 } else
4614 type = null;
4616 if (!Block.Resolve (ec))
4617 return false;
4619 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4620 // emit the "unused variable" warnings.
4621 if (VarBlock != null)
4622 return VarBlock.Resolve (ec);
4624 return true;
4628 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4630 if (type != null)
4631 type = storey.MutateType (type);
4632 if (VarBlock != null)
4633 VarBlock.MutateHoistedGenericType (storey);
4634 Block.MutateHoistedGenericType (storey);
4637 protected override void CloneTo (CloneContext clonectx, Statement t)
4639 Catch target = (Catch) t;
4641 if (type_expr != null)
4642 target.type_expr = type_expr.Clone (clonectx);
4643 if (VarBlock != null)
4644 target.VarBlock = clonectx.LookupBlock (VarBlock);
4645 target.Block = clonectx.LookupBlock (Block);
4649 public class TryFinally : ExceptionStatement {
4650 Statement stmt;
4651 Block fini;
4653 public TryFinally (Statement stmt, Block fini, Location l)
4655 this.stmt = stmt;
4656 this.fini = fini;
4657 loc = l;
4660 public override bool Resolve (EmitContext ec)
4662 bool ok = true;
4664 ec.StartFlowBranching (this);
4666 if (!stmt.Resolve (ec))
4667 ok = false;
4669 if (ok)
4670 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4671 using (ec.With (EmitContext.Flags.InFinally, true)) {
4672 if (!fini.Resolve (ec))
4673 ok = false;
4676 ec.EndFlowBranching ();
4678 ResolveReachability (ec);
4680 return ok;
4683 protected override void EmitPreTryBody (EmitContext ec)
4687 protected override void EmitTryBody (EmitContext ec)
4689 stmt.Emit (ec);
4692 protected override void EmitFinallyBody (EmitContext ec)
4694 fini.Emit (ec);
4697 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4699 stmt.MutateHoistedGenericType (storey);
4700 fini.MutateHoistedGenericType (storey);
4703 protected override void CloneTo (CloneContext clonectx, Statement t)
4705 TryFinally target = (TryFinally) t;
4707 target.stmt = (Statement) stmt.Clone (clonectx);
4708 if (fini != null)
4709 target.fini = clonectx.LookupBlock (fini);
4713 public class TryCatch : Statement {
4714 public Block Block;
4715 public ArrayList Specific;
4716 public Catch General;
4717 bool inside_try_finally, code_follows;
4719 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4721 this.Block = block;
4722 this.Specific = catch_clauses;
4723 this.General = null;
4724 this.inside_try_finally = inside_try_finally;
4726 for (int i = 0; i < catch_clauses.Count; ++i) {
4727 Catch c = (Catch) catch_clauses [i];
4728 if (c.IsGeneral) {
4729 if (i != catch_clauses.Count - 1)
4730 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4731 this.General = c;
4732 catch_clauses.RemoveAt (i);
4733 i--;
4737 loc = l;
4740 public override bool Resolve (EmitContext ec)
4742 bool ok = true;
4744 ec.StartFlowBranching (this);
4746 if (!Block.Resolve (ec))
4747 ok = false;
4749 Type[] prev_catches = new Type [Specific.Count];
4750 int last_index = 0;
4751 foreach (Catch c in Specific){
4752 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4754 if (c.Name != null) {
4755 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4756 if (vi == null)
4757 throw new Exception ();
4759 vi.VariableInfo = null;
4762 if (!c.Resolve (ec)) {
4763 ok = false;
4764 continue;
4767 Type resolved_type = c.CatchType;
4768 for (int ii = 0; ii < last_index; ++ii) {
4769 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4770 Report.Error (160, c.loc,
4771 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4772 TypeManager.CSharpName (prev_catches [ii]));
4773 ok = false;
4777 prev_catches [last_index++] = resolved_type;
4780 if (General != null) {
4781 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4782 foreach (Catch c in Specific){
4783 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4784 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'");
4789 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4791 if (!General.Resolve (ec))
4792 ok = false;
4795 ec.EndFlowBranching ();
4797 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4798 // So, ensure there's some IL code after this statement
4799 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4800 ec.NeedReturnLabel ();
4802 return ok;
4805 public void SomeCodeFollows ()
4807 code_follows = true;
4810 protected override void DoEmit (EmitContext ec)
4812 ILGenerator ig = ec.ig;
4814 if (!inside_try_finally)
4815 ig.BeginExceptionBlock ();
4817 Block.Emit (ec);
4819 foreach (Catch c in Specific)
4820 c.Emit (ec);
4822 if (General != null)
4823 General.Emit (ec);
4825 if (!inside_try_finally)
4826 ig.EndExceptionBlock ();
4829 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4831 Block.MutateHoistedGenericType (storey);
4833 if (General != null)
4834 General.MutateHoistedGenericType (storey);
4835 if (Specific != null) {
4836 foreach (Catch c in Specific)
4837 c.MutateHoistedGenericType (storey);
4841 protected override void CloneTo (CloneContext clonectx, Statement t)
4843 TryCatch target = (TryCatch) t;
4845 target.Block = clonectx.LookupBlock (Block);
4846 if (General != null)
4847 target.General = (Catch) General.Clone (clonectx);
4848 if (Specific != null){
4849 target.Specific = new ArrayList ();
4850 foreach (Catch c in Specific)
4851 target.Specific.Add (c.Clone (clonectx));
4856 // FIXME: Why is it almost exact copy of Using ??
4857 public class UsingTemporary : ExceptionStatement {
4858 TemporaryVariable local_copy;
4859 public Statement Statement;
4860 Expression expr;
4861 Type expr_type;
4863 public UsingTemporary (Expression expr, Statement stmt, Location l)
4865 this.expr = expr;
4866 Statement = stmt;
4867 loc = l;
4870 public override bool Resolve (EmitContext ec)
4872 expr = expr.Resolve (ec);
4873 if (expr == null)
4874 return false;
4876 expr_type = expr.Type;
4878 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4879 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4880 Using.Error_IsNotConvertibleToIDisposable (expr);
4881 return false;
4885 local_copy = new TemporaryVariable (expr_type, loc);
4886 local_copy.Resolve (ec);
4888 ec.StartFlowBranching (this);
4890 bool ok = Statement.Resolve (ec);
4892 ec.EndFlowBranching ();
4894 ResolveReachability (ec);
4896 if (TypeManager.void_dispose_void == null) {
4897 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4898 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4901 return ok;
4904 protected override void EmitPreTryBody (EmitContext ec)
4906 local_copy.EmitAssign (ec, expr);
4909 protected override void EmitTryBody (EmitContext ec)
4911 Statement.Emit (ec);
4914 protected override void EmitFinallyBody (EmitContext ec)
4916 ILGenerator ig = ec.ig;
4917 if (!TypeManager.IsStruct (expr_type)) {
4918 Label skip = ig.DefineLabel ();
4919 local_copy.Emit (ec);
4920 ig.Emit (OpCodes.Brfalse, skip);
4921 local_copy.Emit (ec);
4922 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4923 ig.MarkLabel (skip);
4924 return;
4927 Expression ml = Expression.MemberLookup (
4928 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4929 "Dispose", Location.Null);
4931 if (!(ml is MethodGroupExpr)) {
4932 local_copy.Emit (ec);
4933 ig.Emit (OpCodes.Box, expr_type);
4934 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4935 return;
4938 MethodInfo mi = null;
4940 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4941 if (TypeManager.GetParameterData (mk).Count == 0) {
4942 mi = mk;
4943 break;
4947 if (mi == null) {
4948 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4949 return;
4952 local_copy.AddressOf (ec, AddressOp.Load);
4953 ig.Emit (OpCodes.Call, mi);
4956 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4958 expr_type = storey.MutateType (expr_type);
4959 local_copy.MutateHoistedGenericType (storey);
4960 Statement.MutateHoistedGenericType (storey);
4963 protected override void CloneTo (CloneContext clonectx, Statement t)
4965 UsingTemporary target = (UsingTemporary) t;
4967 target.expr = expr.Clone (clonectx);
4968 target.Statement = Statement.Clone (clonectx);
4972 public class Using : ExceptionStatement {
4973 Statement stmt;
4974 public Statement EmbeddedStatement {
4975 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4978 Expression var;
4979 Expression init;
4981 ExpressionStatement assign;
4983 public Using (Expression var, Expression init, Statement stmt, Location l)
4985 this.var = var;
4986 this.init = init;
4987 this.stmt = stmt;
4988 loc = l;
4991 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4993 Report.SymbolRelatedToPreviousError (expr.Type);
4994 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4995 expr.GetSignatureForError ());
4998 protected override void EmitPreTryBody (EmitContext ec)
5000 assign.EmitStatement (ec);
5003 protected override void EmitTryBody (EmitContext ec)
5005 stmt.Emit (ec);
5008 protected override void EmitFinallyBody (EmitContext ec)
5010 ILGenerator ig = ec.ig;
5011 Label skip = ig.DefineLabel ();
5013 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5014 if (emit_null_check) {
5015 var.Emit (ec);
5016 ig.Emit (OpCodes.Brfalse, skip);
5019 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5021 if (emit_null_check)
5022 ig.MarkLabel (skip);
5025 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5027 assign.MutateHoistedGenericType (storey);
5028 var.MutateHoistedGenericType (storey);
5029 stmt.MutateHoistedGenericType (storey);
5032 public override bool Resolve (EmitContext ec)
5034 if (!ResolveVariable (ec))
5035 return false;
5037 ec.StartFlowBranching (this);
5039 bool ok = stmt.Resolve (ec);
5041 ec.EndFlowBranching ();
5043 ResolveReachability (ec);
5045 if (TypeManager.void_dispose_void == null) {
5046 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5047 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5050 return ok;
5053 bool ResolveVariable (EmitContext ec)
5055 assign = new SimpleAssign (var, init, loc);
5056 assign = assign.ResolveStatement (ec);
5057 if (assign == null)
5058 return false;
5060 if (assign.Type == TypeManager.idisposable_type ||
5061 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5062 return true;
5065 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5066 if (e == null) {
5067 Error_IsNotConvertibleToIDisposable (var);
5068 return false;
5071 throw new NotImplementedException ("covariance?");
5074 protected override void CloneTo (CloneContext clonectx, Statement t)
5076 Using target = (Using) t;
5078 target.var = var.Clone (clonectx);
5079 target.init = init.Clone (clonectx);
5080 target.stmt = stmt.Clone (clonectx);
5084 /// <summary>
5085 /// Implementation of the foreach C# statement
5086 /// </summary>
5087 public class Foreach : Statement {
5089 sealed class ArrayForeach : Statement
5091 class ArrayCounter : TemporaryVariable
5093 StatementExpression increment;
5095 public ArrayCounter (Location loc)
5096 : base (TypeManager.int32_type, loc)
5100 public void ResolveIncrement (EmitContext ec)
5102 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5103 increment.Resolve (ec);
5106 public void EmitIncrement (EmitContext ec)
5108 increment.Emit (ec);
5112 readonly Foreach for_each;
5113 readonly Statement statement;
5115 Expression conv;
5116 TemporaryVariable[] lengths;
5117 Expression [] length_exprs;
5118 ArrayCounter[] counter;
5120 TemporaryVariable copy;
5121 Expression access;
5123 public ArrayForeach (Foreach @foreach, int rank)
5125 for_each = @foreach;
5126 statement = for_each.statement;
5127 loc = @foreach.loc;
5129 counter = new ArrayCounter [rank];
5130 length_exprs = new Expression [rank];
5133 // Only use temporary length variables when dealing with
5134 // multi-dimensional arrays
5136 if (rank > 1)
5137 lengths = new TemporaryVariable [rank];
5140 protected override void CloneTo (CloneContext clonectx, Statement target)
5142 throw new NotImplementedException ();
5145 public override bool Resolve (EmitContext ec)
5147 copy = new TemporaryVariable (for_each.expr.Type, loc);
5148 copy.Resolve (ec);
5150 int rank = length_exprs.Length;
5151 Arguments list = new Arguments (rank);
5152 for (int i = 0; i < rank; i++) {
5153 counter [i] = new ArrayCounter (loc);
5154 counter [i].ResolveIncrement (ec);
5156 if (rank == 1) {
5157 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5158 } else {
5159 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5160 lengths [i].Resolve (ec);
5162 Arguments args = new Arguments (1);
5163 args.Add (new Argument (new IntConstant (i, loc)));
5164 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5167 list.Add (new Argument (counter [i]));
5170 access = new ElementAccess (copy, list).Resolve (ec);
5171 if (access == null)
5172 return false;
5174 Expression var_type = for_each.type;
5175 VarExpr ve = var_type as VarExpr;
5176 if (ve != null) {
5177 // Infer implicitly typed local variable from foreach array type
5178 var_type = new TypeExpression (access.Type, ve.Location);
5181 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5182 if (var_type == null)
5183 return false;
5185 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5186 if (conv == null)
5187 return false;
5189 bool ok = true;
5191 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5192 ec.CurrentBranching.CreateSibling ();
5194 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5195 if (for_each.variable == null)
5196 ok = false;
5198 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5199 if (!statement.Resolve (ec))
5200 ok = false;
5201 ec.EndFlowBranching ();
5203 // There's no direct control flow from the end of the embedded statement to the end of the loop
5204 ec.CurrentBranching.CurrentUsageVector.Goto ();
5206 ec.EndFlowBranching ();
5208 return ok;
5211 protected override void DoEmit (EmitContext ec)
5213 ILGenerator ig = ec.ig;
5215 copy.EmitAssign (ec, for_each.expr);
5217 int rank = length_exprs.Length;
5218 Label[] test = new Label [rank];
5219 Label[] loop = new Label [rank];
5221 for (int i = 0; i < rank; i++) {
5222 test [i] = ig.DefineLabel ();
5223 loop [i] = ig.DefineLabel ();
5225 if (lengths != null)
5226 lengths [i].EmitAssign (ec, length_exprs [i]);
5229 IntConstant zero = new IntConstant (0, loc);
5230 for (int i = 0; i < rank; i++) {
5231 counter [i].EmitAssign (ec, zero);
5233 ig.Emit (OpCodes.Br, test [i]);
5234 ig.MarkLabel (loop [i]);
5237 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5239 statement.Emit (ec);
5241 ig.MarkLabel (ec.LoopBegin);
5243 for (int i = rank - 1; i >= 0; i--){
5244 counter [i].EmitIncrement (ec);
5246 ig.MarkLabel (test [i]);
5247 counter [i].Emit (ec);
5249 if (lengths != null)
5250 lengths [i].Emit (ec);
5251 else
5252 length_exprs [i].Emit (ec);
5254 ig.Emit (OpCodes.Blt, loop [i]);
5257 ig.MarkLabel (ec.LoopEnd);
5260 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5262 for_each.expr.MutateHoistedGenericType (storey);
5264 copy.MutateHoistedGenericType (storey);
5265 conv.MutateHoistedGenericType (storey);
5266 statement.MutateHoistedGenericType (storey);
5268 for (int i = 0; i < counter.Length; i++) {
5269 counter [i].MutateHoistedGenericType (storey);
5270 if (lengths != null)
5271 lengths [i].MutateHoistedGenericType (storey);
5276 sealed class CollectionForeach : Statement
5278 class CollectionForeachStatement : Statement
5280 Type type;
5281 Expression variable, current, conv;
5282 Statement statement;
5283 Assign assign;
5285 public CollectionForeachStatement (Type type, Expression variable,
5286 Expression current, Statement statement,
5287 Location loc)
5289 this.type = type;
5290 this.variable = variable;
5291 this.current = current;
5292 this.statement = statement;
5293 this.loc = loc;
5296 protected override void CloneTo (CloneContext clonectx, Statement target)
5298 throw new NotImplementedException ();
5301 public override bool Resolve (EmitContext ec)
5303 current = current.Resolve (ec);
5304 if (current == null)
5305 return false;
5307 conv = Convert.ExplicitConversion (ec, current, type, loc);
5308 if (conv == null)
5309 return false;
5311 assign = new SimpleAssign (variable, conv, loc);
5312 if (assign.Resolve (ec) == null)
5313 return false;
5315 if (!statement.Resolve (ec))
5316 return false;
5318 return true;
5321 protected override void DoEmit (EmitContext ec)
5323 assign.EmitStatement (ec);
5324 statement.Emit (ec);
5327 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5329 assign.MutateHoistedGenericType (storey);
5330 statement.MutateHoistedGenericType (storey);
5334 Expression variable, expr;
5335 Statement statement;
5337 TemporaryVariable enumerator;
5338 Expression init;
5339 Statement loop;
5340 Statement wrapper;
5342 MethodGroupExpr get_enumerator;
5343 PropertyExpr get_current;
5344 MethodInfo move_next;
5345 Expression var_type;
5346 Type enumerator_type;
5347 bool enumerator_found;
5349 public CollectionForeach (Expression var_type, Expression var,
5350 Expression expr, Statement stmt, Location l)
5352 this.var_type = var_type;
5353 this.variable = var;
5354 this.expr = expr;
5355 statement = stmt;
5356 loc = l;
5359 protected override void CloneTo (CloneContext clonectx, Statement target)
5361 throw new NotImplementedException ();
5364 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5366 Type return_type = mi.ReturnType;
5369 // Ok, we can access it, now make sure that we can do something
5370 // with this `GetEnumerator'
5373 if (return_type == TypeManager.ienumerator_type ||
5374 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5376 // If it is not an interface, lets try to find the methods ourselves.
5377 // For example, if we have:
5378 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5379 // We can avoid the iface call. This is a runtime perf boost.
5380 // even bigger if we have a ValueType, because we avoid the cost
5381 // of boxing.
5383 // We have to make sure that both methods exist for us to take
5384 // this path. If one of the methods does not exist, we will just
5385 // use the interface. Sadly, this complex if statement is the only
5386 // way I could do this without a goto
5389 if (TypeManager.bool_movenext_void == null) {
5390 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5391 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5394 if (TypeManager.ienumerator_getcurrent == null) {
5395 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5396 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5400 // Prefer a generic enumerator over a non-generic one.
5402 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5403 enumerator_type = return_type;
5404 if (!FetchGetCurrent (ec, return_type))
5405 get_current = new PropertyExpr (
5406 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5407 if (!FetchMoveNext (return_type))
5408 move_next = TypeManager.bool_movenext_void;
5409 return true;
5412 if (return_type.IsInterface ||
5413 !FetchMoveNext (return_type) ||
5414 !FetchGetCurrent (ec, return_type)) {
5415 enumerator_type = return_type;
5416 move_next = TypeManager.bool_movenext_void;
5417 get_current = new PropertyExpr (
5418 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5419 return true;
5421 } else {
5423 // Ok, so they dont return an IEnumerable, we will have to
5424 // find if they support the GetEnumerator pattern.
5427 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5428 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",
5429 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5430 return false;
5434 enumerator_type = return_type;
5436 return true;
5440 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5442 bool FetchMoveNext (Type t)
5444 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5445 MemberTypes.Method,
5446 BindingFlags.Public | BindingFlags.Instance,
5447 "MoveNext", null);
5449 foreach (MemberInfo m in move_next_list){
5450 MethodInfo mi = (MethodInfo) m;
5452 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5453 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5454 move_next = mi;
5455 return true;
5459 return false;
5463 // Retrieves a `public T get_Current ()' method from the Type `t'
5465 bool FetchGetCurrent (EmitContext ec, Type t)
5467 PropertyExpr pe = Expression.MemberLookup (
5468 ec.ContainerType, t, "Current", MemberTypes.Property,
5469 Expression.AllBindingFlags, loc) as PropertyExpr;
5470 if (pe == null)
5471 return false;
5473 get_current = pe;
5474 return true;
5477 void Error_Enumerator ()
5479 if (enumerator_found) {
5480 return;
5483 Report.Error (1579, loc,
5484 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5485 TypeManager.CSharpName (expr.Type));
5488 bool IsOverride (MethodInfo m)
5490 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5492 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5493 return false;
5494 if (m is MethodBuilder)
5495 return true;
5497 MethodInfo base_method = m.GetBaseDefinition ();
5498 return base_method != m;
5501 bool TryType (EmitContext ec, Type t)
5503 MethodGroupExpr mg = Expression.MemberLookup (
5504 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5505 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5506 if (mg == null)
5507 return false;
5509 MethodInfo result = null;
5510 MethodInfo tmp_move_next = null;
5511 PropertyExpr tmp_get_cur = null;
5512 Type tmp_enumerator_type = enumerator_type;
5513 foreach (MethodInfo mi in mg.Methods) {
5514 if (TypeManager.GetParameterData (mi).Count != 0)
5515 continue;
5517 // Check whether GetEnumerator is public
5518 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5519 continue;
5521 if (IsOverride (mi))
5522 continue;
5524 enumerator_found = true;
5526 if (!GetEnumeratorFilter (ec, mi))
5527 continue;
5529 if (result != null) {
5530 if (TypeManager.IsGenericType (result.ReturnType)) {
5531 if (!TypeManager.IsGenericType (mi.ReturnType))
5532 continue;
5534 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5535 Report.SymbolRelatedToPreviousError (t);
5536 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5537 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5538 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5539 return false;
5542 // Always prefer generics enumerators
5543 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5544 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5545 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5546 continue;
5548 Report.SymbolRelatedToPreviousError (result);
5549 Report.SymbolRelatedToPreviousError (mi);
5550 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5551 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5552 return false;
5555 result = mi;
5556 tmp_move_next = move_next;
5557 tmp_get_cur = get_current;
5558 tmp_enumerator_type = enumerator_type;
5559 if (mi.DeclaringType == t)
5560 break;
5563 if (result != null) {
5564 move_next = tmp_move_next;
5565 get_current = tmp_get_cur;
5566 enumerator_type = tmp_enumerator_type;
5567 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5568 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5570 if (t != expr.Type) {
5571 expr = Convert.ExplicitConversion (
5572 ec, expr, t, loc);
5573 if (expr == null)
5574 throw new InternalErrorException ();
5577 get_enumerator.InstanceExpression = expr;
5578 get_enumerator.IsBase = t != expr.Type;
5580 return true;
5583 return false;
5586 bool ProbeCollectionType (EmitContext ec, Type t)
5588 int errors = Report.Errors;
5589 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5590 if (TryType (ec, tt))
5591 return true;
5592 tt = tt.BaseType;
5595 if (Report.Errors > errors)
5596 return false;
5599 // Now try to find the method in the interfaces
5601 Type [] ifaces = TypeManager.GetInterfaces (t);
5602 foreach (Type i in ifaces){
5603 if (TryType (ec, i))
5604 return true;
5607 return false;
5610 public override bool Resolve (EmitContext ec)
5612 enumerator_type = TypeManager.ienumerator_type;
5614 if (!ProbeCollectionType (ec, expr.Type)) {
5615 Error_Enumerator ();
5616 return false;
5619 VarExpr ve = var_type as VarExpr;
5620 if (ve != null) {
5621 // Infer implicitly typed local variable from foreach enumerable type
5622 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5625 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5626 if (var_type == null)
5627 return false;
5629 enumerator = new TemporaryVariable (enumerator_type, loc);
5630 enumerator.Resolve (ec);
5632 init = new Invocation (get_enumerator, null);
5633 init = init.Resolve (ec);
5634 if (init == null)
5635 return false;
5637 Expression move_next_expr;
5639 MemberInfo[] mi = new MemberInfo[] { move_next };
5640 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5641 mg.InstanceExpression = enumerator;
5643 move_next_expr = new Invocation (mg, null);
5646 get_current.InstanceExpression = enumerator;
5648 Statement block = new CollectionForeachStatement (
5649 var_type.Type, variable, get_current, statement, loc);
5651 loop = new While (move_next_expr, block, loc);
5654 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5655 if (implements_idisposable || !enumerator_type.IsSealed) {
5656 wrapper = new DisposableWrapper (this, implements_idisposable);
5657 } else {
5658 wrapper = new NonDisposableWrapper (this);
5661 return wrapper.Resolve (ec);
5664 protected override void DoEmit (EmitContext ec)
5666 wrapper.Emit (ec);
5669 class NonDisposableWrapper : Statement {
5670 CollectionForeach parent;
5672 internal NonDisposableWrapper (CollectionForeach parent)
5674 this.parent = parent;
5677 protected override void CloneTo (CloneContext clonectx, Statement target)
5679 throw new NotSupportedException ();
5682 public override bool Resolve (EmitContext ec)
5684 return parent.ResolveLoop (ec);
5687 protected override void DoEmit (EmitContext ec)
5689 parent.EmitLoopInit (ec);
5690 parent.EmitLoopBody (ec);
5693 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5695 throw new NotSupportedException ();
5699 sealed class DisposableWrapper : ExceptionStatement
5701 CollectionForeach parent;
5702 bool implements_idisposable;
5704 internal DisposableWrapper (CollectionForeach parent, bool implements)
5706 this.parent = parent;
5707 this.implements_idisposable = implements;
5710 protected override void CloneTo (CloneContext clonectx, Statement target)
5712 throw new NotSupportedException ();
5715 public override bool Resolve (EmitContext ec)
5717 bool ok = true;
5719 ec.StartFlowBranching (this);
5721 if (!parent.ResolveLoop (ec))
5722 ok = false;
5724 ec.EndFlowBranching ();
5726 ResolveReachability (ec);
5728 if (TypeManager.void_dispose_void == null) {
5729 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5730 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5732 return ok;
5735 protected override void EmitPreTryBody (EmitContext ec)
5737 parent.EmitLoopInit (ec);
5740 protected override void EmitTryBody (EmitContext ec)
5742 parent.EmitLoopBody (ec);
5745 protected override void EmitFinallyBody (EmitContext ec)
5747 Expression instance = parent.enumerator;
5748 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5749 ILGenerator ig = ec.ig;
5751 parent.enumerator.Emit (ec);
5753 Label call_dispose = ig.DefineLabel ();
5755 if (!implements_idisposable) {
5756 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5757 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5758 temp.Store (ec);
5759 temp.Emit (ec);
5760 instance = temp;
5763 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5765 // using 'endfinally' to empty the evaluation stack
5766 ig.Emit (OpCodes.Endfinally);
5767 ig.MarkLabel (call_dispose);
5770 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5773 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5775 throw new NotSupportedException ();
5779 bool ResolveLoop (EmitContext ec)
5781 return loop.Resolve (ec);
5784 void EmitLoopInit (EmitContext ec)
5786 enumerator.EmitAssign (ec, init);
5789 void EmitLoopBody (EmitContext ec)
5791 loop.Emit (ec);
5794 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5796 enumerator_type = storey.MutateType (enumerator_type);
5797 init.MutateHoistedGenericType (storey);
5798 loop.MutateHoistedGenericType (storey);
5802 Expression type;
5803 Expression variable;
5804 Expression expr;
5805 Statement statement;
5807 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5808 Statement stmt, Location l)
5810 this.type = type;
5811 this.variable = var;
5812 this.expr = expr;
5813 statement = stmt;
5814 loc = l;
5817 public Statement Statement {
5818 get { return statement; }
5821 public override bool Resolve (EmitContext ec)
5823 expr = expr.Resolve (ec);
5824 if (expr == null)
5825 return false;
5827 if (expr.IsNull) {
5828 Report.Error (186, loc, "Use of null is not valid in this context");
5829 return false;
5832 if (expr.Type == TypeManager.string_type) {
5833 statement = new ArrayForeach (this, 1);
5834 } else if (expr.Type.IsArray) {
5835 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5836 } else {
5837 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5838 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5839 expr.ExprClassName);
5840 return false;
5843 statement = new CollectionForeach (type, variable, expr, statement, loc);
5846 return statement.Resolve (ec);
5849 protected override void DoEmit (EmitContext ec)
5851 ILGenerator ig = ec.ig;
5853 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5854 ec.LoopBegin = ig.DefineLabel ();
5855 ec.LoopEnd = ig.DefineLabel ();
5857 statement.Emit (ec);
5859 ec.LoopBegin = old_begin;
5860 ec.LoopEnd = old_end;
5863 protected override void CloneTo (CloneContext clonectx, Statement t)
5865 Foreach target = (Foreach) t;
5867 target.type = type.Clone (clonectx);
5868 target.variable = variable.Clone (clonectx);
5869 target.expr = expr.Clone (clonectx);
5870 target.statement = statement.Clone (clonectx);
5873 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5875 statement.MutateHoistedGenericType (storey);