2009-11-02 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / statement.cs
blob7039e2aab8d6cffdb7c15af0c6fb1cb2e90c0279
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 (BlockContext 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 (BlockContext 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 ec.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 public virtual void Emit (EmitContext ec)
67 ec.Mark (loc);
68 DoEmit (ec);
72 // This routine must be overrided in derived classes and make copies
73 // of all the data that might be modified if resolved
74 //
75 protected abstract void CloneTo (CloneContext clonectx, Statement target);
77 public Statement Clone (CloneContext clonectx)
79 Statement s = (Statement) this.MemberwiseClone ();
80 CloneTo (clonectx, s);
81 return s;
84 public virtual Expression CreateExpressionTree (ResolveContext ec)
86 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
87 return null;
90 public Statement PerformClone ()
92 CloneContext clonectx = new CloneContext ();
94 return Clone (clonectx);
97 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
100 public sealed class EmptyStatement : Statement {
102 private EmptyStatement () {}
104 public static readonly EmptyStatement Value = new EmptyStatement ();
106 public override bool Resolve (BlockContext ec)
108 return true;
111 public override bool ResolveUnreachable (BlockContext ec, bool warn)
113 return true;
116 protected override void DoEmit (EmitContext ec)
120 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
124 protected override void CloneTo (CloneContext clonectx, Statement target)
126 // nothing needed.
130 public class If : Statement {
131 Expression expr;
132 public Statement TrueStatement;
133 public Statement FalseStatement;
135 bool is_true_ret;
137 public If (Expression bool_expr, Statement true_statement, Location l)
138 : this (bool_expr, true_statement, null, l)
142 public If (Expression bool_expr,
143 Statement true_statement,
144 Statement false_statement,
145 Location l)
147 this.expr = bool_expr;
148 TrueStatement = true_statement;
149 FalseStatement = false_statement;
150 loc = l;
153 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
155 expr.MutateHoistedGenericType (storey);
156 TrueStatement.MutateHoistedGenericType (storey);
157 if (FalseStatement != null)
158 FalseStatement.MutateHoistedGenericType (storey);
161 public override bool Resolve (BlockContext ec)
163 bool ok = true;
165 Report.Debug (1, "START IF BLOCK", loc);
167 expr = expr.Resolve (ec);
168 if (expr == null) {
169 ok = false;
170 } else {
172 // Dead code elimination
174 if (expr is Constant) {
175 bool take = !((Constant) expr).IsDefaultValue;
177 if (take) {
178 if (!TrueStatement.Resolve (ec))
179 return false;
181 if ((FalseStatement != null) &&
182 !FalseStatement.ResolveUnreachable (ec, true))
183 return false;
184 FalseStatement = null;
185 } else {
186 if (!TrueStatement.ResolveUnreachable (ec, true))
187 return false;
188 TrueStatement = null;
190 if ((FalseStatement != null) &&
191 !FalseStatement.Resolve (ec))
192 return false;
195 return true;
199 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
201 ok &= TrueStatement.Resolve (ec);
203 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
205 ec.CurrentBranching.CreateSibling ();
207 if (FalseStatement != null)
208 ok &= FalseStatement.Resolve (ec);
210 ec.EndFlowBranching ();
212 Report.Debug (1, "END IF BLOCK", loc);
214 return ok;
217 protected override void DoEmit (EmitContext ec)
219 ILGenerator ig = ec.ig;
220 Label false_target = ig.DefineLabel ();
221 Label end;
224 // If we're a boolean constant, Resolve() already
225 // eliminated dead code for us.
227 Constant c = expr as Constant;
228 if (c != null){
229 c.EmitSideEffect (ec);
231 if (!c.IsDefaultValue)
232 TrueStatement.Emit (ec);
233 else if (FalseStatement != null)
234 FalseStatement.Emit (ec);
236 return;
239 expr.EmitBranchable (ec, false_target, false);
241 TrueStatement.Emit (ec);
243 if (FalseStatement != null){
244 bool branch_emitted = false;
246 end = ig.DefineLabel ();
247 if (!is_true_ret){
248 ig.Emit (OpCodes.Br, end);
249 branch_emitted = true;
252 ig.MarkLabel (false_target);
253 FalseStatement.Emit (ec);
255 if (branch_emitted)
256 ig.MarkLabel (end);
257 } else {
258 ig.MarkLabel (false_target);
262 protected override void CloneTo (CloneContext clonectx, Statement t)
264 If target = (If) t;
266 target.expr = expr.Clone (clonectx);
267 target.TrueStatement = TrueStatement.Clone (clonectx);
268 if (FalseStatement != null)
269 target.FalseStatement = FalseStatement.Clone (clonectx);
273 public class Do : Statement {
274 public Expression expr;
275 public Statement EmbeddedStatement;
277 public Do (Statement statement, BooleanExpression bool_expr, Location l)
279 expr = bool_expr;
280 EmbeddedStatement = statement;
281 loc = l;
284 public override bool Resolve (BlockContext ec)
286 bool ok = true;
288 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
290 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
292 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
293 if (!EmbeddedStatement.Resolve (ec))
294 ok = false;
295 ec.EndFlowBranching ();
297 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
298 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
300 expr = expr.Resolve (ec);
301 if (expr == null)
302 ok = false;
303 else if (expr is Constant){
304 bool infinite = !((Constant) expr).IsDefaultValue;
305 if (infinite)
306 ec.CurrentBranching.CurrentUsageVector.Goto ();
309 ec.EndFlowBranching ();
311 return ok;
314 protected override void DoEmit (EmitContext ec)
316 ILGenerator ig = ec.ig;
317 Label loop = ig.DefineLabel ();
318 Label old_begin = ec.LoopBegin;
319 Label old_end = ec.LoopEnd;
321 ec.LoopBegin = ig.DefineLabel ();
322 ec.LoopEnd = ig.DefineLabel ();
324 ig.MarkLabel (loop);
325 EmbeddedStatement.Emit (ec);
326 ig.MarkLabel (ec.LoopBegin);
329 // Dead code elimination
331 if (expr is Constant){
332 bool res = !((Constant) expr).IsDefaultValue;
334 expr.EmitSideEffect (ec);
335 if (res)
336 ec.ig.Emit (OpCodes.Br, loop);
337 } else
338 expr.EmitBranchable (ec, loop, true);
340 ig.MarkLabel (ec.LoopEnd);
342 ec.LoopBegin = old_begin;
343 ec.LoopEnd = old_end;
346 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
348 expr.MutateHoistedGenericType (storey);
349 EmbeddedStatement.MutateHoistedGenericType (storey);
352 protected override void CloneTo (CloneContext clonectx, Statement t)
354 Do target = (Do) t;
356 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
357 target.expr = expr.Clone (clonectx);
361 public class While : Statement {
362 public Expression expr;
363 public Statement Statement;
364 bool infinite, empty;
366 public While (BooleanExpression bool_expr, Statement statement, Location l)
368 this.expr = bool_expr;
369 Statement = statement;
370 loc = l;
373 public override bool Resolve (BlockContext ec)
375 bool ok = true;
377 expr = expr.Resolve (ec);
378 if (expr == null)
379 ok = false;
382 // Inform whether we are infinite or not
384 if (expr is Constant){
385 bool value = !((Constant) expr).IsDefaultValue;
387 if (value == false){
388 if (!Statement.ResolveUnreachable (ec, true))
389 return false;
390 empty = true;
391 return true;
392 } else
393 infinite = true;
396 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
397 if (!infinite)
398 ec.CurrentBranching.CreateSibling ();
400 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
401 if (!Statement.Resolve (ec))
402 ok = false;
403 ec.EndFlowBranching ();
405 // There's no direct control flow from the end of the embedded statement to the end of the loop
406 ec.CurrentBranching.CurrentUsageVector.Goto ();
408 ec.EndFlowBranching ();
410 return ok;
413 protected override void DoEmit (EmitContext ec)
415 if (empty) {
416 expr.EmitSideEffect (ec);
417 return;
420 ILGenerator ig = ec.ig;
421 Label old_begin = ec.LoopBegin;
422 Label old_end = ec.LoopEnd;
424 ec.LoopBegin = ig.DefineLabel ();
425 ec.LoopEnd = ig.DefineLabel ();
428 // Inform whether we are infinite or not
430 if (expr is Constant){
431 // expr is 'true', since the 'empty' case above handles the 'false' case
432 ig.MarkLabel (ec.LoopBegin);
433 expr.EmitSideEffect (ec);
434 Statement.Emit (ec);
435 ig.Emit (OpCodes.Br, ec.LoopBegin);
438 // Inform that we are infinite (ie, `we return'), only
439 // if we do not `break' inside the code.
441 ig.MarkLabel (ec.LoopEnd);
442 } else {
443 Label while_loop = ig.DefineLabel ();
445 ig.Emit (OpCodes.Br, ec.LoopBegin);
446 ig.MarkLabel (while_loop);
448 Statement.Emit (ec);
450 ig.MarkLabel (ec.LoopBegin);
451 ec.Mark (loc);
453 expr.EmitBranchable (ec, while_loop, true);
455 ig.MarkLabel (ec.LoopEnd);
458 ec.LoopBegin = old_begin;
459 ec.LoopEnd = old_end;
462 public override void Emit (EmitContext ec)
464 DoEmit (ec);
467 protected override void CloneTo (CloneContext clonectx, Statement t)
469 While target = (While) t;
471 target.expr = expr.Clone (clonectx);
472 target.Statement = Statement.Clone (clonectx);
475 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
477 expr.MutateHoistedGenericType (storey);
478 Statement.MutateHoistedGenericType (storey);
482 public class For : Statement {
483 Expression Test;
484 Statement InitStatement;
485 Statement Increment;
486 public Statement Statement;
487 bool infinite, empty;
489 public For (Statement init_statement,
490 BooleanExpression test,
491 Statement increment,
492 Statement statement,
493 Location l)
495 InitStatement = init_statement;
496 Test = test;
497 Increment = increment;
498 Statement = statement;
499 loc = l;
502 public override bool Resolve (BlockContext ec)
504 bool ok = true;
506 if (InitStatement != null){
507 if (!InitStatement.Resolve (ec))
508 ok = false;
511 if (Test != null){
512 Test = Test.Resolve (ec);
513 if (Test == null)
514 ok = false;
515 else if (Test is Constant){
516 bool value = !((Constant) Test).IsDefaultValue;
518 if (value == false){
519 if (!Statement.ResolveUnreachable (ec, true))
520 return false;
521 if ((Increment != null) &&
522 !Increment.ResolveUnreachable (ec, false))
523 return false;
524 empty = true;
525 return true;
526 } else
527 infinite = true;
529 } else
530 infinite = true;
532 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
533 if (!infinite)
534 ec.CurrentBranching.CreateSibling ();
536 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
538 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
539 if (!Statement.Resolve (ec))
540 ok = false;
541 ec.EndFlowBranching ();
543 if (Increment != null){
544 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
545 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
546 ok = false;
547 } else {
548 if (!Increment.Resolve (ec))
549 ok = false;
553 // There's no direct control flow from the end of the embedded statement to the end of the loop
554 ec.CurrentBranching.CurrentUsageVector.Goto ();
556 ec.EndFlowBranching ();
558 return ok;
561 protected override void DoEmit (EmitContext ec)
563 if (InitStatement != null && InitStatement != EmptyStatement.Value)
564 InitStatement.Emit (ec);
566 if (empty) {
567 Test.EmitSideEffect (ec);
568 return;
571 ILGenerator ig = ec.ig;
572 Label old_begin = ec.LoopBegin;
573 Label old_end = ec.LoopEnd;
574 Label loop = ig.DefineLabel ();
575 Label test = ig.DefineLabel ();
577 ec.LoopBegin = ig.DefineLabel ();
578 ec.LoopEnd = ig.DefineLabel ();
580 ig.Emit (OpCodes.Br, test);
581 ig.MarkLabel (loop);
582 Statement.Emit (ec);
584 ig.MarkLabel (ec.LoopBegin);
585 if (Increment != EmptyStatement.Value)
586 Increment.Emit (ec);
588 ig.MarkLabel (test);
590 // If test is null, there is no test, and we are just
591 // an infinite loop
593 if (Test != null){
595 // The Resolve code already catches the case for
596 // Test == Constant (false) so we know that
597 // this is true
599 if (Test is Constant) {
600 Test.EmitSideEffect (ec);
601 ig.Emit (OpCodes.Br, loop);
602 } else {
603 Test.EmitBranchable (ec, loop, true);
606 } else
607 ig.Emit (OpCodes.Br, loop);
608 ig.MarkLabel (ec.LoopEnd);
610 ec.LoopBegin = old_begin;
611 ec.LoopEnd = old_end;
614 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
616 if (InitStatement != null)
617 InitStatement.MutateHoistedGenericType (storey);
618 if (Test != null)
619 Test.MutateHoistedGenericType (storey);
620 if (Increment != null)
621 Increment.MutateHoistedGenericType (storey);
623 Statement.MutateHoistedGenericType (storey);
626 protected override void CloneTo (CloneContext clonectx, Statement t)
628 For target = (For) t;
630 if (InitStatement != null)
631 target.InitStatement = InitStatement.Clone (clonectx);
632 if (Test != null)
633 target.Test = Test.Clone (clonectx);
634 if (Increment != null)
635 target.Increment = Increment.Clone (clonectx);
636 target.Statement = Statement.Clone (clonectx);
640 public class StatementExpression : Statement {
641 ExpressionStatement expr;
643 public StatementExpression (ExpressionStatement expr)
645 this.expr = expr;
646 loc = expr.Location;
649 public override bool Resolve (BlockContext ec)
651 if (expr != null && expr.eclass == ExprClass.Invalid)
652 expr = expr.ResolveStatement (ec);
653 return expr != null;
656 protected override void DoEmit (EmitContext ec)
658 expr.EmitStatement (ec);
661 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
663 expr.MutateHoistedGenericType (storey);
666 public override string ToString ()
668 return "StatementExpression (" + expr + ")";
671 protected override void CloneTo (CloneContext clonectx, Statement t)
673 StatementExpression target = (StatementExpression) t;
675 target.expr = (ExpressionStatement) expr.Clone (clonectx);
679 // A 'return' or a 'yield break'
680 public abstract class ExitStatement : Statement
682 protected bool unwind_protect;
683 protected abstract bool DoResolve (BlockContext ec);
685 public virtual void Error_FinallyClause (Report Report)
687 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
690 public sealed override bool Resolve (BlockContext ec)
692 if (!DoResolve (ec))
693 return false;
695 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
696 if (unwind_protect)
697 ec.NeedReturnLabel ();
698 ec.CurrentBranching.CurrentUsageVector.Goto ();
699 return true;
703 /// <summary>
704 /// Implements the return statement
705 /// </summary>
706 public class Return : ExitStatement {
707 protected Expression Expr;
708 public Return (Expression expr, Location l)
710 Expr = expr;
711 loc = l;
714 protected override bool DoResolve (BlockContext ec)
716 if (Expr == null) {
717 if (ec.ReturnType == TypeManager.void_type)
718 return true;
720 ec.Report.Error (126, loc,
721 "An object of a type convertible to `{0}' is required for the return statement",
722 TypeManager.CSharpName (ec.ReturnType));
723 return false;
726 if (ec.CurrentBlock.Toplevel.IsIterator) {
727 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
728 "statement to return a value, or yield break to end the iteration");
731 AnonymousExpression am = ec.CurrentAnonymousMethod;
732 if (am == null && ec.ReturnType == TypeManager.void_type) {
733 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
734 ec.GetSignatureForError ());
737 Expr = Expr.Resolve (ec);
738 if (Expr == null)
739 return false;
741 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
742 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
743 return true;
746 if (Expr.Type != ec.ReturnType) {
747 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
749 if (Expr == null) {
750 if (am != null) {
751 ec.Report.Error (1662, loc,
752 "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",
753 am.ContainerType, am.GetSignatureForError ());
755 return false;
759 return true;
762 protected override void DoEmit (EmitContext ec)
764 if (Expr != null) {
765 Expr.Emit (ec);
767 if (unwind_protect)
768 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
771 if (unwind_protect)
772 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
773 else
774 ec.ig.Emit (OpCodes.Ret);
777 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
779 if (Expr != null)
780 Expr.MutateHoistedGenericType (storey);
783 protected override void CloneTo (CloneContext clonectx, Statement t)
785 Return target = (Return) t;
786 // It's null for simple return;
787 if (Expr != null)
788 target.Expr = Expr.Clone (clonectx);
792 public class Goto : Statement {
793 string target;
794 LabeledStatement label;
795 bool unwind_protect;
797 public override bool Resolve (BlockContext ec)
799 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
800 ec.CurrentBranching.CurrentUsageVector.Goto ();
801 return true;
804 public Goto (string label, Location l)
806 loc = l;
807 target = label;
810 public string Target {
811 get { return target; }
814 public void SetResolvedTarget (LabeledStatement label)
816 this.label = label;
817 label.AddReference ();
820 protected override void CloneTo (CloneContext clonectx, Statement target)
822 // Nothing to clone
825 protected override void DoEmit (EmitContext ec)
827 if (label == null)
828 throw new InternalErrorException ("goto emitted before target resolved");
829 Label l = label.LabelTarget (ec);
830 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
833 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
838 public class LabeledStatement : Statement {
839 string name;
840 bool defined;
841 bool referenced;
842 Label label;
843 ILGenerator ig;
845 FlowBranching.UsageVector vectors;
847 public LabeledStatement (string name, Location l)
849 this.name = name;
850 this.loc = l;
853 public Label LabelTarget (EmitContext ec)
855 if (defined)
856 return label;
857 ig = ec.ig;
858 label = ec.ig.DefineLabel ();
859 defined = true;
861 return label;
864 public string Name {
865 get { return name; }
868 public bool IsDefined {
869 get { return defined; }
872 public bool HasBeenReferenced {
873 get { return referenced; }
876 public FlowBranching.UsageVector JumpOrigins {
877 get { return vectors; }
880 public void AddUsageVector (FlowBranching.UsageVector vector)
882 vector = vector.Clone ();
883 vector.Next = vectors;
884 vectors = vector;
887 protected override void CloneTo (CloneContext clonectx, Statement target)
889 // nothing to clone
892 public override bool Resolve (BlockContext ec)
894 // this flow-branching will be terminated when the surrounding block ends
895 ec.StartFlowBranching (this);
896 return true;
899 protected override void DoEmit (EmitContext ec)
901 if (ig != null && ig != ec.ig)
902 throw new InternalErrorException ("cannot happen");
903 LabelTarget (ec);
904 ec.ig.MarkLabel (label);
907 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
911 public void AddReference ()
913 referenced = true;
918 /// <summary>
919 /// `goto default' statement
920 /// </summary>
921 public class GotoDefault : Statement {
923 public GotoDefault (Location l)
925 loc = l;
928 protected override void CloneTo (CloneContext clonectx, Statement target)
930 // nothing to clone
933 public override bool Resolve (BlockContext ec)
935 ec.CurrentBranching.CurrentUsageVector.Goto ();
937 if (ec.Switch == null) {
938 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
939 return false;
942 if (!ec.Switch.GotDefault) {
943 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
944 return false;
947 return true;
950 protected override void DoEmit (EmitContext ec)
952 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
955 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
960 /// <summary>
961 /// `goto case' statement
962 /// </summary>
963 public class GotoCase : Statement {
964 Expression expr;
965 SwitchLabel sl;
967 public GotoCase (Expression e, Location l)
969 expr = e;
970 loc = l;
973 public override bool Resolve (BlockContext ec)
975 if (ec.Switch == null){
976 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
977 return false;
980 ec.CurrentBranching.CurrentUsageVector.Goto ();
982 expr = expr.Resolve (ec);
983 if (expr == null)
984 return false;
986 Constant c = expr as Constant;
987 if (c == null) {
988 ec.Report.Error (150, expr.Location, "A constant value is expected");
989 return false;
992 Type type = ec.Switch.SwitchType;
993 Constant res = c.TryReduce (ec, type, c.Location);
994 if (res == null) {
995 c.Error_ValueCannotBeConverted (ec, loc, type, true);
996 return false;
999 if (!Convert.ImplicitStandardConversionExists (c, type))
1000 ec.Report.Warning (469, 2, loc,
1001 "The `goto case' value is not implicitly convertible to type `{0}'",
1002 TypeManager.CSharpName (type));
1004 object val = res.GetValue ();
1005 if (val == null)
1006 val = SwitchLabel.NullStringCase;
1008 sl = (SwitchLabel) ec.Switch.Elements [val];
1010 if (sl == null){
1011 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1012 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1013 return false;
1016 return true;
1019 protected override void DoEmit (EmitContext ec)
1021 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1024 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1026 expr.MutateHoistedGenericType (storey);
1029 protected override void CloneTo (CloneContext clonectx, Statement t)
1031 GotoCase target = (GotoCase) t;
1033 target.expr = expr.Clone (clonectx);
1037 public class Throw : Statement {
1038 Expression expr;
1040 public Throw (Expression expr, Location l)
1042 this.expr = expr;
1043 loc = l;
1046 public override bool Resolve (BlockContext ec)
1048 if (expr == null) {
1049 ec.CurrentBranching.CurrentUsageVector.Goto ();
1050 return ec.CurrentBranching.CheckRethrow (loc);
1053 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1054 ec.CurrentBranching.CurrentUsageVector.Goto ();
1056 if (expr == null)
1057 return false;
1059 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1060 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1061 else
1062 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1064 return true;
1067 protected override void DoEmit (EmitContext ec)
1069 if (expr == null)
1070 ec.ig.Emit (OpCodes.Rethrow);
1071 else {
1072 expr.Emit (ec);
1074 ec.ig.Emit (OpCodes.Throw);
1078 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1080 if (expr != null)
1081 expr.MutateHoistedGenericType (storey);
1084 protected override void CloneTo (CloneContext clonectx, Statement t)
1086 Throw target = (Throw) t;
1088 if (expr != null)
1089 target.expr = expr.Clone (clonectx);
1093 public class Break : Statement {
1095 public Break (Location l)
1097 loc = l;
1100 bool unwind_protect;
1102 public override bool Resolve (BlockContext ec)
1104 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1105 ec.CurrentBranching.CurrentUsageVector.Goto ();
1106 return true;
1109 protected override void DoEmit (EmitContext ec)
1111 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1114 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1118 protected override void CloneTo (CloneContext clonectx, Statement t)
1120 // nothing needed
1124 public class Continue : Statement {
1126 public Continue (Location l)
1128 loc = l;
1131 bool unwind_protect;
1133 public override bool Resolve (BlockContext ec)
1135 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1136 ec.CurrentBranching.CurrentUsageVector.Goto ();
1137 return true;
1140 protected override void DoEmit (EmitContext ec)
1142 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1145 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1149 protected override void CloneTo (CloneContext clonectx, Statement t)
1151 // nothing needed.
1155 public interface ILocalVariable
1157 void Emit (EmitContext ec);
1158 void EmitAssign (EmitContext ec);
1159 void EmitAddressOf (EmitContext ec);
1162 public interface IKnownVariable {
1163 Block Block { get; }
1164 Location Location { get; }
1168 // The information about a user-perceived local variable
1170 public class LocalInfo : IKnownVariable, ILocalVariable {
1171 public readonly FullNamedExpression Type;
1173 public Type VariableType;
1174 public readonly string Name;
1175 public readonly Location Location;
1176 public readonly Block Block;
1178 public VariableInfo VariableInfo;
1179 HoistedVariable hoisted_variant;
1181 [Flags]
1182 enum Flags : byte {
1183 Used = 1,
1184 ReadOnly = 2,
1185 Pinned = 4,
1186 IsThis = 8,
1187 AddressTaken = 32,
1188 CompilerGenerated = 64,
1189 IsConstant = 128
1192 public enum ReadOnlyContext: byte {
1193 Using,
1194 Foreach,
1195 Fixed
1198 Flags flags;
1199 ReadOnlyContext ro_context;
1200 LocalBuilder builder;
1202 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1204 Type = type;
1205 Name = name;
1206 Block = block;
1207 Location = l;
1210 public LocalInfo (DeclSpace ds, Block block, Location l)
1212 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1213 Block = block;
1214 Location = l;
1217 public void ResolveVariable (EmitContext ec)
1219 if (HoistedVariant != null)
1220 return;
1222 if (builder == null) {
1223 if (Pinned)
1225 // This is needed to compile on both .NET 1.x and .NET 2.x
1226 // the later introduced `DeclareLocal (Type t, bool pinned)'
1228 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1229 else
1230 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1234 public void Emit (EmitContext ec)
1236 ec.ig.Emit (OpCodes.Ldloc, builder);
1239 public void EmitAssign (EmitContext ec)
1241 ec.ig.Emit (OpCodes.Stloc, builder);
1244 public void EmitAddressOf (EmitContext ec)
1246 ec.ig.Emit (OpCodes.Ldloca, builder);
1249 public void EmitSymbolInfo (EmitContext ec)
1251 if (builder != null)
1252 ec.DefineLocalVariable (Name, builder);
1256 // Hoisted local variable variant
1258 public HoistedVariable HoistedVariant {
1259 get {
1260 return hoisted_variant;
1262 set {
1263 hoisted_variant = value;
1267 public bool IsThisAssigned (BlockContext ec, Block block)
1269 if (VariableInfo == null)
1270 throw new Exception ();
1272 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1273 return true;
1275 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1278 public bool IsAssigned (BlockContext ec)
1280 if (VariableInfo == null)
1281 throw new Exception ();
1283 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1286 public bool Resolve (ResolveContext ec)
1288 if (VariableType != null)
1289 return true;
1291 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1292 if (texpr == null)
1293 return false;
1295 VariableType = texpr.Type;
1297 if (TypeManager.IsGenericParameter (VariableType))
1298 return true;
1300 if (VariableType.IsAbstract && VariableType.IsSealed) {
1301 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1302 return false;
1305 if (VariableType.IsPointer && !ec.IsUnsafe)
1306 Expression.UnsafeError (ec, Location);
1308 return true;
1311 public bool IsConstant {
1312 get { return (flags & Flags.IsConstant) != 0; }
1313 set { flags |= Flags.IsConstant; }
1316 public bool AddressTaken {
1317 get { return (flags & Flags.AddressTaken) != 0; }
1318 set { flags |= Flags.AddressTaken; }
1321 public bool CompilerGenerated {
1322 get { return (flags & Flags.CompilerGenerated) != 0; }
1323 set { flags |= Flags.CompilerGenerated; }
1326 public override string ToString ()
1328 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1329 Name, Type, VariableInfo, Location);
1332 public bool Used {
1333 get { return (flags & Flags.Used) != 0; }
1334 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1337 public bool ReadOnly {
1338 get { return (flags & Flags.ReadOnly) != 0; }
1341 public void SetReadOnlyContext (ReadOnlyContext context)
1343 flags |= Flags.ReadOnly;
1344 ro_context = context;
1347 public string GetReadOnlyContext ()
1349 if (!ReadOnly)
1350 throw new InternalErrorException ("Variable is not readonly");
1352 switch (ro_context) {
1353 case ReadOnlyContext.Fixed:
1354 return "fixed variable";
1355 case ReadOnlyContext.Foreach:
1356 return "foreach iteration variable";
1357 case ReadOnlyContext.Using:
1358 return "using variable";
1360 throw new NotImplementedException ();
1364 // Whether the variable is pinned, if Pinned the variable has been
1365 // allocated in a pinned slot with DeclareLocal.
1367 public bool Pinned {
1368 get { return (flags & Flags.Pinned) != 0; }
1369 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1372 public bool IsThis {
1373 get { return (flags & Flags.IsThis) != 0; }
1374 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1377 Block IKnownVariable.Block {
1378 get { return Block; }
1381 Location IKnownVariable.Location {
1382 get { return Location; }
1385 public LocalInfo Clone (CloneContext clonectx)
1388 // Variables in anonymous block are not resolved yet
1390 if (VariableType == null)
1391 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1394 // Variables in method block are resolved
1396 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1397 li.VariableType = VariableType;
1398 return li;
1402 /// <summary>
1403 /// Block represents a C# block.
1404 /// </summary>
1406 /// <remarks>
1407 /// This class is used in a number of places: either to represent
1408 /// explicit blocks that the programmer places or implicit blocks.
1410 /// Implicit blocks are used as labels or to introduce variable
1411 /// declarations.
1413 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1414 /// they contain extra information that is not necessary on normal blocks.
1415 /// </remarks>
1416 public class Block : Statement {
1417 public Block Parent;
1418 public Location StartLocation;
1419 public Location EndLocation = Location.Null;
1421 public ExplicitBlock Explicit;
1422 public ToplevelBlock Toplevel; // TODO: Use Explicit
1424 [Flags]
1425 public enum Flags
1427 Unchecked = 1,
1428 BlockUsed = 2,
1429 VariablesInitialized = 4,
1430 HasRet = 8,
1431 Unsafe = 16,
1432 IsIterator = 32,
1433 HasCapturedVariable = 64,
1434 HasCapturedThis = 1 << 7,
1435 IsExpressionTree = 1 << 8
1438 protected Flags flags;
1440 public bool Unchecked {
1441 get { return (flags & Flags.Unchecked) != 0; }
1442 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1445 public bool Unsafe {
1446 get { return (flags & Flags.Unsafe) != 0; }
1447 set { flags |= Flags.Unsafe; }
1451 // The statements in this block
1453 protected ArrayList statements;
1456 // An array of Blocks. We keep track of children just
1457 // to generate the local variable declarations.
1459 // Statements and child statements are handled through the
1460 // statements.
1462 ArrayList children;
1465 // Labels. (label, block) pairs.
1467 protected HybridDictionary labels;
1470 // Keeps track of (name, type) pairs
1472 IDictionary variables;
1475 // Keeps track of constants
1476 HybridDictionary constants;
1479 // Temporary variables.
1481 ArrayList temporary_variables;
1484 // If this is a switch section, the enclosing switch block.
1486 Block switch_block;
1488 protected ArrayList scope_initializers;
1490 ArrayList anonymous_children;
1492 protected static int id;
1494 int this_id;
1496 int assignable_slots;
1497 bool unreachable_shown;
1498 bool unreachable;
1500 public Block (Block parent)
1501 : this (parent, (Flags) 0, Location.Null, Location.Null)
1504 public Block (Block parent, Flags flags)
1505 : this (parent, flags, Location.Null, Location.Null)
1508 public Block (Block parent, Location start, Location end)
1509 : this (parent, (Flags) 0, start, end)
1513 // Useful when TopLevel block is downgraded to normal block
1515 public Block (ToplevelBlock parent, ToplevelBlock source)
1516 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1518 statements = source.statements;
1519 children = source.children;
1520 labels = source.labels;
1521 variables = source.variables;
1522 constants = source.constants;
1523 switch_block = source.switch_block;
1526 public Block (Block parent, Flags flags, Location start, Location end)
1528 if (parent != null) {
1529 parent.AddChild (this);
1531 // the appropriate constructors will fixup these fields
1532 Toplevel = parent.Toplevel;
1533 Explicit = parent.Explicit;
1536 this.Parent = parent;
1537 this.flags = flags;
1538 this.StartLocation = start;
1539 this.EndLocation = end;
1540 this.loc = start;
1541 this_id = id++;
1542 statements = new ArrayList (4);
1545 public Block CreateSwitchBlock (Location start)
1547 // FIXME: should this be implicit?
1548 Block new_block = new ExplicitBlock (this, start, start);
1549 new_block.switch_block = this;
1550 return new_block;
1553 public int ID {
1554 get { return this_id; }
1557 public IDictionary Variables {
1558 get {
1559 if (variables == null)
1560 variables = new ListDictionary ();
1561 return variables;
1565 void AddChild (Block b)
1567 if (children == null)
1568 children = new ArrayList (1);
1570 children.Add (b);
1573 public void SetEndLocation (Location loc)
1575 EndLocation = loc;
1578 protected void Error_158 (string name, Location loc)
1580 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1581 "by the same name in a contained scope", name);
1584 /// <summary>
1585 /// Adds a label to the current block.
1586 /// </summary>
1588 /// <returns>
1589 /// false if the name already exists in this block. true
1590 /// otherwise.
1591 /// </returns>
1593 public bool AddLabel (LabeledStatement target)
1595 if (switch_block != null)
1596 return switch_block.AddLabel (target);
1598 string name = target.Name;
1600 Block cur = this;
1601 while (cur != null) {
1602 LabeledStatement s = cur.DoLookupLabel (name);
1603 if (s != null) {
1604 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1605 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1606 return false;
1609 if (this == Explicit)
1610 break;
1612 cur = cur.Parent;
1615 while (cur != null) {
1616 if (cur.DoLookupLabel (name) != null) {
1617 Error_158 (name, target.loc);
1618 return false;
1621 if (children != null) {
1622 foreach (Block b in children) {
1623 LabeledStatement s = b.DoLookupLabel (name);
1624 if (s == null)
1625 continue;
1627 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1628 Error_158 (name, target.loc);
1629 return false;
1633 cur = cur.Parent;
1636 Toplevel.CheckError158 (name, target.loc);
1638 if (labels == null)
1639 labels = new HybridDictionary();
1641 labels.Add (name, target);
1642 return true;
1645 public LabeledStatement LookupLabel (string name)
1647 LabeledStatement s = DoLookupLabel (name);
1648 if (s != null)
1649 return s;
1651 if (children == null)
1652 return null;
1654 foreach (Block child in children) {
1655 if (Explicit != child.Explicit)
1656 continue;
1658 s = child.LookupLabel (name);
1659 if (s != null)
1660 return s;
1663 return null;
1666 LabeledStatement DoLookupLabel (string name)
1668 if (switch_block != null)
1669 return switch_block.LookupLabel (name);
1671 if (labels != null)
1672 if (labels.Contains (name))
1673 return ((LabeledStatement) labels [name]);
1675 return null;
1678 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1680 Block b = this;
1681 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1682 while (kvi == null) {
1683 b = b.Explicit.Parent;
1684 if (b == null)
1685 return true;
1686 kvi = b.Explicit.GetKnownVariable (name);
1689 if (kvi.Block == b)
1690 return true;
1692 // Is kvi.Block nested inside 'b'
1693 if (b.Explicit != kvi.Block.Explicit) {
1695 // If a variable by the same name it defined in a nested block of this
1696 // block, we violate the invariant meaning in a block.
1698 if (b == this) {
1699 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1700 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1701 return false;
1705 // It's ok if the definition is in a nested subblock of b, but not
1706 // nested inside this block -- a definition in a sibling block
1707 // should not affect us.
1709 return true;
1713 // Block 'b' and kvi.Block are the same textual block.
1714 // However, different variables are extant.
1716 // Check if the variable is in scope in both blocks. We use
1717 // an indirect check that depends on AddVariable doing its
1718 // part in maintaining the invariant-meaning-in-block property.
1720 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1721 return true;
1723 if (this is ToplevelBlock) {
1724 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1725 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1726 return false;
1730 // Even though we detected the error when the name is used, we
1731 // treat it as if the variable declaration was in error.
1733 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1734 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1735 return false;
1738 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1740 LocalInfo vi = GetLocalInfo (name);
1741 if (vi != null) {
1742 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1743 if (Explicit == vi.Block.Explicit) {
1744 Error_AlreadyDeclared (l, name, null);
1745 } else {
1746 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1747 "parent or current" : "parent");
1749 return false;
1752 if (block != null) {
1753 Expression e = block.GetParameterReference (name, Location.Null);
1754 if (e != null) {
1755 ParameterReference pr = e as ParameterReference;
1756 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1757 Error_AlreadyDeclared (loc, name);
1758 else
1759 Error_AlreadyDeclared (loc, name, "parent or current");
1760 return false;
1764 return true;
1767 public LocalInfo AddVariable (Expression type, string name, Location l)
1769 if (!CheckParentConflictName (Toplevel, name, l))
1770 return null;
1772 if (Toplevel.GenericMethod != null) {
1773 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1774 if (tp.Name == name) {
1775 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1776 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1777 return null;
1782 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1783 if (kvi != null) {
1784 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1785 Error_AlreadyDeclared (l, name, "child");
1786 return null;
1789 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1790 AddVariable (vi);
1792 if ((flags & Flags.VariablesInitialized) != 0)
1793 throw new InternalErrorException ("block has already been resolved");
1795 return vi;
1798 protected virtual void AddVariable (LocalInfo li)
1800 Variables.Add (li.Name, li);
1801 Explicit.AddKnownVariable (li.Name, li);
1804 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1806 if (reason == null) {
1807 Error_AlreadyDeclared (loc, var);
1808 return;
1811 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1812 "in this scope because it would give a different meaning " +
1813 "to `{0}', which is already used in a `{1}' scope " +
1814 "to denote something else", var, reason);
1817 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1819 Toplevel.Report.Error (128, loc,
1820 "A local variable named `{0}' is already defined in this scope", name);
1823 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1825 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1826 name, conflict);
1829 public bool AddConstant (Expression type, string name, Expression value, Location l)
1831 if (AddVariable (type, name, l) == null)
1832 return false;
1834 if (constants == null)
1835 constants = new HybridDictionary();
1837 constants.Add (name, value);
1839 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1840 Use ();
1841 return true;
1844 static int next_temp_id = 0;
1846 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1848 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1850 if (temporary_variables == null)
1851 temporary_variables = new ArrayList ();
1853 int id = ++next_temp_id;
1854 string name = "$s_" + id.ToString ();
1856 LocalInfo li = new LocalInfo (te, name, this, loc);
1857 li.CompilerGenerated = true;
1858 temporary_variables.Add (li);
1859 return li;
1862 public LocalInfo GetLocalInfo (string name)
1864 LocalInfo ret;
1865 for (Block b = this; b != null; b = b.Parent) {
1866 if (b.variables != null) {
1867 ret = (LocalInfo) b.variables [name];
1868 if (ret != null)
1869 return ret;
1873 return null;
1876 public Expression GetVariableType (string name)
1878 LocalInfo vi = GetLocalInfo (name);
1879 return vi == null ? null : vi.Type;
1882 public Expression GetConstantExpression (string name)
1884 for (Block b = this; b != null; b = b.Parent) {
1885 if (b.constants != null) {
1886 Expression ret = b.constants [name] as Expression;
1887 if (ret != null)
1888 return ret;
1891 return null;
1895 // It should be used by expressions which require to
1896 // register a statement during resolve process.
1898 public void AddScopeStatement (Statement s)
1900 if (scope_initializers == null)
1901 scope_initializers = new ArrayList ();
1903 scope_initializers.Add (s);
1906 public void AddStatement (Statement s)
1908 statements.Add (s);
1909 flags |= Flags.BlockUsed;
1912 public bool Used {
1913 get { return (flags & Flags.BlockUsed) != 0; }
1916 public void Use ()
1918 flags |= Flags.BlockUsed;
1921 public bool HasRet {
1922 get { return (flags & Flags.HasRet) != 0; }
1925 public int AssignableSlots {
1926 get {
1927 // TODO: Re-enable
1928 // if ((flags & Flags.VariablesInitialized) == 0)
1929 // throw new Exception ("Variables have not been initialized yet");
1930 return assignable_slots;
1934 public ArrayList AnonymousChildren {
1935 get { return anonymous_children; }
1938 public void AddAnonymousChild (ToplevelBlock b)
1940 if (anonymous_children == null)
1941 anonymous_children = new ArrayList ();
1943 anonymous_children.Add (b);
1946 void DoResolveConstants (BlockContext ec)
1948 if (constants == null)
1949 return;
1951 if (variables == null)
1952 throw new InternalErrorException ("cannot happen");
1954 foreach (DictionaryEntry de in variables) {
1955 string name = (string) de.Key;
1956 LocalInfo vi = (LocalInfo) de.Value;
1957 Type variable_type = vi.VariableType;
1959 if (variable_type == null) {
1960 if (vi.Type is VarExpr)
1961 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1963 continue;
1966 Expression cv = (Expression) constants [name];
1967 if (cv == null)
1968 continue;
1970 // Don't let 'const int Foo = Foo;' succeed.
1971 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1972 // which in turn causes the 'must be constant' error to be triggered.
1973 constants.Remove (name);
1975 if (!Const.IsConstantTypeValid (variable_type)) {
1976 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1977 continue;
1980 ec.CurrentBlock = this;
1981 Expression e;
1982 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1983 e = cv.Resolve (ec);
1985 if (e == null)
1986 continue;
1988 Constant ce = e as Constant;
1989 if (ce == null) {
1990 Const.Error_ExpressionMustBeConstant (vi.Location, name, ec.Report);
1991 continue;
1994 e = ce.ConvertImplicitly (variable_type);
1995 if (e == null) {
1996 if (TypeManager.IsReferenceType (variable_type))
1997 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name, ec.Report);
1998 else
1999 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2000 continue;
2003 constants.Add (name, e);
2004 vi.IsConstant = true;
2008 protected void ResolveMeta (BlockContext ec, int offset)
2010 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2012 // If some parent block was unsafe, we remain unsafe even if this block
2013 // isn't explicitly marked as such.
2014 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2015 flags |= Flags.VariablesInitialized;
2017 if (variables != null) {
2018 foreach (LocalInfo li in variables.Values) {
2019 if (!li.Resolve (ec))
2020 continue;
2021 li.VariableInfo = new VariableInfo (li, offset);
2022 offset += li.VariableInfo.Length;
2025 assignable_slots = offset;
2027 DoResolveConstants (ec);
2029 if (children == null)
2030 return;
2031 foreach (Block b in children)
2032 b.ResolveMeta (ec, offset);
2037 // Emits the local variable declarations for a block
2039 public virtual void EmitMeta (EmitContext ec)
2041 if (variables != null){
2042 foreach (LocalInfo vi in variables.Values)
2043 vi.ResolveVariable (ec);
2046 if (temporary_variables != null) {
2047 for (int i = 0; i < temporary_variables.Count; i++)
2048 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2051 if (children != null) {
2052 for (int i = 0; i < children.Count; i++)
2053 ((Block)children[i]).EmitMeta(ec);
2057 void UsageWarning (BlockContext ec)
2059 if (variables == null || ec.Report.WarningLevel < 3)
2060 return;
2062 foreach (DictionaryEntry de in variables) {
2063 LocalInfo vi = (LocalInfo) de.Value;
2065 if (!vi.Used) {
2066 string name = (string) de.Key;
2068 // vi.VariableInfo can be null for 'catch' variables
2069 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2070 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2071 else
2072 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2077 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2079 Statement body;
2081 // Some statements are wrapped by a Block. Since
2082 // others' internal could be changed, here I treat
2083 // them as possibly wrapped by Block equally.
2084 Block b = s as Block;
2085 if (b != null && b.statements.Count == 1)
2086 s = (Statement) b.statements [0];
2088 if (s is Lock)
2089 body = ((Lock) s).Statement;
2090 else if (s is For)
2091 body = ((For) s).Statement;
2092 else if (s is Foreach)
2093 body = ((Foreach) s).Statement;
2094 else if (s is While)
2095 body = ((While) s).Statement;
2096 else if (s is Fixed)
2097 body = ((Fixed) s).Statement;
2098 else if (s is Using)
2099 body = ((Using) s).EmbeddedStatement;
2100 else if (s is UsingTemporary)
2101 body = ((UsingTemporary) s).Statement;
2102 else
2103 return;
2105 if (body == null || body is EmptyStatement)
2106 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2109 public override bool Resolve (BlockContext ec)
2111 Block prev_block = ec.CurrentBlock;
2112 bool ok = true;
2114 int errors = ec.Report.Errors;
2116 ec.CurrentBlock = this;
2117 ec.StartFlowBranching (this);
2119 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2122 // Compiler generated scope statements
2124 if (scope_initializers != null) {
2125 foreach (Statement s in scope_initializers)
2126 s.Resolve (ec);
2130 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2131 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2132 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2133 // responsible for handling the situation.
2135 int statement_count = statements.Count;
2136 for (int ix = 0; ix < statement_count; ix++){
2137 Statement s = (Statement) statements [ix];
2138 // Check possible empty statement (CS0642)
2139 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2140 statements [ix + 1] is ExplicitBlock)
2141 CheckPossibleMistakenEmptyStatement (ec, s);
2144 // Warn if we detect unreachable code.
2146 if (unreachable) {
2147 if (s is EmptyStatement)
2148 continue;
2150 if (!unreachable_shown && !(s is LabeledStatement)) {
2151 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2152 unreachable_shown = true;
2155 Block c_block = s as Block;
2156 if (c_block != null)
2157 c_block.unreachable = c_block.unreachable_shown = true;
2161 // Note that we're not using ResolveUnreachable() for unreachable
2162 // statements here. ResolveUnreachable() creates a temporary
2163 // flow branching and kills it afterwards. This leads to problems
2164 // if you have two unreachable statements where the first one
2165 // assigns a variable and the second one tries to access it.
2168 if (!s.Resolve (ec)) {
2169 ok = false;
2170 if (ec.IsInProbingMode)
2171 break;
2173 statements [ix] = EmptyStatement.Value;
2174 continue;
2177 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2178 statements [ix] = EmptyStatement.Value;
2180 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2181 if (unreachable && s is LabeledStatement)
2182 throw new InternalErrorException ("should not happen");
2185 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2186 ec.CurrentBranching, statement_count);
2188 while (ec.CurrentBranching is FlowBranchingLabeled)
2189 ec.EndFlowBranching ();
2191 bool flow_unreachable = ec.EndFlowBranching ();
2193 ec.CurrentBlock = prev_block;
2195 if (flow_unreachable)
2196 flags |= Flags.HasRet;
2198 // If we're a non-static `struct' constructor which doesn't have an
2199 // initializer, then we must initialize all of the struct's fields.
2200 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2201 ok = false;
2203 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2204 foreach (LabeledStatement label in labels.Values)
2205 if (!label.HasBeenReferenced)
2206 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2209 if (ok && errors == ec.Report.Errors)
2210 UsageWarning (ec);
2212 return ok;
2215 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2217 unreachable_shown = true;
2218 unreachable = true;
2220 if (warn)
2221 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2223 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2224 bool ok = Resolve (ec);
2225 ec.KillFlowBranching ();
2227 return ok;
2230 protected override void DoEmit (EmitContext ec)
2232 for (int ix = 0; ix < statements.Count; ix++){
2233 Statement s = (Statement) statements [ix];
2234 s.Emit (ec);
2238 public override void Emit (EmitContext ec)
2240 if (scope_initializers != null)
2241 EmitScopeInitializers (ec);
2243 ec.Mark (StartLocation);
2244 DoEmit (ec);
2246 if (SymbolWriter.HasSymbolWriter)
2247 EmitSymbolInfo (ec);
2250 protected void EmitScopeInitializers (EmitContext ec)
2252 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2254 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2255 foreach (Statement s in scope_initializers)
2256 s.Emit (ec);
2259 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2262 protected virtual void EmitSymbolInfo (EmitContext ec)
2264 if (variables != null) {
2265 foreach (LocalInfo vi in variables.Values) {
2266 vi.EmitSymbolInfo (ec);
2271 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2273 MutateVariables (storey);
2275 if (scope_initializers != null) {
2276 foreach (Statement s in scope_initializers)
2277 s.MutateHoistedGenericType (storey);
2280 foreach (Statement s in statements)
2281 s.MutateHoistedGenericType (storey);
2284 void MutateVariables (AnonymousMethodStorey storey)
2286 if (variables != null) {
2287 foreach (LocalInfo vi in variables.Values) {
2288 vi.VariableType = storey.MutateType (vi.VariableType);
2292 if (temporary_variables != null) {
2293 foreach (LocalInfo vi in temporary_variables)
2294 vi.VariableType = storey.MutateType (vi.VariableType);
2298 public override string ToString ()
2300 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2303 protected override void CloneTo (CloneContext clonectx, Statement t)
2305 Block target = (Block) t;
2307 clonectx.AddBlockMap (this, target);
2309 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2310 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2311 if (Parent != null)
2312 target.Parent = clonectx.RemapBlockCopy (Parent);
2314 if (variables != null){
2315 target.variables = new Hashtable ();
2317 foreach (DictionaryEntry de in variables){
2318 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2319 target.variables [de.Key] = newlocal;
2320 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2324 target.statements = new ArrayList (statements.Count);
2325 foreach (Statement s in statements)
2326 target.statements.Add (s.Clone (clonectx));
2328 if (target.children != null){
2329 target.children = new ArrayList (children.Count);
2330 foreach (Block b in children){
2331 target.children.Add (clonectx.LookupBlock (b));
2336 // TODO: labels, switch_block, constants (?), anonymous_children
2341 public class ExplicitBlock : Block {
2342 HybridDictionary known_variables;
2343 protected AnonymousMethodStorey am_storey;
2345 public ExplicitBlock (Block parent, Location start, Location end)
2346 : this (parent, (Flags) 0, start, end)
2350 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2351 : base (parent, flags, start, end)
2353 this.Explicit = this;
2356 // <summary>
2357 // Marks a variable with name @name as being used in this or a child block.
2358 // If a variable name has been used in a child block, it's illegal to
2359 // declare a variable with the same name in the current block.
2360 // </summary>
2361 internal void AddKnownVariable (string name, IKnownVariable info)
2363 if (known_variables == null)
2364 known_variables = new HybridDictionary();
2366 known_variables [name] = info;
2368 if (Parent != null)
2369 Parent.Explicit.AddKnownVariable (name, info);
2372 public AnonymousMethodStorey AnonymousMethodStorey {
2373 get { return am_storey; }
2377 // Creates anonymous method storey in current block
2379 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2382 // When referencing a variable in iterator storey from children anonymous method
2384 if (Toplevel.am_storey is IteratorStorey) {
2385 return Toplevel.am_storey;
2389 // An iterator has only 1 storey block
2391 if (ec.CurrentIterator != null)
2392 return ec.CurrentIterator.Storey;
2394 if (am_storey == null) {
2395 MemberBase mc = ec.MemberContext as MemberBase;
2396 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2399 // Creates anonymous method storey for this block
2401 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2404 return am_storey;
2407 public override void Emit (EmitContext ec)
2409 if (am_storey != null)
2410 am_storey.EmitStoreyInstantiation (ec);
2412 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2413 if (emit_debug_info)
2414 ec.BeginScope ();
2416 base.Emit (ec);
2418 if (emit_debug_info)
2419 ec.EndScope ();
2422 public override void EmitMeta (EmitContext ec)
2425 // Creates anonymous method storey
2427 if (am_storey != null) {
2428 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2430 // Creates parent storey reference when hoisted this is accessible
2432 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2433 ExplicitBlock parent = Toplevel.Parent.Explicit;
2436 // Hoisted this exists in top-level parent storey only
2438 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2439 parent = parent.Parent.Explicit;
2441 am_storey.AddParentStoreyReference (parent.am_storey);
2444 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2447 am_storey.DefineType ();
2448 am_storey.ResolveType ();
2449 am_storey.Define ();
2450 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2452 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2453 if (ref_blocks != null) {
2454 foreach (ExplicitBlock ref_block in ref_blocks) {
2455 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2456 if (b.am_storey != null) {
2457 b.am_storey.AddParentStoreyReference (am_storey);
2459 // Stop propagation inside same top block
2460 if (b.Toplevel == Toplevel)
2461 break;
2463 b = b.Toplevel;
2465 b.HasCapturedVariable = true;
2471 base.EmitMeta (ec);
2474 internal IKnownVariable GetKnownVariable (string name)
2476 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2479 public bool HasCapturedThis
2481 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2482 get { return (flags & Flags.HasCapturedThis) != 0; }
2485 public bool HasCapturedVariable
2487 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2488 get { return (flags & Flags.HasCapturedVariable) != 0; }
2491 protected override void CloneTo (CloneContext clonectx, Statement t)
2493 ExplicitBlock target = (ExplicitBlock) t;
2494 target.known_variables = null;
2495 base.CloneTo (clonectx, t);
2499 public class ToplevelParameterInfo : IKnownVariable {
2500 public readonly ToplevelBlock Block;
2501 public readonly int Index;
2502 public VariableInfo VariableInfo;
2504 Block IKnownVariable.Block {
2505 get { return Block; }
2507 public Parameter Parameter {
2508 get { return Block.Parameters [Index]; }
2511 public Type ParameterType {
2512 get { return Block.Parameters.Types [Index]; }
2515 public Location Location {
2516 get { return Parameter.Location; }
2519 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2521 this.Block = block;
2522 this.Index = idx;
2527 // A toplevel block contains extra information, the split is done
2528 // only to separate information that would otherwise bloat the more
2529 // lightweight Block.
2531 // In particular, this was introduced when the support for Anonymous
2532 // Methods was implemented.
2534 public class ToplevelBlock : ExplicitBlock
2537 // Block is converted to an expression
2539 sealed class BlockScopeExpression : Expression
2541 Expression child;
2542 readonly ToplevelBlock block;
2544 public BlockScopeExpression (Expression child, ToplevelBlock block)
2546 this.child = child;
2547 this.block = block;
2550 public override Expression CreateExpressionTree (ResolveContext ec)
2552 throw new NotSupportedException ();
2555 public override Expression DoResolve (ResolveContext ec)
2557 if (child == null)
2558 return null;
2560 child = child.Resolve (ec);
2561 if (child == null)
2562 return null;
2564 eclass = child.eclass;
2565 type = child.Type;
2566 return this;
2569 public override void Emit (EmitContext ec)
2571 block.EmitMeta (ec);
2572 block.EmitScopeInitializers (ec);
2573 child.Emit (ec);
2576 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2578 type = storey.MutateType (type);
2579 child.MutateHoistedGenericType (storey);
2580 block.MutateHoistedGenericType (storey);
2584 GenericMethod generic;
2585 protected ParametersCompiled parameters;
2586 ToplevelParameterInfo[] parameter_info;
2587 LocalInfo this_variable;
2588 bool resolved;
2589 bool unreachable;
2590 CompilerContext compiler;
2592 public HoistedVariable HoistedThisVariable;
2594 public bool Resolved {
2595 get {
2596 return resolved;
2601 // The parameters for the block.
2603 public ParametersCompiled Parameters {
2604 get { return parameters; }
2607 public Report Report {
2608 get { return compiler.Report; }
2611 public GenericMethod GenericMethod {
2612 get { return generic; }
2615 public ToplevelBlock Container {
2616 get { return Parent == null ? null : Parent.Toplevel; }
2619 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2620 this (ctx, parent, (Flags) 0, parameters, start)
2624 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2625 this (ctx, parent, parameters, start)
2627 this.generic = generic;
2630 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2631 this (ctx, null, (Flags) 0, parameters, start)
2635 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2636 this (ctx, null, flags, parameters, start)
2640 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2641 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2642 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2643 base (null, flags, start, Location.Null)
2645 this.compiler = ctx;
2646 this.Toplevel = this;
2648 this.parameters = parameters;
2649 this.Parent = parent;
2650 if (parent != null)
2651 parent.AddAnonymousChild (this);
2653 if (!this.parameters.IsEmpty)
2654 ProcessParameters ();
2657 public ToplevelBlock (CompilerContext ctx, Location loc)
2658 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2662 protected override void CloneTo (CloneContext clonectx, Statement t)
2664 ToplevelBlock target = (ToplevelBlock) t;
2665 base.CloneTo (clonectx, t);
2667 if (parameters.Count != 0)
2668 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2669 for (int i = 0; i < parameters.Count; ++i)
2670 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2673 public bool CheckError158 (string name, Location loc)
2675 if (AnonymousChildren != null) {
2676 foreach (ToplevelBlock child in AnonymousChildren) {
2677 if (!child.CheckError158 (name, loc))
2678 return false;
2682 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2683 if (!c.DoCheckError158 (name, loc))
2684 return false;
2687 return true;
2690 void ProcessParameters ()
2692 int n = parameters.Count;
2693 parameter_info = new ToplevelParameterInfo [n];
2694 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2695 for (int i = 0; i < n; ++i) {
2696 parameter_info [i] = new ToplevelParameterInfo (this, i);
2698 Parameter p = parameters [i];
2699 if (p == null)
2700 continue;
2702 string name = p.Name;
2703 if (CheckParentConflictName (top_parent, name, loc))
2704 AddKnownVariable (name, parameter_info [i]);
2707 // mark this block as "used" so that we create local declarations in a sub-block
2708 // FIXME: This appears to uncover a lot of bugs
2709 //this.Use ();
2712 bool DoCheckError158 (string name, Location loc)
2714 LabeledStatement s = LookupLabel (name);
2715 if (s != null) {
2716 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2717 Error_158 (name, loc);
2718 return false;
2721 return true;
2724 public override Expression CreateExpressionTree (ResolveContext ec)
2726 if (statements.Count == 1) {
2727 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2728 if (scope_initializers != null)
2729 expr = new BlockScopeExpression (expr, this);
2731 return expr;
2734 return base.CreateExpressionTree (ec);
2738 // Reformats this block to be top-level iterator block
2740 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2742 IsIterator = true;
2744 // Creates block with original statements
2745 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2747 source.statements = new ArrayList (1);
2748 source.AddStatement (new Return (iterator, iterator.Location));
2749 source.IsIterator = false;
2751 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2752 source.am_storey = iterator_storey;
2753 return iterator_storey;
2757 // Returns a parameter reference expression for the given name,
2758 // or null if there is no such parameter
2760 public Expression GetParameterReference (string name, Location loc)
2762 for (ToplevelBlock t = this; t != null; t = t.Container) {
2763 Expression expr = t.GetParameterReferenceExpression (name, loc);
2764 if (expr != null)
2765 return expr;
2768 return null;
2771 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2773 int idx = parameters.GetParameterIndexByName (name);
2774 return idx < 0 ?
2775 null : new ParameterReference (parameter_info [idx], loc);
2778 // <summary>
2779 // Returns the "this" instance variable of this block.
2780 // See AddThisVariable() for more information.
2781 // </summary>
2782 public LocalInfo ThisVariable {
2783 get { return this_variable; }
2786 // <summary>
2787 // This is used by non-static `struct' constructors which do not have an
2788 // initializer - in this case, the constructor must initialize all of the
2789 // struct's fields. To do this, we add a "this" variable and use the flow
2790 // analysis code to ensure that it's been fully initialized before control
2791 // leaves the constructor.
2792 // </summary>
2793 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2795 if (this_variable == null) {
2796 this_variable = new LocalInfo (ds, this, l);
2797 this_variable.Used = true;
2798 this_variable.IsThis = true;
2800 Variables.Add ("this", this_variable);
2803 return this_variable;
2806 public bool IsIterator {
2807 get { return (flags & Flags.IsIterator) != 0; }
2808 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2812 // Block has been converted to expression tree
2814 public bool IsExpressionTree {
2815 get { return (flags & Flags.IsExpressionTree) != 0; }
2818 public bool IsThisAssigned (BlockContext ec)
2820 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2823 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2825 if (resolved)
2826 return true;
2828 resolved = true;
2830 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2831 flags |= Flags.IsExpressionTree;
2833 try {
2834 if (!ResolveMeta (rc, ip))
2835 return false;
2837 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2838 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2840 if (!Resolve (rc))
2841 return false;
2843 unreachable = top_level.End ();
2845 } catch (Exception) {
2846 #if PRODUCTION
2847 if (rc.CurrentBlock != null) {
2848 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2849 } else {
2850 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2852 #endif
2853 throw;
2856 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2857 if (rc.CurrentAnonymousMethod == null) {
2858 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2859 return false;
2860 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2861 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2862 rc.CurrentAnonymousMethod.GetSignatureForError ());
2863 return false;
2867 return true;
2870 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2872 int errors = ec.Report.Errors;
2873 int orig_count = parameters.Count;
2875 if (ip != null)
2876 parameters = ip;
2878 // Assert: orig_count != parameter.Count => orig_count == 0
2879 if (orig_count != 0 && orig_count != parameters.Count)
2880 throw new InternalErrorException ("parameter information mismatch");
2882 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2884 for (int i = 0; i < orig_count; ++i) {
2885 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2887 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2888 continue;
2890 VariableInfo vi = new VariableInfo (ip, i, offset);
2891 parameter_info [i].VariableInfo = vi;
2892 offset += vi.Length;
2895 ResolveMeta (ec, offset);
2897 return ec.Report.Errors == errors;
2900 // <summary>
2901 // Check whether all `out' parameters have been assigned.
2902 // </summary>
2903 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2905 if (vector.IsUnreachable)
2906 return;
2908 int n = parameter_info == null ? 0 : parameter_info.Length;
2910 for (int i = 0; i < n; i++) {
2911 VariableInfo var = parameter_info [i].VariableInfo;
2913 if (var == null)
2914 continue;
2916 if (vector.IsAssigned (var, false))
2917 continue;
2919 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2920 var.Name);
2924 public override void Emit (EmitContext ec)
2926 if (Report.Errors > 0)
2927 return;
2929 #if PRODUCTION
2930 try {
2931 #endif
2932 EmitMeta (ec);
2934 if (ec.HasReturnLabel)
2935 ec.ReturnLabel = ec.ig.DefineLabel ();
2937 base.Emit (ec);
2939 ec.Mark (EndLocation);
2941 if (ec.HasReturnLabel)
2942 ec.ig.MarkLabel (ec.ReturnLabel);
2944 if (ec.return_value != null) {
2945 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2946 ec.ig.Emit (OpCodes.Ret);
2947 } else {
2949 // If `HasReturnLabel' is set, then we already emitted a
2950 // jump to the end of the method, so we must emit a `ret'
2951 // there.
2953 // Unfortunately, System.Reflection.Emit automatically emits
2954 // a leave to the end of a finally block. This is a problem
2955 // if no code is following the try/finally block since we may
2956 // jump to a point after the end of the method.
2957 // As a workaround, we're always creating a return label in
2958 // this case.
2961 if (ec.HasReturnLabel || !unreachable) {
2962 if (ec.ReturnType != TypeManager.void_type)
2963 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2964 ec.ig.Emit (OpCodes.Ret);
2968 #if PRODUCTION
2969 } catch (Exception e){
2970 Console.WriteLine ("Exception caught by the compiler while emitting:");
2971 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2973 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2974 throw;
2976 #endif
2979 public override void EmitMeta (EmitContext ec)
2981 parameters.ResolveVariable ();
2983 // Avoid declaring an IL variable for this_variable since it is not accessed
2984 // from the generated IL
2985 if (this_variable != null)
2986 Variables.Remove ("this");
2987 base.EmitMeta (ec);
2990 protected override void EmitSymbolInfo (EmitContext ec)
2992 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2993 if ((ae != null) && (ae.Storey != null))
2994 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2996 base.EmitSymbolInfo (ec);
3000 public class SwitchLabel {
3001 Expression label;
3002 object converted;
3003 Location loc;
3005 Label il_label;
3006 bool il_label_set;
3007 Label il_label_code;
3008 bool il_label_code_set;
3010 public static readonly object NullStringCase = new object ();
3013 // if expr == null, then it is the default case.
3015 public SwitchLabel (Expression expr, Location l)
3017 label = expr;
3018 loc = l;
3021 public Expression Label {
3022 get {
3023 return label;
3027 public Location Location {
3028 get { return loc; }
3031 public object Converted {
3032 get {
3033 return converted;
3037 public Label GetILLabel (EmitContext ec)
3039 if (!il_label_set){
3040 il_label = ec.ig.DefineLabel ();
3041 il_label_set = true;
3043 return il_label;
3046 public Label GetILLabelCode (EmitContext ec)
3048 if (!il_label_code_set){
3049 il_label_code = ec.ig.DefineLabel ();
3050 il_label_code_set = true;
3052 return il_label_code;
3056 // Resolves the expression, reduces it to a literal if possible
3057 // and then converts it to the requested type.
3059 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3061 Expression e = label.Resolve (ec);
3063 if (e == null)
3064 return false;
3066 Constant c = e as Constant;
3067 if (c == null){
3068 ec.Report.Error (150, loc, "A constant value is expected");
3069 return false;
3072 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3073 converted = NullStringCase;
3074 return true;
3077 if (allow_nullable && c.GetValue () == null) {
3078 converted = NullStringCase;
3079 return true;
3082 c = c.ImplicitConversionRequired (ec, required_type, loc);
3083 if (c == null)
3084 return false;
3086 converted = c.GetValue ();
3087 return true;
3090 public void Error_AlreadyOccurs (ResolveContext ec, Type switch_type, SwitchLabel collision_with)
3092 string label;
3093 if (converted == null)
3094 label = "default";
3095 else if (converted == NullStringCase)
3096 label = "null";
3097 else
3098 label = converted.ToString ();
3100 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3101 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3104 public SwitchLabel Clone (CloneContext clonectx)
3106 return new SwitchLabel (label.Clone (clonectx), loc);
3110 public class SwitchSection {
3111 // An array of SwitchLabels.
3112 public readonly ArrayList Labels;
3113 public readonly Block Block;
3115 public SwitchSection (ArrayList labels, Block block)
3117 Labels = labels;
3118 Block = block;
3121 public SwitchSection Clone (CloneContext clonectx)
3123 ArrayList cloned_labels = new ArrayList ();
3125 foreach (SwitchLabel sl in cloned_labels)
3126 cloned_labels.Add (sl.Clone (clonectx));
3128 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3132 public class Switch : Statement {
3133 public ArrayList Sections;
3134 public Expression Expr;
3136 /// <summary>
3137 /// Maps constants whose type type SwitchType to their SwitchLabels.
3138 /// </summary>
3139 public IDictionary Elements;
3141 /// <summary>
3142 /// The governing switch type
3143 /// </summary>
3144 public Type SwitchType;
3147 // Computed
3149 Label default_target;
3150 Label null_target;
3151 Expression new_expr;
3152 bool is_constant;
3153 bool has_null_case;
3154 SwitchSection constant_section;
3155 SwitchSection default_section;
3157 ExpressionStatement string_dictionary;
3158 FieldExpr switch_cache_field;
3159 static int unique_counter;
3162 // Nullable Types support
3164 Nullable.Unwrap unwrap;
3166 protected bool HaveUnwrap {
3167 get { return unwrap != null; }
3171 // The types allowed to be implicitly cast from
3172 // on the governing type
3174 static Type [] allowed_types;
3176 public Switch (Expression e, ArrayList sects, Location l)
3178 Expr = e;
3179 Sections = sects;
3180 loc = l;
3183 public bool GotDefault {
3184 get {
3185 return default_section != null;
3189 public Label DefaultTarget {
3190 get {
3191 return default_target;
3196 // Determines the governing type for a switch. The returned
3197 // expression might be the expression from the switch, or an
3198 // expression that includes any potential conversions to the
3199 // integral types or to string.
3201 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3203 Type t = expr.Type;
3205 if (t == TypeManager.byte_type ||
3206 t == TypeManager.sbyte_type ||
3207 t == TypeManager.ushort_type ||
3208 t == TypeManager.short_type ||
3209 t == TypeManager.uint32_type ||
3210 t == TypeManager.int32_type ||
3211 t == TypeManager.uint64_type ||
3212 t == TypeManager.int64_type ||
3213 t == TypeManager.char_type ||
3214 t == TypeManager.string_type ||
3215 t == TypeManager.bool_type ||
3216 TypeManager.IsEnumType (t))
3217 return expr;
3219 if (allowed_types == null){
3220 allowed_types = new Type [] {
3221 TypeManager.sbyte_type,
3222 TypeManager.byte_type,
3223 TypeManager.short_type,
3224 TypeManager.ushort_type,
3225 TypeManager.int32_type,
3226 TypeManager.uint32_type,
3227 TypeManager.int64_type,
3228 TypeManager.uint64_type,
3229 TypeManager.char_type,
3230 TypeManager.string_type
3235 // Try to find a *user* defined implicit conversion.
3237 // If there is no implicit conversion, or if there are multiple
3238 // conversions, we have to report an error
3240 Expression converted = null;
3241 foreach (Type tt in allowed_types){
3242 Expression e;
3244 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3245 if (e == null)
3246 continue;
3249 // Ignore over-worked ImplicitUserConversions that do
3250 // an implicit conversion in addition to the user conversion.
3252 if (!(e is UserCast))
3253 continue;
3255 if (converted != null){
3256 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3257 return null;
3260 converted = e;
3262 return converted;
3266 // Performs the basic sanity checks on the switch statement
3267 // (looks for duplicate keys and non-constant expressions).
3269 // It also returns a hashtable with the keys that we will later
3270 // use to compute the switch tables
3272 bool CheckSwitch (ResolveContext ec)
3274 bool error = false;
3275 Elements = Sections.Count > 10 ?
3276 (IDictionary)new Hashtable () :
3277 (IDictionary)new ListDictionary ();
3279 foreach (SwitchSection ss in Sections){
3280 foreach (SwitchLabel sl in ss.Labels){
3281 if (sl.Label == null){
3282 if (default_section != null){
3283 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3284 error = true;
3286 default_section = ss;
3287 continue;
3290 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3291 error = true;
3292 continue;
3295 object key = sl.Converted;
3296 if (key == SwitchLabel.NullStringCase)
3297 has_null_case = true;
3299 try {
3300 Elements.Add (key, sl);
3301 } catch (ArgumentException) {
3302 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)Elements [key]);
3303 error = true;
3307 return !error;
3310 void EmitObjectInteger (ILGenerator ig, object k)
3312 if (k is int)
3313 IntConstant.EmitInt (ig, (int) k);
3314 else if (k is Constant) {
3315 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3317 else if (k is uint)
3318 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3319 else if (k is long)
3321 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3323 IntConstant.EmitInt (ig, (int) (long) k);
3324 ig.Emit (OpCodes.Conv_I8);
3326 else
3327 LongConstant.EmitLong (ig, (long) k);
3329 else if (k is ulong)
3331 ulong ul = (ulong) k;
3332 if (ul < (1L<<32))
3334 IntConstant.EmitInt (ig, unchecked ((int) ul));
3335 ig.Emit (OpCodes.Conv_U8);
3337 else
3339 LongConstant.EmitLong (ig, unchecked ((long) ul));
3342 else if (k is char)
3343 IntConstant.EmitInt (ig, (int) ((char) k));
3344 else if (k is sbyte)
3345 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3346 else if (k is byte)
3347 IntConstant.EmitInt (ig, (int) ((byte) k));
3348 else if (k is short)
3349 IntConstant.EmitInt (ig, (int) ((short) k));
3350 else if (k is ushort)
3351 IntConstant.EmitInt (ig, (int) ((ushort) k));
3352 else if (k is bool)
3353 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3354 else
3355 throw new Exception ("Unhandled case");
3358 // structure used to hold blocks of keys while calculating table switch
3359 class KeyBlock : IComparable
3361 public KeyBlock (long _first)
3363 first = last = _first;
3365 public long first;
3366 public long last;
3367 public ArrayList element_keys = null;
3368 // how many items are in the bucket
3369 public int Size = 1;
3370 public int Length
3372 get { return (int) (last - first + 1); }
3374 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3376 return kb_last.last - kb_first.first + 1;
3378 public int CompareTo (object obj)
3380 KeyBlock kb = (KeyBlock) obj;
3381 int nLength = Length;
3382 int nLengthOther = kb.Length;
3383 if (nLengthOther == nLength)
3384 return (int) (kb.first - first);
3385 return nLength - nLengthOther;
3389 /// <summary>
3390 /// This method emits code for a lookup-based switch statement (non-string)
3391 /// Basically it groups the cases into blocks that are at least half full,
3392 /// and then spits out individual lookup opcodes for each block.
3393 /// It emits the longest blocks first, and short blocks are just
3394 /// handled with direct compares.
3395 /// </summary>
3396 /// <param name="ec"></param>
3397 /// <param name="val"></param>
3398 /// <returns></returns>
3399 void TableSwitchEmit (EmitContext ec, Expression val)
3401 int element_count = Elements.Count;
3402 object [] element_keys = new object [element_count];
3403 Elements.Keys.CopyTo (element_keys, 0);
3404 Array.Sort (element_keys);
3406 // initialize the block list with one element per key
3407 ArrayList key_blocks = new ArrayList (element_count);
3408 foreach (object key in element_keys)
3409 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3411 KeyBlock current_kb;
3412 // iteratively merge the blocks while they are at least half full
3413 // there's probably a really cool way to do this with a tree...
3414 while (key_blocks.Count > 1)
3416 ArrayList key_blocks_new = new ArrayList ();
3417 current_kb = (KeyBlock) key_blocks [0];
3418 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3420 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3421 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3423 // merge blocks
3424 current_kb.last = kb.last;
3425 current_kb.Size += kb.Size;
3427 else
3429 // start a new block
3430 key_blocks_new.Add (current_kb);
3431 current_kb = kb;
3434 key_blocks_new.Add (current_kb);
3435 if (key_blocks.Count == key_blocks_new.Count)
3436 break;
3437 key_blocks = key_blocks_new;
3440 // initialize the key lists
3441 foreach (KeyBlock kb in key_blocks)
3442 kb.element_keys = new ArrayList ();
3444 // fill the key lists
3445 int iBlockCurr = 0;
3446 if (key_blocks.Count > 0) {
3447 current_kb = (KeyBlock) key_blocks [0];
3448 foreach (object key in element_keys)
3450 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3451 System.Convert.ToInt64 (key) > current_kb.last;
3452 if (next_block)
3453 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3454 current_kb.element_keys.Add (key);
3458 // sort the blocks so we can tackle the largest ones first
3459 key_blocks.Sort ();
3461 // okay now we can start...
3462 ILGenerator ig = ec.ig;
3463 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3464 Label lbl_default = default_target;
3466 Type type_keys = null;
3467 if (element_keys.Length > 0)
3468 type_keys = element_keys [0].GetType (); // used for conversions
3470 Type compare_type;
3472 if (TypeManager.IsEnumType (SwitchType))
3473 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3474 else
3475 compare_type = SwitchType;
3477 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3479 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3480 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3481 if (kb.Length <= 2)
3483 foreach (object key in kb.element_keys) {
3484 SwitchLabel sl = (SwitchLabel) Elements [key];
3485 if (key is int && (int) key == 0) {
3486 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3487 } else {
3488 val.Emit (ec);
3489 EmitObjectInteger (ig, key);
3490 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3494 else
3496 // TODO: if all the keys in the block are the same and there are
3497 // no gaps/defaults then just use a range-check.
3498 if (compare_type == TypeManager.int64_type ||
3499 compare_type == TypeManager.uint64_type)
3501 // TODO: optimize constant/I4 cases
3503 // check block range (could be > 2^31)
3504 val.Emit (ec);
3505 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3506 ig.Emit (OpCodes.Blt, lbl_default);
3507 val.Emit (ec);
3508 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3509 ig.Emit (OpCodes.Bgt, lbl_default);
3511 // normalize range
3512 val.Emit (ec);
3513 if (kb.first != 0)
3515 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3516 ig.Emit (OpCodes.Sub);
3518 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3520 else
3522 // normalize range
3523 val.Emit (ec);
3524 int first = (int) kb.first;
3525 if (first > 0)
3527 IntConstant.EmitInt (ig, first);
3528 ig.Emit (OpCodes.Sub);
3530 else if (first < 0)
3532 IntConstant.EmitInt (ig, -first);
3533 ig.Emit (OpCodes.Add);
3537 // first, build the list of labels for the switch
3538 int iKey = 0;
3539 int cJumps = kb.Length;
3540 Label [] switch_labels = new Label [cJumps];
3541 for (int iJump = 0; iJump < cJumps; iJump++)
3543 object key = kb.element_keys [iKey];
3544 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3546 SwitchLabel sl = (SwitchLabel) Elements [key];
3547 switch_labels [iJump] = sl.GetILLabel (ec);
3548 iKey++;
3550 else
3551 switch_labels [iJump] = lbl_default;
3553 // emit the switch opcode
3554 ig.Emit (OpCodes.Switch, switch_labels);
3557 // mark the default for this block
3558 if (iBlock != 0)
3559 ig.MarkLabel (lbl_default);
3562 // TODO: find the default case and emit it here,
3563 // to prevent having to do the following jump.
3564 // make sure to mark other labels in the default section
3566 // the last default just goes to the end
3567 if (element_keys.Length > 0)
3568 ig.Emit (OpCodes.Br, lbl_default);
3570 // now emit the code for the sections
3571 bool found_default = false;
3573 foreach (SwitchSection ss in Sections) {
3574 foreach (SwitchLabel sl in ss.Labels) {
3575 if (sl.Converted == SwitchLabel.NullStringCase) {
3576 ig.MarkLabel (null_target);
3577 } else if (sl.Label == null) {
3578 ig.MarkLabel (lbl_default);
3579 found_default = true;
3580 if (!has_null_case)
3581 ig.MarkLabel (null_target);
3583 ig.MarkLabel (sl.GetILLabel (ec));
3584 ig.MarkLabel (sl.GetILLabelCode (ec));
3586 ss.Block.Emit (ec);
3589 if (!found_default) {
3590 ig.MarkLabel (lbl_default);
3591 if (!has_null_case) {
3592 ig.MarkLabel (null_target);
3596 ig.MarkLabel (lbl_end);
3599 SwitchSection FindSection (SwitchLabel label)
3601 foreach (SwitchSection ss in Sections){
3602 foreach (SwitchLabel sl in ss.Labels){
3603 if (label == sl)
3604 return ss;
3608 return null;
3611 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3613 foreach (SwitchSection ss in Sections)
3614 ss.Block.MutateHoistedGenericType (storey);
3617 public static void Reset ()
3619 unique_counter = 0;
3620 allowed_types = null;
3623 public override bool Resolve (BlockContext ec)
3625 Expr = Expr.Resolve (ec);
3626 if (Expr == null)
3627 return false;
3629 new_expr = SwitchGoverningType (ec, Expr);
3631 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3632 unwrap = Nullable.Unwrap.Create (Expr, false);
3633 if (unwrap == null)
3634 return false;
3636 new_expr = SwitchGoverningType (ec, unwrap);
3639 if (new_expr == null){
3640 ec.Report.Error (151, loc,
3641 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3642 TypeManager.CSharpName (Expr.Type));
3643 return false;
3646 // Validate switch.
3647 SwitchType = new_expr.Type;
3649 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3650 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3651 return false;
3654 if (!CheckSwitch (ec))
3655 return false;
3657 if (HaveUnwrap)
3658 Elements.Remove (SwitchLabel.NullStringCase);
3660 Switch old_switch = ec.Switch;
3661 ec.Switch = this;
3662 ec.Switch.SwitchType = SwitchType;
3664 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3665 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3667 is_constant = new_expr is Constant;
3668 if (is_constant) {
3669 object key = ((Constant) new_expr).GetValue ();
3670 SwitchLabel label = (SwitchLabel) Elements [key];
3672 constant_section = FindSection (label);
3673 if (constant_section == null)
3674 constant_section = default_section;
3677 bool first = true;
3678 bool ok = true;
3679 foreach (SwitchSection ss in Sections){
3680 if (!first)
3681 ec.CurrentBranching.CreateSibling (
3682 null, FlowBranching.SiblingType.SwitchSection);
3683 else
3684 first = false;
3686 if (is_constant && (ss != constant_section)) {
3687 // If we're a constant switch, we're only emitting
3688 // one single section - mark all the others as
3689 // unreachable.
3690 ec.CurrentBranching.CurrentUsageVector.Goto ();
3691 if (!ss.Block.ResolveUnreachable (ec, true)) {
3692 ok = false;
3694 } else {
3695 if (!ss.Block.Resolve (ec))
3696 ok = false;
3700 if (default_section == null)
3701 ec.CurrentBranching.CreateSibling (
3702 null, FlowBranching.SiblingType.SwitchSection);
3704 ec.EndFlowBranching ();
3705 ec.Switch = old_switch;
3707 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3709 if (!ok)
3710 return false;
3712 if (SwitchType == TypeManager.string_type && !is_constant) {
3713 // TODO: Optimize single case, and single+default case
3714 ResolveStringSwitchMap (ec);
3717 return true;
3720 void ResolveStringSwitchMap (ResolveContext ec)
3722 FullNamedExpression string_dictionary_type;
3723 if (TypeManager.generic_ienumerable_type != null) {
3724 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3725 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3727 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3728 new TypeArguments (
3729 new TypeExpression (TypeManager.string_type, loc),
3730 new TypeExpression (TypeManager.int32_type, loc)), loc);
3731 } else {
3732 MemberAccess system_collections_generic = new MemberAccess (
3733 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3735 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3738 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3739 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3740 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3741 if (!field.Define ())
3742 return;
3743 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3745 ArrayList init = new ArrayList ();
3746 int counter = 0;
3747 Elements.Clear ();
3748 string value = null;
3749 foreach (SwitchSection section in Sections) {
3750 int last_count = init.Count;
3751 foreach (SwitchLabel sl in section.Labels) {
3752 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3753 continue;
3755 value = (string) sl.Converted;
3756 ArrayList init_args = new ArrayList (2);
3757 init_args.Add (new StringLiteral (value, sl.Location));
3758 init_args.Add (new IntConstant (counter, loc));
3759 init.Add (new CollectionElementInitializer (init_args, loc));
3763 // Don't add empty sections
3765 if (last_count == init.Count)
3766 continue;
3768 Elements.Add (counter, section.Labels [0]);
3769 ++counter;
3772 Arguments args = new Arguments (1);
3773 args.Add (new Argument (new IntConstant (init.Count, loc)));
3774 Expression initializer = new NewInitialize (string_dictionary_type, args,
3775 new CollectionOrObjectInitializers (init, loc), loc);
3777 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3778 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3781 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3783 ILGenerator ig = ec.ig;
3784 Label l_initialized = ig.DefineLabel ();
3787 // Skip initialization when value is null
3789 value.EmitBranchable (ec, null_target, false);
3792 // Check if string dictionary is initialized and initialize
3794 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3795 string_dictionary.EmitStatement (ec);
3796 ig.MarkLabel (l_initialized);
3798 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3800 ResolveContext rc = new ResolveContext (ec.MemberContext);
3802 if (TypeManager.generic_ienumerable_type != null) {
3803 Arguments get_value_args = new Arguments (2);
3804 get_value_args.Add (new Argument (value));
3805 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3806 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3807 if (get_item == null)
3808 return;
3811 // A value was not found, go to default case
3813 get_item.EmitBranchable (ec, default_target, false);
3814 } else {
3815 Arguments get_value_args = new Arguments (1);
3816 get_value_args.Add (new Argument (value));
3818 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3819 if (get_item == null)
3820 return;
3822 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3823 get_item_object.EmitAssign (ec, get_item, true, false);
3824 ec.ig.Emit (OpCodes.Brfalse, default_target);
3826 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3827 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3829 get_item_int.EmitStatement (ec);
3830 get_item_object.Release (ec);
3833 TableSwitchEmit (ec, string_switch_variable);
3834 string_switch_variable.Release (ec);
3837 protected override void DoEmit (EmitContext ec)
3839 ILGenerator ig = ec.ig;
3841 default_target = ig.DefineLabel ();
3842 null_target = ig.DefineLabel ();
3844 // Store variable for comparission purposes
3845 // TODO: Don't duplicate non-captured VariableReference
3846 LocalTemporary value;
3847 if (HaveUnwrap) {
3848 value = new LocalTemporary (SwitchType);
3849 unwrap.EmitCheck (ec);
3850 ig.Emit (OpCodes.Brfalse, null_target);
3851 new_expr.Emit (ec);
3852 value.Store (ec);
3853 } else if (!is_constant) {
3854 value = new LocalTemporary (SwitchType);
3855 new_expr.Emit (ec);
3856 value.Store (ec);
3857 } else
3858 value = null;
3861 // Setup the codegen context
3863 Label old_end = ec.LoopEnd;
3864 Switch old_switch = ec.Switch;
3866 ec.LoopEnd = ig.DefineLabel ();
3867 ec.Switch = this;
3869 // Emit Code.
3870 if (is_constant) {
3871 if (constant_section != null)
3872 constant_section.Block.Emit (ec);
3873 } else if (string_dictionary != null) {
3874 DoEmitStringSwitch (value, ec);
3875 } else {
3876 TableSwitchEmit (ec, value);
3879 if (value != null)
3880 value.Release (ec);
3882 // Restore context state.
3883 ig.MarkLabel (ec.LoopEnd);
3886 // Restore the previous context
3888 ec.LoopEnd = old_end;
3889 ec.Switch = old_switch;
3892 protected override void CloneTo (CloneContext clonectx, Statement t)
3894 Switch target = (Switch) t;
3896 target.Expr = Expr.Clone (clonectx);
3897 target.Sections = new ArrayList ();
3898 foreach (SwitchSection ss in Sections){
3899 target.Sections.Add (ss.Clone (clonectx));
3904 // A place where execution can restart in an iterator
3905 public abstract class ResumableStatement : Statement
3907 bool prepared;
3908 protected Label resume_point;
3910 public Label PrepareForEmit (EmitContext ec)
3912 if (!prepared) {
3913 prepared = true;
3914 resume_point = ec.ig.DefineLabel ();
3916 return resume_point;
3919 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3921 return end;
3923 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3928 // Base class for statements that are implemented in terms of try...finally
3929 public abstract class ExceptionStatement : ResumableStatement
3931 bool code_follows;
3932 Iterator iter;
3934 protected abstract void EmitPreTryBody (EmitContext ec);
3935 protected abstract void EmitTryBody (EmitContext ec);
3936 protected abstract void EmitFinallyBody (EmitContext ec);
3938 protected sealed override void DoEmit (EmitContext ec)
3940 ILGenerator ig = ec.ig;
3942 EmitPreTryBody (ec);
3944 if (resume_points != null) {
3945 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3946 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3949 ig.BeginExceptionBlock ();
3951 if (resume_points != null) {
3952 ig.MarkLabel (resume_point);
3954 // For normal control flow, we want to fall-through the Switch
3955 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3956 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3957 IntConstant.EmitInt (ig, first_resume_pc);
3958 ig.Emit (OpCodes.Sub);
3960 Label [] labels = new Label [resume_points.Count];
3961 for (int i = 0; i < resume_points.Count; ++i)
3962 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3963 ig.Emit (OpCodes.Switch, labels);
3966 EmitTryBody (ec);
3968 ig.BeginFinallyBlock ();
3970 Label start_finally = ec.ig.DefineLabel ();
3971 if (resume_points != null) {
3972 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3973 ig.Emit (OpCodes.Brfalse_S, start_finally);
3974 ig.Emit (OpCodes.Endfinally);
3977 ig.MarkLabel (start_finally);
3978 EmitFinallyBody (ec);
3980 ig.EndExceptionBlock ();
3983 public void SomeCodeFollows ()
3985 code_follows = true;
3988 public override bool Resolve (BlockContext ec)
3990 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3991 // So, ensure there's some IL code after this statement.
3992 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3993 ec.NeedReturnLabel ();
3995 iter = ec.CurrentIterator;
3996 return true;
3999 ArrayList resume_points;
4000 int first_resume_pc;
4001 public void AddResumePoint (ResumableStatement stmt, int pc)
4003 if (resume_points == null) {
4004 resume_points = new ArrayList ();
4005 first_resume_pc = pc;
4008 if (pc != first_resume_pc + resume_points.Count)
4009 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4011 resume_points.Add (stmt);
4014 Label dispose_try_block;
4015 bool prepared_for_dispose, emitted_dispose;
4016 public override Label PrepareForDispose (EmitContext ec, Label end)
4018 if (!prepared_for_dispose) {
4019 prepared_for_dispose = true;
4020 dispose_try_block = ec.ig.DefineLabel ();
4022 return dispose_try_block;
4025 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4027 if (emitted_dispose)
4028 return;
4030 emitted_dispose = true;
4032 ILGenerator ig = ec.ig;
4034 Label end_of_try = ig.DefineLabel ();
4036 // Ensure that the only way we can get into this code is through a dispatcher
4037 if (have_dispatcher)
4038 ig.Emit (OpCodes.Br, end);
4040 ig.BeginExceptionBlock ();
4042 ig.MarkLabel (dispose_try_block);
4044 Label [] labels = null;
4045 for (int i = 0; i < resume_points.Count; ++i) {
4046 ResumableStatement s = (ResumableStatement) resume_points [i];
4047 Label ret = s.PrepareForDispose (ec, end_of_try);
4048 if (ret.Equals (end_of_try) && labels == null)
4049 continue;
4050 if (labels == null) {
4051 labels = new Label [resume_points.Count];
4052 for (int j = 0; j < i; ++j)
4053 labels [j] = end_of_try;
4055 labels [i] = ret;
4058 if (labels != null) {
4059 int j;
4060 for (j = 1; j < labels.Length; ++j)
4061 if (!labels [0].Equals (labels [j]))
4062 break;
4063 bool emit_dispatcher = j < labels.Length;
4065 if (emit_dispatcher) {
4066 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4067 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4068 IntConstant.EmitInt (ig, first_resume_pc);
4069 ig.Emit (OpCodes.Sub);
4070 ig.Emit (OpCodes.Switch, labels);
4071 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4074 foreach (ResumableStatement s in resume_points)
4075 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4078 ig.MarkLabel (end_of_try);
4080 ig.BeginFinallyBlock ();
4082 EmitFinallyBody (ec);
4084 ig.EndExceptionBlock ();
4088 public class Lock : ExceptionStatement {
4089 Expression expr;
4090 public Statement Statement;
4091 TemporaryVariable temp;
4093 public Lock (Expression expr, Statement stmt, Location l)
4095 this.expr = expr;
4096 Statement = stmt;
4097 loc = l;
4100 public override bool Resolve (BlockContext ec)
4102 expr = expr.Resolve (ec);
4103 if (expr == null)
4104 return false;
4106 if (!TypeManager.IsReferenceType (expr.Type)){
4107 ec.Report.Error (185, loc,
4108 "`{0}' is not a reference type as required by the lock statement",
4109 TypeManager.CSharpName (expr.Type));
4110 return false;
4113 ec.StartFlowBranching (this);
4114 bool ok = Statement.Resolve (ec);
4115 ec.EndFlowBranching ();
4117 ok &= base.Resolve (ec);
4119 // Avoid creating libraries that reference the internal
4120 // mcs NullType:
4121 Type t = expr.Type;
4122 if (t == TypeManager.null_type)
4123 t = TypeManager.object_type;
4125 temp = new TemporaryVariable (t, loc);
4126 temp.Resolve (ec);
4128 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4129 Type monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", Kind.Class, true);
4130 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4131 monitor_type, "Enter", loc, TypeManager.object_type);
4132 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4133 monitor_type, "Exit", loc, TypeManager.object_type);
4136 return ok;
4139 protected override void EmitPreTryBody (EmitContext ec)
4141 ILGenerator ig = ec.ig;
4143 temp.EmitAssign (ec, expr);
4144 temp.Emit (ec);
4145 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4148 protected override void EmitTryBody (EmitContext ec)
4150 Statement.Emit (ec);
4153 protected override void EmitFinallyBody (EmitContext ec)
4155 temp.Emit (ec);
4156 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4159 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4161 expr.MutateHoistedGenericType (storey);
4162 temp.MutateHoistedGenericType (storey);
4163 Statement.MutateHoistedGenericType (storey);
4166 protected override void CloneTo (CloneContext clonectx, Statement t)
4168 Lock target = (Lock) t;
4170 target.expr = expr.Clone (clonectx);
4171 target.Statement = Statement.Clone (clonectx);
4175 public class Unchecked : Statement {
4176 public Block Block;
4178 public Unchecked (Block b)
4180 Block = b;
4181 b.Unchecked = true;
4184 public override bool Resolve (BlockContext ec)
4186 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4187 return Block.Resolve (ec);
4190 protected override void DoEmit (EmitContext ec)
4192 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4193 Block.Emit (ec);
4196 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4198 Block.MutateHoistedGenericType (storey);
4201 protected override void CloneTo (CloneContext clonectx, Statement t)
4203 Unchecked target = (Unchecked) t;
4205 target.Block = clonectx.LookupBlock (Block);
4209 public class Checked : Statement {
4210 public Block Block;
4212 public Checked (Block b)
4214 Block = b;
4215 b.Unchecked = false;
4218 public override bool Resolve (BlockContext ec)
4220 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4221 return Block.Resolve (ec);
4224 protected override void DoEmit (EmitContext ec)
4226 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4227 Block.Emit (ec);
4230 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4232 Block.MutateHoistedGenericType (storey);
4235 protected override void CloneTo (CloneContext clonectx, Statement t)
4237 Checked target = (Checked) t;
4239 target.Block = clonectx.LookupBlock (Block);
4243 public class Unsafe : Statement {
4244 public Block Block;
4246 public Unsafe (Block b)
4248 Block = b;
4249 Block.Unsafe = true;
4250 loc = b.StartLocation;
4253 public override bool Resolve (BlockContext ec)
4255 if (ec.CurrentIterator != null)
4256 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4258 using (ec.Set (ResolveContext.Options.UnsafeScope))
4259 return Block.Resolve (ec);
4262 protected override void DoEmit (EmitContext ec)
4264 Block.Emit (ec);
4267 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4269 Block.MutateHoistedGenericType (storey);
4272 protected override void CloneTo (CloneContext clonectx, Statement t)
4274 Unsafe target = (Unsafe) t;
4276 target.Block = clonectx.LookupBlock (Block);
4281 // Fixed statement
4283 public class Fixed : Statement {
4284 Expression type;
4285 ArrayList declarators;
4286 Statement statement;
4287 Type expr_type;
4288 Emitter[] data;
4289 bool has_ret;
4291 abstract class Emitter
4293 protected LocalInfo vi;
4294 protected Expression converted;
4296 protected Emitter (Expression expr, LocalInfo li)
4298 converted = expr;
4299 vi = li;
4302 public abstract void Emit (EmitContext ec);
4303 public abstract void EmitExit (EmitContext ec);
4306 class ExpressionEmitter : Emitter {
4307 public ExpressionEmitter (Expression converted, LocalInfo li) :
4308 base (converted, li)
4312 public override void Emit (EmitContext ec) {
4314 // Store pointer in pinned location
4316 converted.Emit (ec);
4317 vi.EmitAssign (ec);
4320 public override void EmitExit (EmitContext ec)
4322 ec.ig.Emit (OpCodes.Ldc_I4_0);
4323 ec.ig.Emit (OpCodes.Conv_U);
4324 vi.EmitAssign (ec);
4328 class StringEmitter : Emitter
4330 LocalInfo pinned_string;
4332 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4333 base (expr, li)
4335 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4336 pinned_string.Pinned = true;
4339 public StringEmitter Resolve (ResolveContext rc)
4341 pinned_string.Resolve (rc);
4343 if (TypeManager.int_get_offset_to_string_data == null) {
4344 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4345 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4348 return this;
4351 public override void Emit (EmitContext ec)
4353 pinned_string.ResolveVariable (ec);
4355 converted.Emit (ec);
4356 pinned_string.EmitAssign (ec);
4358 // TODO: Should use Binary::Add
4359 pinned_string.Emit (ec);
4360 ec.ig.Emit (OpCodes.Conv_I);
4362 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4363 //pe.InstanceExpression = pinned_string;
4364 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4366 ec.ig.Emit (OpCodes.Add);
4367 vi.EmitAssign (ec);
4370 public override void EmitExit (EmitContext ec)
4372 ec.ig.Emit (OpCodes.Ldnull);
4373 pinned_string.EmitAssign (ec);
4377 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4379 this.type = type;
4380 declarators = decls;
4381 statement = stmt;
4382 loc = l;
4385 public Statement Statement {
4386 get { return statement; }
4389 public override bool Resolve (BlockContext ec)
4391 if (!ec.IsUnsafe){
4392 Expression.UnsafeError (ec, loc);
4393 return false;
4396 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4397 if (texpr == null) {
4398 if (type is VarExpr)
4399 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4401 return false;
4404 expr_type = texpr.Type;
4406 data = new Emitter [declarators.Count];
4408 if (!expr_type.IsPointer){
4409 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4410 return false;
4413 int i = 0;
4414 foreach (Pair p in declarators){
4415 LocalInfo vi = (LocalInfo) p.First;
4416 Expression e = (Expression) p.Second;
4418 vi.VariableInfo.SetAssigned (ec);
4419 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4422 // The rules for the possible declarators are pretty wise,
4423 // but the production on the grammar is more concise.
4425 // So we have to enforce these rules here.
4427 // We do not resolve before doing the case 1 test,
4428 // because the grammar is explicit in that the token &
4429 // is present, so we need to test for this particular case.
4432 if (e is Cast){
4433 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4434 return false;
4437 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4438 e = e.Resolve (ec);
4441 if (e == null)
4442 return false;
4445 // Case 2: Array
4447 if (e.Type.IsArray){
4448 Type array_type = TypeManager.GetElementType (e.Type);
4451 // Provided that array_type is unmanaged,
4453 if (!TypeManager.VerifyUnManaged (array_type, loc))
4454 return false;
4457 // and T* is implicitly convertible to the
4458 // pointer type given in the fixed statement.
4460 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4462 Expression converted = Convert.ImplicitConversionRequired (
4463 ec, array_ptr, vi.VariableType, loc);
4464 if (converted == null)
4465 return false;
4468 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4470 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4471 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4472 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc)))),
4473 new NullPointer (loc),
4474 converted);
4476 converted = converted.Resolve (ec);
4478 data [i] = new ExpressionEmitter (converted, vi);
4479 i++;
4481 continue;
4485 // Case 3: string
4487 if (e.Type == TypeManager.string_type){
4488 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4489 i++;
4490 continue;
4493 // Case 4: fixed buffer
4494 if (e is FixedBufferPtr) {
4495 data [i++] = new ExpressionEmitter (e, vi);
4496 continue;
4500 // Case 1: & object.
4502 Unary u = e as Unary;
4503 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4504 IVariableReference vr = u.Expr as IVariableReference;
4505 if (vr == null || !vr.IsFixed) {
4506 data [i] = new ExpressionEmitter (e, vi);
4510 if (data [i++] == null)
4511 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4513 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4516 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4517 bool ok = statement.Resolve (ec);
4518 bool flow_unreachable = ec.EndFlowBranching ();
4519 has_ret = flow_unreachable;
4521 return ok;
4524 protected override void DoEmit (EmitContext ec)
4526 for (int i = 0; i < data.Length; i++) {
4527 data [i].Emit (ec);
4530 statement.Emit (ec);
4532 if (has_ret)
4533 return;
4536 // Clear the pinned variable
4538 for (int i = 0; i < data.Length; i++) {
4539 data [i].EmitExit (ec);
4543 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4545 // Fixed statement cannot be used inside anonymous methods or lambdas
4546 throw new NotSupportedException ();
4549 protected override void CloneTo (CloneContext clonectx, Statement t)
4551 Fixed target = (Fixed) t;
4553 target.type = type.Clone (clonectx);
4554 target.declarators = new ArrayList (declarators.Count);
4555 foreach (Pair p in declarators) {
4556 LocalInfo vi = (LocalInfo) p.First;
4557 Expression e = (Expression) p.Second;
4559 target.declarators.Add (
4560 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4563 target.statement = statement.Clone (clonectx);
4567 public class Catch : Statement {
4568 public readonly string Name;
4569 public Block Block;
4570 public Block VarBlock;
4572 Expression type_expr;
4573 Type type;
4575 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4577 type_expr = type;
4578 Name = name;
4579 Block = block;
4580 VarBlock = var_block;
4581 loc = l;
4584 public Type CatchType {
4585 get {
4586 return type;
4590 public bool IsGeneral {
4591 get {
4592 return type_expr == null;
4596 protected override void DoEmit (EmitContext ec)
4598 ILGenerator ig = ec.ig;
4600 if (CatchType != null)
4601 ig.BeginCatchBlock (CatchType);
4602 else
4603 ig.BeginCatchBlock (TypeManager.object_type);
4605 if (VarBlock != null)
4606 VarBlock.Emit (ec);
4608 if (Name != null) {
4609 // TODO: Move to resolve
4610 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4611 lvr.Resolve (new ResolveContext (ec.MemberContext));
4613 // Only to make verifier happy
4614 if (TypeManager.IsGenericParameter (lvr.Type))
4615 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4617 Expression source;
4618 if (lvr.IsHoisted) {
4619 LocalTemporary lt = new LocalTemporary (lvr.Type);
4620 lt.Store (ec);
4621 source = lt;
4622 } else {
4623 // Variable is at the top of the stack
4624 source = EmptyExpression.Null;
4627 lvr.EmitAssign (ec, source, false, false);
4628 } else
4629 ig.Emit (OpCodes.Pop);
4631 Block.Emit (ec);
4634 public override bool Resolve (BlockContext ec)
4636 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4637 if (type_expr != null) {
4638 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4639 if (te == null)
4640 return false;
4642 type = te.Type;
4644 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4645 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4646 return false;
4648 } else
4649 type = null;
4651 if (!Block.Resolve (ec))
4652 return false;
4654 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4655 // emit the "unused variable" warnings.
4656 if (VarBlock != null)
4657 return VarBlock.Resolve (ec);
4659 return true;
4663 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4665 if (type != null)
4666 type = storey.MutateType (type);
4667 if (VarBlock != null)
4668 VarBlock.MutateHoistedGenericType (storey);
4669 Block.MutateHoistedGenericType (storey);
4672 protected override void CloneTo (CloneContext clonectx, Statement t)
4674 Catch target = (Catch) t;
4676 if (type_expr != null)
4677 target.type_expr = type_expr.Clone (clonectx);
4678 if (VarBlock != null)
4679 target.VarBlock = clonectx.LookupBlock (VarBlock);
4680 target.Block = clonectx.LookupBlock (Block);
4684 public class TryFinally : ExceptionStatement {
4685 Statement stmt;
4686 Block fini;
4688 public TryFinally (Statement stmt, Block fini, Location l)
4690 this.stmt = stmt;
4691 this.fini = fini;
4692 loc = l;
4695 public override bool Resolve (BlockContext ec)
4697 bool ok = true;
4699 ec.StartFlowBranching (this);
4701 if (!stmt.Resolve (ec))
4702 ok = false;
4704 if (ok)
4705 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4706 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4707 if (!fini.Resolve (ec))
4708 ok = false;
4711 ec.EndFlowBranching ();
4713 ok &= base.Resolve (ec);
4715 return ok;
4718 protected override void EmitPreTryBody (EmitContext ec)
4722 protected override void EmitTryBody (EmitContext ec)
4724 stmt.Emit (ec);
4727 protected override void EmitFinallyBody (EmitContext ec)
4729 fini.Emit (ec);
4732 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4734 stmt.MutateHoistedGenericType (storey);
4735 fini.MutateHoistedGenericType (storey);
4738 protected override void CloneTo (CloneContext clonectx, Statement t)
4740 TryFinally target = (TryFinally) t;
4742 target.stmt = (Statement) stmt.Clone (clonectx);
4743 if (fini != null)
4744 target.fini = clonectx.LookupBlock (fini);
4748 public class TryCatch : Statement {
4749 public Block Block;
4750 public ArrayList Specific;
4751 public Catch General;
4752 bool inside_try_finally, code_follows;
4754 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4756 this.Block = block;
4757 this.Specific = catch_clauses;
4758 this.inside_try_finally = inside_try_finally;
4760 Catch c = (Catch) catch_clauses [0];
4761 if (c.IsGeneral) {
4762 this.General = c;
4763 catch_clauses.RemoveAt (0);
4766 loc = l;
4769 public override bool Resolve (BlockContext ec)
4771 bool ok = true;
4773 ec.StartFlowBranching (this);
4775 if (!Block.Resolve (ec))
4776 ok = false;
4778 Type[] prev_catches = new Type [Specific.Count];
4779 int last_index = 0;
4780 foreach (Catch c in Specific){
4781 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4783 if (c.Name != null) {
4784 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4785 if (vi == null)
4786 throw new Exception ();
4788 vi.VariableInfo = null;
4791 if (!c.Resolve (ec)) {
4792 ok = false;
4793 continue;
4796 Type resolved_type = c.CatchType;
4797 for (int ii = 0; ii < last_index; ++ii) {
4798 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4799 ec.Report.Error (160, c.loc,
4800 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4801 TypeManager.CSharpName (prev_catches [ii]));
4802 ok = false;
4806 prev_catches [last_index++] = resolved_type;
4809 if (General != null) {
4810 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4811 foreach (Catch c in Specific){
4812 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4813 ec.Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4818 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4820 if (!General.Resolve (ec))
4821 ok = false;
4824 ec.EndFlowBranching ();
4826 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4827 // So, ensure there's some IL code after this statement
4828 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4829 ec.NeedReturnLabel ();
4831 return ok;
4834 public void SomeCodeFollows ()
4836 code_follows = true;
4839 protected override void DoEmit (EmitContext ec)
4841 ILGenerator ig = ec.ig;
4843 if (!inside_try_finally)
4844 ig.BeginExceptionBlock ();
4846 Block.Emit (ec);
4848 foreach (Catch c in Specific)
4849 c.Emit (ec);
4851 if (General != null)
4852 General.Emit (ec);
4854 if (!inside_try_finally)
4855 ig.EndExceptionBlock ();
4858 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4860 Block.MutateHoistedGenericType (storey);
4862 if (General != null)
4863 General.MutateHoistedGenericType (storey);
4864 if (Specific != null) {
4865 foreach (Catch c in Specific)
4866 c.MutateHoistedGenericType (storey);
4870 protected override void CloneTo (CloneContext clonectx, Statement t)
4872 TryCatch target = (TryCatch) t;
4874 target.Block = clonectx.LookupBlock (Block);
4875 if (General != null)
4876 target.General = (Catch) General.Clone (clonectx);
4877 if (Specific != null){
4878 target.Specific = new ArrayList ();
4879 foreach (Catch c in Specific)
4880 target.Specific.Add (c.Clone (clonectx));
4885 // FIXME: Why is it almost exact copy of Using ??
4886 public class UsingTemporary : ExceptionStatement {
4887 TemporaryVariable local_copy;
4888 public Statement Statement;
4889 Expression expr;
4890 Type expr_type;
4892 public UsingTemporary (Expression expr, Statement stmt, Location l)
4894 this.expr = expr;
4895 Statement = stmt;
4896 loc = l;
4899 public override bool Resolve (BlockContext ec)
4901 expr = expr.Resolve (ec);
4902 if (expr == null)
4903 return false;
4905 expr_type = expr.Type;
4907 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4908 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4909 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4910 return false;
4914 local_copy = new TemporaryVariable (expr_type, loc);
4915 local_copy.Resolve (ec);
4917 ec.StartFlowBranching (this);
4919 bool ok = Statement.Resolve (ec);
4921 ec.EndFlowBranching ();
4923 ok &= base.Resolve (ec);
4925 if (TypeManager.void_dispose_void == null) {
4926 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4927 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4930 return ok;
4933 protected override void EmitPreTryBody (EmitContext ec)
4935 local_copy.EmitAssign (ec, expr);
4938 protected override void EmitTryBody (EmitContext ec)
4940 Statement.Emit (ec);
4943 protected override void EmitFinallyBody (EmitContext ec)
4945 ILGenerator ig = ec.ig;
4946 if (!TypeManager.IsStruct (expr_type)) {
4947 Label skip = ig.DefineLabel ();
4948 local_copy.Emit (ec);
4949 ig.Emit (OpCodes.Brfalse, skip);
4950 local_copy.Emit (ec);
4951 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4952 ig.MarkLabel (skip);
4953 return;
4956 Expression ml = Expression.MemberLookup (RootContext.ToplevelTypes.Compiler,
4957 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4958 "Dispose", Location.Null);
4960 if (!(ml is MethodGroupExpr)) {
4961 local_copy.Emit (ec);
4962 ig.Emit (OpCodes.Box, expr_type);
4963 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4964 return;
4967 MethodInfo mi = null;
4969 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4970 if (TypeManager.GetParameterData (mk).Count == 0) {
4971 mi = mk;
4972 break;
4976 if (mi == null) {
4977 RootContext.ToplevelTypes.Compiler.Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4978 return;
4981 local_copy.AddressOf (ec, AddressOp.Load);
4982 ig.Emit (OpCodes.Call, mi);
4985 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4987 expr_type = storey.MutateType (expr_type);
4988 local_copy.MutateHoistedGenericType (storey);
4989 Statement.MutateHoistedGenericType (storey);
4992 protected override void CloneTo (CloneContext clonectx, Statement t)
4994 UsingTemporary target = (UsingTemporary) t;
4996 target.expr = expr.Clone (clonectx);
4997 target.Statement = Statement.Clone (clonectx);
5001 public class Using : ExceptionStatement {
5002 Statement stmt;
5003 public Statement EmbeddedStatement {
5004 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5007 Expression var;
5008 Expression init;
5010 ExpressionStatement assign;
5012 public Using (Expression var, Expression init, Statement stmt, Location l)
5014 this.var = var;
5015 this.init = init;
5016 this.stmt = stmt;
5017 loc = l;
5020 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
5022 ec.Report.SymbolRelatedToPreviousError (expr.Type);
5023 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5024 TypeManager.CSharpName (expr.Type));
5027 protected override void EmitPreTryBody (EmitContext ec)
5029 assign.EmitStatement (ec);
5032 protected override void EmitTryBody (EmitContext ec)
5034 stmt.Emit (ec);
5037 protected override void EmitFinallyBody (EmitContext ec)
5039 ILGenerator ig = ec.ig;
5040 Label skip = ig.DefineLabel ();
5042 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5043 if (emit_null_check) {
5044 var.Emit (ec);
5045 ig.Emit (OpCodes.Brfalse, skip);
5048 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5050 if (emit_null_check)
5051 ig.MarkLabel (skip);
5054 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5056 assign.MutateHoistedGenericType (storey);
5057 var.MutateHoistedGenericType (storey);
5058 stmt.MutateHoistedGenericType (storey);
5061 public override bool Resolve (BlockContext ec)
5063 if (!ResolveVariable (ec))
5064 return false;
5066 ec.StartFlowBranching (this);
5068 bool ok = stmt.Resolve (ec);
5070 ec.EndFlowBranching ();
5072 ok &= base.Resolve (ec);
5074 if (TypeManager.void_dispose_void == null) {
5075 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5076 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5079 return ok;
5082 bool ResolveVariable (BlockContext ec)
5084 assign = new SimpleAssign (var, init, loc);
5085 assign = assign.ResolveStatement (ec);
5086 if (assign == null)
5087 return false;
5089 if (assign.Type == TypeManager.idisposable_type ||
5090 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5091 return true;
5094 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5095 if (e == null) {
5096 Error_IsNotConvertibleToIDisposable (ec, var);
5097 return false;
5100 throw new NotImplementedException ("covariance?");
5103 protected override void CloneTo (CloneContext clonectx, Statement t)
5105 Using target = (Using) t;
5107 target.var = var.Clone (clonectx);
5108 target.init = init.Clone (clonectx);
5109 target.stmt = stmt.Clone (clonectx);
5113 /// <summary>
5114 /// Implementation of the foreach C# statement
5115 /// </summary>
5116 public class Foreach : Statement {
5118 sealed class ArrayForeach : Statement
5120 class ArrayCounter : TemporaryVariable
5122 StatementExpression increment;
5124 public ArrayCounter (Location loc)
5125 : base (TypeManager.int32_type, loc)
5129 public void ResolveIncrement (BlockContext ec)
5131 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5132 increment.Resolve (ec);
5135 public void EmitIncrement (EmitContext ec)
5137 increment.Emit (ec);
5141 readonly Foreach for_each;
5142 readonly Statement statement;
5144 Expression conv;
5145 TemporaryVariable[] lengths;
5146 Expression [] length_exprs;
5147 ArrayCounter[] counter;
5149 TemporaryVariable copy;
5150 Expression access;
5152 public ArrayForeach (Foreach @foreach, int rank)
5154 for_each = @foreach;
5155 statement = for_each.statement;
5156 loc = @foreach.loc;
5158 counter = new ArrayCounter [rank];
5159 length_exprs = new Expression [rank];
5162 // Only use temporary length variables when dealing with
5163 // multi-dimensional arrays
5165 if (rank > 1)
5166 lengths = new TemporaryVariable [rank];
5169 protected override void CloneTo (CloneContext clonectx, Statement target)
5171 throw new NotImplementedException ();
5174 public override bool Resolve (BlockContext ec)
5176 copy = new TemporaryVariable (for_each.expr.Type, loc);
5177 copy.Resolve (ec);
5179 int rank = length_exprs.Length;
5180 Arguments list = new Arguments (rank);
5181 for (int i = 0; i < rank; i++) {
5182 counter [i] = new ArrayCounter (loc);
5183 counter [i].ResolveIncrement (ec);
5185 if (rank == 1) {
5186 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5187 } else {
5188 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5189 lengths [i].Resolve (ec);
5191 Arguments args = new Arguments (1);
5192 args.Add (new Argument (new IntConstant (i, loc)));
5193 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5196 list.Add (new Argument (counter [i]));
5199 access = new ElementAccess (copy, list).Resolve (ec);
5200 if (access == null)
5201 return false;
5203 Expression var_type = for_each.type;
5204 VarExpr ve = var_type as VarExpr;
5205 if (ve != null) {
5206 // Infer implicitly typed local variable from foreach array type
5207 var_type = new TypeExpression (access.Type, ve.Location);
5210 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5211 if (var_type == null)
5212 return false;
5214 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5215 if (conv == null)
5216 return false;
5218 bool ok = true;
5220 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5221 ec.CurrentBranching.CreateSibling ();
5223 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5224 if (for_each.variable == null)
5225 ok = false;
5227 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5228 if (!statement.Resolve (ec))
5229 ok = false;
5230 ec.EndFlowBranching ();
5232 // There's no direct control flow from the end of the embedded statement to the end of the loop
5233 ec.CurrentBranching.CurrentUsageVector.Goto ();
5235 ec.EndFlowBranching ();
5237 return ok;
5240 protected override void DoEmit (EmitContext ec)
5242 ILGenerator ig = ec.ig;
5244 copy.EmitAssign (ec, for_each.expr);
5246 int rank = length_exprs.Length;
5247 Label[] test = new Label [rank];
5248 Label[] loop = new Label [rank];
5250 for (int i = 0; i < rank; i++) {
5251 test [i] = ig.DefineLabel ();
5252 loop [i] = ig.DefineLabel ();
5254 if (lengths != null)
5255 lengths [i].EmitAssign (ec, length_exprs [i]);
5258 IntConstant zero = new IntConstant (0, loc);
5259 for (int i = 0; i < rank; i++) {
5260 counter [i].EmitAssign (ec, zero);
5262 ig.Emit (OpCodes.Br, test [i]);
5263 ig.MarkLabel (loop [i]);
5266 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5268 statement.Emit (ec);
5270 ig.MarkLabel (ec.LoopBegin);
5272 for (int i = rank - 1; i >= 0; i--){
5273 counter [i].EmitIncrement (ec);
5275 ig.MarkLabel (test [i]);
5276 counter [i].Emit (ec);
5278 if (lengths != null)
5279 lengths [i].Emit (ec);
5280 else
5281 length_exprs [i].Emit (ec);
5283 ig.Emit (OpCodes.Blt, loop [i]);
5286 ig.MarkLabel (ec.LoopEnd);
5289 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5291 for_each.expr.MutateHoistedGenericType (storey);
5293 copy.MutateHoistedGenericType (storey);
5294 conv.MutateHoistedGenericType (storey);
5295 statement.MutateHoistedGenericType (storey);
5297 for (int i = 0; i < counter.Length; i++) {
5298 counter [i].MutateHoistedGenericType (storey);
5299 if (lengths != null)
5300 lengths [i].MutateHoistedGenericType (storey);
5305 sealed class CollectionForeach : Statement
5307 class CollectionForeachStatement : Statement
5309 Type type;
5310 Expression variable, current, conv;
5311 Statement statement;
5312 Assign assign;
5314 public CollectionForeachStatement (Type type, Expression variable,
5315 Expression current, Statement statement,
5316 Location loc)
5318 this.type = type;
5319 this.variable = variable;
5320 this.current = current;
5321 this.statement = statement;
5322 this.loc = loc;
5325 protected override void CloneTo (CloneContext clonectx, Statement target)
5327 throw new NotImplementedException ();
5330 public override bool Resolve (BlockContext ec)
5332 current = current.Resolve (ec);
5333 if (current == null)
5334 return false;
5336 conv = Convert.ExplicitConversion (ec, current, type, loc);
5337 if (conv == null)
5338 return false;
5340 assign = new SimpleAssign (variable, conv, loc);
5341 if (assign.Resolve (ec) == null)
5342 return false;
5344 if (!statement.Resolve (ec))
5345 return false;
5347 return true;
5350 protected override void DoEmit (EmitContext ec)
5352 assign.EmitStatement (ec);
5353 statement.Emit (ec);
5356 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5358 assign.MutateHoistedGenericType (storey);
5359 statement.MutateHoistedGenericType (storey);
5363 Expression variable, expr;
5364 Statement statement;
5366 TemporaryVariable enumerator;
5367 Expression init;
5368 Statement loop;
5369 Statement wrapper;
5371 MethodGroupExpr get_enumerator;
5372 PropertyExpr get_current;
5373 MethodInfo move_next;
5374 Expression var_type;
5375 Type enumerator_type;
5376 bool enumerator_found;
5378 public CollectionForeach (Expression var_type, Expression var,
5379 Expression expr, Statement stmt, Location l)
5381 this.var_type = var_type;
5382 this.variable = var;
5383 this.expr = expr;
5384 statement = stmt;
5385 loc = l;
5388 protected override void CloneTo (CloneContext clonectx, Statement target)
5390 throw new NotImplementedException ();
5393 bool GetEnumeratorFilter (ResolveContext ec, MethodInfo mi)
5395 Type return_type = mi.ReturnType;
5398 // Ok, we can access it, now make sure that we can do something
5399 // with this `GetEnumerator'
5402 if (return_type == TypeManager.ienumerator_type ||
5403 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5405 // If it is not an interface, lets try to find the methods ourselves.
5406 // For example, if we have:
5407 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5408 // We can avoid the iface call. This is a runtime perf boost.
5409 // even bigger if we have a ValueType, because we avoid the cost
5410 // of boxing.
5412 // We have to make sure that both methods exist for us to take
5413 // this path. If one of the methods does not exist, we will just
5414 // use the interface. Sadly, this complex if statement is the only
5415 // way I could do this without a goto
5418 if (TypeManager.bool_movenext_void == null) {
5419 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5420 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5423 if (TypeManager.ienumerator_getcurrent == null) {
5424 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5425 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5429 // Prefer a generic enumerator over a non-generic one.
5431 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5432 enumerator_type = return_type;
5433 if (!FetchGetCurrent (ec, return_type))
5434 get_current = new PropertyExpr (
5435 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5436 if (!FetchMoveNext (return_type))
5437 move_next = TypeManager.bool_movenext_void;
5438 return true;
5441 if (return_type.IsInterface ||
5442 !FetchMoveNext (return_type) ||
5443 !FetchGetCurrent (ec, return_type)) {
5444 enumerator_type = return_type;
5445 move_next = TypeManager.bool_movenext_void;
5446 get_current = new PropertyExpr (
5447 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5448 return true;
5450 } else {
5452 // Ok, so they dont return an IEnumerable, we will have to
5453 // find if they support the GetEnumerator pattern.
5456 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5457 ec.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",
5458 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5459 return false;
5463 enumerator_type = return_type;
5465 return true;
5469 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5471 bool FetchMoveNext (Type t)
5473 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5474 MemberTypes.Method,
5475 BindingFlags.Public | BindingFlags.Instance,
5476 "MoveNext", null);
5478 if (move_next_list == null)
5479 return false;
5481 foreach (MemberInfo m in move_next_list){
5482 MethodInfo mi = (MethodInfo) m;
5484 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5485 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5486 move_next = mi;
5487 return true;
5491 return false;
5495 // Retrieves a `public T get_Current ()' method from the Type `t'
5497 bool FetchGetCurrent (ResolveContext ec, Type t)
5499 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5500 ec.CurrentType, t, "Current", MemberTypes.Property,
5501 Expression.AllBindingFlags, loc) as PropertyExpr;
5502 if (pe == null)
5503 return false;
5505 get_current = pe;
5506 return true;
5509 void Error_Enumerator (BlockContext ec)
5511 if (enumerator_found) {
5512 return;
5515 ec.Report.Error (1579, loc,
5516 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5517 TypeManager.CSharpName (expr.Type));
5520 bool IsOverride (MethodInfo m)
5522 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5524 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5525 return false;
5526 if (m is MethodBuilder)
5527 return true;
5529 MethodInfo base_method = m.GetBaseDefinition ();
5530 return base_method != m;
5533 bool TryType (ResolveContext ec, Type t)
5535 MethodGroupExpr mg = Expression.MemberLookup (ec.Compiler,
5536 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5537 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5538 if (mg == null)
5539 return false;
5541 MethodInfo result = null;
5542 MethodInfo tmp_move_next = null;
5543 PropertyExpr tmp_get_cur = null;
5544 Type tmp_enumerator_type = enumerator_type;
5545 foreach (MethodInfo mi in mg.Methods) {
5546 if (TypeManager.GetParameterData (mi).Count != 0)
5547 continue;
5549 // Check whether GetEnumerator is public
5550 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5551 continue;
5553 if (IsOverride (mi))
5554 continue;
5556 enumerator_found = true;
5558 if (!GetEnumeratorFilter (ec, mi))
5559 continue;
5561 if (result != null) {
5562 if (TypeManager.IsGenericType (result.ReturnType)) {
5563 if (!TypeManager.IsGenericType (mi.ReturnType))
5564 continue;
5566 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5567 ec.Report.SymbolRelatedToPreviousError (t);
5568 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5569 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5570 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5571 return false;
5574 // Always prefer generics enumerators
5575 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5576 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5577 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5578 continue;
5580 ec.Report.SymbolRelatedToPreviousError (result);
5581 ec.Report.SymbolRelatedToPreviousError (mi);
5582 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5583 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5584 return false;
5587 result = mi;
5588 tmp_move_next = move_next;
5589 tmp_get_cur = get_current;
5590 tmp_enumerator_type = enumerator_type;
5591 if (mi.DeclaringType == t)
5592 break;
5595 if (result != null) {
5596 move_next = tmp_move_next;
5597 get_current = tmp_get_cur;
5598 enumerator_type = tmp_enumerator_type;
5599 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5600 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5602 if (t != expr.Type) {
5603 expr = Convert.ExplicitConversion (
5604 ec, expr, t, loc);
5605 if (expr == null)
5606 throw new InternalErrorException ();
5609 get_enumerator.InstanceExpression = expr;
5610 get_enumerator.IsBase = t != expr.Type;
5612 return true;
5615 return false;
5618 bool ProbeCollectionType (ResolveContext ec, Type t)
5620 int errors = ec.Report.Errors;
5621 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5622 if (TryType (ec, tt))
5623 return true;
5624 tt = tt.BaseType;
5627 if (ec.Report.Errors > errors)
5628 return false;
5631 // Now try to find the method in the interfaces
5633 Type [] ifaces = TypeManager.GetInterfaces (t);
5634 foreach (Type i in ifaces){
5635 if (TryType (ec, i))
5636 return true;
5639 return false;
5642 public override bool Resolve (BlockContext ec)
5644 enumerator_type = TypeManager.ienumerator_type;
5646 if (!ProbeCollectionType (ec, expr.Type)) {
5647 Error_Enumerator (ec);
5648 return false;
5651 VarExpr ve = var_type as VarExpr;
5652 if (ve != null) {
5653 // Infer implicitly typed local variable from foreach enumerable type
5654 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5657 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5658 if (var_type == null)
5659 return false;
5661 enumerator = new TemporaryVariable (enumerator_type, loc);
5662 enumerator.Resolve (ec);
5664 init = new Invocation (get_enumerator, null);
5665 init = init.Resolve (ec);
5666 if (init == null)
5667 return false;
5669 Expression move_next_expr;
5671 MemberInfo[] mi = new MemberInfo[] { move_next };
5672 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5673 mg.InstanceExpression = enumerator;
5675 move_next_expr = new Invocation (mg, null);
5678 get_current.InstanceExpression = enumerator;
5680 Statement block = new CollectionForeachStatement (
5681 var_type.Type, variable, get_current, statement, loc);
5683 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5686 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5687 if (implements_idisposable || !enumerator_type.IsSealed) {
5688 wrapper = new DisposableWrapper (this, implements_idisposable);
5689 } else {
5690 wrapper = new NonDisposableWrapper (this);
5693 return wrapper.Resolve (ec);
5696 protected override void DoEmit (EmitContext ec)
5698 wrapper.Emit (ec);
5701 class NonDisposableWrapper : Statement {
5702 CollectionForeach parent;
5704 internal NonDisposableWrapper (CollectionForeach parent)
5706 this.parent = parent;
5709 protected override void CloneTo (CloneContext clonectx, Statement target)
5711 throw new NotSupportedException ();
5714 public override bool Resolve (BlockContext ec)
5716 return parent.ResolveLoop (ec);
5719 protected override void DoEmit (EmitContext ec)
5721 parent.EmitLoopInit (ec);
5722 parent.EmitLoopBody (ec);
5725 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5727 throw new NotSupportedException ();
5731 sealed class DisposableWrapper : ExceptionStatement
5733 CollectionForeach parent;
5734 bool implements_idisposable;
5736 internal DisposableWrapper (CollectionForeach parent, bool implements)
5738 this.parent = parent;
5739 this.implements_idisposable = implements;
5742 protected override void CloneTo (CloneContext clonectx, Statement target)
5744 throw new NotSupportedException ();
5747 public override bool Resolve (BlockContext ec)
5749 bool ok = true;
5751 ec.StartFlowBranching (this);
5753 if (!parent.ResolveLoop (ec))
5754 ok = false;
5756 ec.EndFlowBranching ();
5758 ok &= base.Resolve (ec);
5760 if (TypeManager.void_dispose_void == null) {
5761 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5762 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5764 return ok;
5767 protected override void EmitPreTryBody (EmitContext ec)
5769 parent.EmitLoopInit (ec);
5772 protected override void EmitTryBody (EmitContext ec)
5774 parent.EmitLoopBody (ec);
5777 protected override void EmitFinallyBody (EmitContext ec)
5779 Expression instance = parent.enumerator;
5780 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5781 ILGenerator ig = ec.ig;
5783 parent.enumerator.Emit (ec);
5785 Label call_dispose = ig.DefineLabel ();
5787 if (!implements_idisposable) {
5788 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5789 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5790 temp.Store (ec);
5791 temp.Emit (ec);
5792 instance = temp;
5795 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5797 // using 'endfinally' to empty the evaluation stack
5798 ig.Emit (OpCodes.Endfinally);
5799 ig.MarkLabel (call_dispose);
5802 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5805 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5807 throw new NotSupportedException ();
5811 bool ResolveLoop (BlockContext ec)
5813 return loop.Resolve (ec);
5816 void EmitLoopInit (EmitContext ec)
5818 enumerator.EmitAssign (ec, init);
5821 void EmitLoopBody (EmitContext ec)
5823 loop.Emit (ec);
5826 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5828 enumerator_type = storey.MutateType (enumerator_type);
5829 init.MutateHoistedGenericType (storey);
5830 loop.MutateHoistedGenericType (storey);
5834 Expression type;
5835 Expression variable;
5836 Expression expr;
5837 Statement statement;
5839 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5840 Statement stmt, Location l)
5842 this.type = type;
5843 this.variable = var;
5844 this.expr = expr;
5845 statement = stmt;
5846 loc = l;
5849 public Statement Statement {
5850 get { return statement; }
5853 public override bool Resolve (BlockContext ec)
5855 expr = expr.Resolve (ec);
5856 if (expr == null)
5857 return false;
5859 if (expr.IsNull) {
5860 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5861 return false;
5864 if (expr.Type == TypeManager.string_type) {
5865 statement = new ArrayForeach (this, 1);
5866 } else if (expr.Type.IsArray) {
5867 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5868 } else {
5869 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5870 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5871 expr.ExprClassName);
5872 return false;
5875 statement = new CollectionForeach (type, variable, expr, statement, loc);
5878 return statement.Resolve (ec);
5881 protected override void DoEmit (EmitContext ec)
5883 ILGenerator ig = ec.ig;
5885 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5886 ec.LoopBegin = ig.DefineLabel ();
5887 ec.LoopEnd = ig.DefineLabel ();
5889 statement.Emit (ec);
5891 ec.LoopBegin = old_begin;
5892 ec.LoopEnd = old_end;
5895 protected override void CloneTo (CloneContext clonectx, Statement t)
5897 Foreach target = (Foreach) t;
5899 target.type = type.Clone (clonectx);
5900 target.variable = variable.Clone (clonectx);
5901 target.expr = expr.Clone (clonectx);
5902 target.statement = statement.Clone (clonectx);
5905 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5907 statement.MutateHoistedGenericType (storey);