revert 144379
[mcs.git] / mcs / statement.cs
blobbb4a8007f7a1b358bf618457d993b7dae3669d38
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 public HoistedVariable HoistedVariableReference;
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 (HoistedVariableReference != 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);
1255 public bool IsThisAssigned (BlockContext ec, Block block)
1257 if (VariableInfo == null)
1258 throw new Exception ();
1260 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1261 return true;
1263 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1266 public bool IsAssigned (BlockContext ec)
1268 if (VariableInfo == null)
1269 throw new Exception ();
1271 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1274 public bool Resolve (ResolveContext ec)
1276 if (VariableType != null)
1277 return true;
1279 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1280 if (texpr == null)
1281 return false;
1283 VariableType = texpr.Type;
1285 if (TypeManager.IsGenericParameter (VariableType))
1286 return true;
1288 if (VariableType.IsAbstract && VariableType.IsSealed) {
1289 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1290 return false;
1293 if (VariableType.IsPointer && !ec.IsUnsafe)
1294 Expression.UnsafeError (ec, Location);
1296 return true;
1299 public bool IsConstant {
1300 get { return (flags & Flags.IsConstant) != 0; }
1301 set { flags |= Flags.IsConstant; }
1304 public bool AddressTaken {
1305 get { return (flags & Flags.AddressTaken) != 0; }
1306 set { flags |= Flags.AddressTaken; }
1309 public bool CompilerGenerated {
1310 get { return (flags & Flags.CompilerGenerated) != 0; }
1311 set { flags |= Flags.CompilerGenerated; }
1314 public override string ToString ()
1316 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1317 Name, Type, VariableInfo, Location);
1320 public bool Used {
1321 get { return (flags & Flags.Used) != 0; }
1322 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1325 public bool ReadOnly {
1326 get { return (flags & Flags.ReadOnly) != 0; }
1329 public void SetReadOnlyContext (ReadOnlyContext context)
1331 flags |= Flags.ReadOnly;
1332 ro_context = context;
1335 public string GetReadOnlyContext ()
1337 if (!ReadOnly)
1338 throw new InternalErrorException ("Variable is not readonly");
1340 switch (ro_context) {
1341 case ReadOnlyContext.Fixed:
1342 return "fixed variable";
1343 case ReadOnlyContext.Foreach:
1344 return "foreach iteration variable";
1345 case ReadOnlyContext.Using:
1346 return "using variable";
1348 throw new NotImplementedException ();
1352 // Whether the variable is pinned, if Pinned the variable has been
1353 // allocated in a pinned slot with DeclareLocal.
1355 public bool Pinned {
1356 get { return (flags & Flags.Pinned) != 0; }
1357 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1360 public bool IsThis {
1361 get { return (flags & Flags.IsThis) != 0; }
1362 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1365 Block IKnownVariable.Block {
1366 get { return Block; }
1369 Location IKnownVariable.Location {
1370 get { return Location; }
1373 public LocalInfo Clone (CloneContext clonectx)
1376 // Variables in anonymous block are not resolved yet
1378 if (VariableType == null)
1379 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1382 // Variables in method block are resolved
1384 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1385 li.VariableType = VariableType;
1386 return li;
1390 /// <summary>
1391 /// Block represents a C# block.
1392 /// </summary>
1394 /// <remarks>
1395 /// This class is used in a number of places: either to represent
1396 /// explicit blocks that the programmer places or implicit blocks.
1398 /// Implicit blocks are used as labels or to introduce variable
1399 /// declarations.
1401 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1402 /// they contain extra information that is not necessary on normal blocks.
1403 /// </remarks>
1404 public class Block : Statement {
1405 public Block Parent;
1406 public Location StartLocation;
1407 public Location EndLocation = Location.Null;
1409 public ExplicitBlock Explicit;
1410 public ToplevelBlock Toplevel; // TODO: Use Explicit
1412 [Flags]
1413 public enum Flags : byte {
1414 Unchecked = 1,
1415 BlockUsed = 2,
1416 VariablesInitialized = 4,
1417 HasRet = 8,
1418 Unsafe = 16,
1419 IsIterator = 32,
1420 HasCapturedVariable = 64,
1421 HasCapturedThis = 128
1423 protected Flags flags;
1425 public bool Unchecked {
1426 get { return (flags & Flags.Unchecked) != 0; }
1427 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1430 public bool Unsafe {
1431 get { return (flags & Flags.Unsafe) != 0; }
1432 set { flags |= Flags.Unsafe; }
1436 // The statements in this block
1438 protected ArrayList statements;
1441 // An array of Blocks. We keep track of children just
1442 // to generate the local variable declarations.
1444 // Statements and child statements are handled through the
1445 // statements.
1447 ArrayList children;
1450 // Labels. (label, block) pairs.
1452 protected HybridDictionary labels;
1455 // Keeps track of (name, type) pairs
1457 IDictionary variables;
1460 // Keeps track of constants
1461 HybridDictionary constants;
1464 // Temporary variables.
1466 ArrayList temporary_variables;
1469 // If this is a switch section, the enclosing switch block.
1471 Block switch_block;
1473 protected ArrayList scope_initializers;
1475 ArrayList anonymous_children;
1477 protected static int id;
1479 int this_id;
1481 int assignable_slots;
1482 bool unreachable_shown;
1483 bool unreachable;
1485 public Block (Block parent)
1486 : this (parent, (Flags) 0, Location.Null, Location.Null)
1489 public Block (Block parent, Flags flags)
1490 : this (parent, flags, Location.Null, Location.Null)
1493 public Block (Block parent, Location start, Location end)
1494 : this (parent, (Flags) 0, start, end)
1498 // Useful when TopLevel block is downgraded to normal block
1500 public Block (ToplevelBlock parent, ToplevelBlock source)
1501 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1503 statements = source.statements;
1504 children = source.children;
1505 labels = source.labels;
1506 variables = source.variables;
1507 constants = source.constants;
1508 switch_block = source.switch_block;
1511 public Block (Block parent, Flags flags, Location start, Location end)
1513 if (parent != null) {
1514 parent.AddChild (this);
1516 // the appropriate constructors will fixup these fields
1517 Toplevel = parent.Toplevel;
1518 Explicit = parent.Explicit;
1521 this.Parent = parent;
1522 this.flags = flags;
1523 this.StartLocation = start;
1524 this.EndLocation = end;
1525 this.loc = start;
1526 this_id = id++;
1527 statements = new ArrayList (4);
1530 public Block CreateSwitchBlock (Location start)
1532 // FIXME: should this be implicit?
1533 Block new_block = new ExplicitBlock (this, start, start);
1534 new_block.switch_block = this;
1535 return new_block;
1538 public int ID {
1539 get { return this_id; }
1542 public IDictionary Variables {
1543 get {
1544 if (variables == null)
1545 variables = new ListDictionary ();
1546 return variables;
1550 void AddChild (Block b)
1552 if (children == null)
1553 children = new ArrayList (1);
1555 children.Add (b);
1558 public void SetEndLocation (Location loc)
1560 EndLocation = loc;
1563 protected void Error_158 (string name, Location loc)
1565 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1566 "by the same name in a contained scope", name);
1569 /// <summary>
1570 /// Adds a label to the current block.
1571 /// </summary>
1573 /// <returns>
1574 /// false if the name already exists in this block. true
1575 /// otherwise.
1576 /// </returns>
1578 public bool AddLabel (LabeledStatement target)
1580 if (switch_block != null)
1581 return switch_block.AddLabel (target);
1583 string name = target.Name;
1585 Block cur = this;
1586 while (cur != null) {
1587 LabeledStatement s = cur.DoLookupLabel (name);
1588 if (s != null) {
1589 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1590 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1591 return false;
1594 if (this == Explicit)
1595 break;
1597 cur = cur.Parent;
1600 while (cur != null) {
1601 if (cur.DoLookupLabel (name) != null) {
1602 Error_158 (name, target.loc);
1603 return false;
1606 if (children != null) {
1607 foreach (Block b in children) {
1608 LabeledStatement s = b.DoLookupLabel (name);
1609 if (s == null)
1610 continue;
1612 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1613 Error_158 (name, target.loc);
1614 return false;
1618 cur = cur.Parent;
1621 Toplevel.CheckError158 (name, target.loc);
1623 if (labels == null)
1624 labels = new HybridDictionary();
1626 labels.Add (name, target);
1627 return true;
1630 public LabeledStatement LookupLabel (string name)
1632 LabeledStatement s = DoLookupLabel (name);
1633 if (s != null)
1634 return s;
1636 if (children == null)
1637 return null;
1639 foreach (Block child in children) {
1640 if (Explicit != child.Explicit)
1641 continue;
1643 s = child.LookupLabel (name);
1644 if (s != null)
1645 return s;
1648 return null;
1651 LabeledStatement DoLookupLabel (string name)
1653 if (switch_block != null)
1654 return switch_block.LookupLabel (name);
1656 if (labels != null)
1657 if (labels.Contains (name))
1658 return ((LabeledStatement) labels [name]);
1660 return null;
1663 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1665 Block b = this;
1666 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1667 while (kvi == null) {
1668 b = b.Explicit.Parent;
1669 if (b == null)
1670 return true;
1671 kvi = b.Explicit.GetKnownVariable (name);
1674 if (kvi.Block == b)
1675 return true;
1677 // Is kvi.Block nested inside 'b'
1678 if (b.Explicit != kvi.Block.Explicit) {
1680 // If a variable by the same name it defined in a nested block of this
1681 // block, we violate the invariant meaning in a block.
1683 if (b == this) {
1684 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1685 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1686 return false;
1690 // It's ok if the definition is in a nested subblock of b, but not
1691 // nested inside this block -- a definition in a sibling block
1692 // should not affect us.
1694 return true;
1698 // Block 'b' and kvi.Block are the same textual block.
1699 // However, different variables are extant.
1701 // Check if the variable is in scope in both blocks. We use
1702 // an indirect check that depends on AddVariable doing its
1703 // part in maintaining the invariant-meaning-in-block property.
1705 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1706 return true;
1708 if (this is ToplevelBlock) {
1709 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1710 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1711 return false;
1715 // Even though we detected the error when the name is used, we
1716 // treat it as if the variable declaration was in error.
1718 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1719 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1720 return false;
1723 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1725 LocalInfo vi = GetLocalInfo (name);
1726 if (vi != null) {
1727 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1728 if (Explicit == vi.Block.Explicit) {
1729 Error_AlreadyDeclared (l, name, null);
1730 } else {
1731 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1732 "parent or current" : "parent");
1734 return false;
1737 if (block != null) {
1738 Expression e = block.GetParameterReference (name, Location.Null);
1739 if (e != null) {
1740 ParameterReference pr = e as ParameterReference;
1741 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1742 Error_AlreadyDeclared (loc, name);
1743 else
1744 Error_AlreadyDeclared (loc, name, "parent or current");
1745 return false;
1749 return true;
1752 public LocalInfo AddVariable (Expression type, string name, Location l)
1754 if (!CheckParentConflictName (Toplevel, name, l))
1755 return null;
1757 if (Toplevel.GenericMethod != null) {
1758 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1759 if (tp.Name == name) {
1760 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1761 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1762 return null;
1767 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1768 if (kvi != null) {
1769 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1770 Error_AlreadyDeclared (l, name, "child");
1771 return null;
1774 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1775 AddVariable (vi);
1777 if ((flags & Flags.VariablesInitialized) != 0)
1778 throw new InternalErrorException ("block has already been resolved");
1780 return vi;
1783 protected virtual void AddVariable (LocalInfo li)
1785 Variables.Add (li.Name, li);
1786 Explicit.AddKnownVariable (li.Name, li);
1789 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1791 if (reason == null) {
1792 Error_AlreadyDeclared (loc, var);
1793 return;
1796 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1797 "in this scope because it would give a different meaning " +
1798 "to `{0}', which is already used in a `{1}' scope " +
1799 "to denote something else", var, reason);
1802 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1804 Toplevel.Report.Error (128, loc,
1805 "A local variable named `{0}' is already defined in this scope", name);
1808 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1810 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1811 name, conflict);
1814 public bool AddConstant (Expression type, string name, Expression value, Location l)
1816 if (AddVariable (type, name, l) == null)
1817 return false;
1819 if (constants == null)
1820 constants = new HybridDictionary();
1822 constants.Add (name, value);
1824 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1825 Use ();
1826 return true;
1829 static int next_temp_id = 0;
1831 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1833 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1835 if (temporary_variables == null)
1836 temporary_variables = new ArrayList ();
1838 int id = ++next_temp_id;
1839 string name = "$s_" + id.ToString ();
1841 LocalInfo li = new LocalInfo (te, name, this, loc);
1842 li.CompilerGenerated = true;
1843 temporary_variables.Add (li);
1844 return li;
1847 public LocalInfo GetLocalInfo (string name)
1849 LocalInfo ret;
1850 for (Block b = this; b != null; b = b.Parent) {
1851 if (b.variables != null) {
1852 ret = (LocalInfo) b.variables [name];
1853 if (ret != null)
1854 return ret;
1858 return null;
1861 public Expression GetVariableType (string name)
1863 LocalInfo vi = GetLocalInfo (name);
1864 return vi == null ? null : vi.Type;
1867 public Expression GetConstantExpression (string name)
1869 for (Block b = this; b != null; b = b.Parent) {
1870 if (b.constants != null) {
1871 Expression ret = b.constants [name] as Expression;
1872 if (ret != null)
1873 return ret;
1876 return null;
1880 // It should be used by expressions which require to
1881 // register a statement during resolve process.
1883 public void AddScopeStatement (Statement s)
1885 if (scope_initializers == null)
1886 scope_initializers = new ArrayList ();
1888 scope_initializers.Add (s);
1891 public void AddStatement (Statement s)
1893 statements.Add (s);
1894 flags |= Flags.BlockUsed;
1897 public bool Used {
1898 get { return (flags & Flags.BlockUsed) != 0; }
1901 public void Use ()
1903 flags |= Flags.BlockUsed;
1906 public bool HasRet {
1907 get { return (flags & Flags.HasRet) != 0; }
1910 public int AssignableSlots {
1911 get {
1912 // TODO: Re-enable
1913 // if ((flags & Flags.VariablesInitialized) == 0)
1914 // throw new Exception ("Variables have not been initialized yet");
1915 return assignable_slots;
1919 public ArrayList AnonymousChildren {
1920 get { return anonymous_children; }
1923 public void AddAnonymousChild (ToplevelBlock b)
1925 if (anonymous_children == null)
1926 anonymous_children = new ArrayList ();
1928 anonymous_children.Add (b);
1931 void DoResolveConstants (BlockContext ec)
1933 if (constants == null)
1934 return;
1936 if (variables == null)
1937 throw new InternalErrorException ("cannot happen");
1939 foreach (DictionaryEntry de in variables) {
1940 string name = (string) de.Key;
1941 LocalInfo vi = (LocalInfo) de.Value;
1942 Type variable_type = vi.VariableType;
1944 if (variable_type == null) {
1945 if (vi.Type is VarExpr)
1946 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1948 continue;
1951 Expression cv = (Expression) constants [name];
1952 if (cv == null)
1953 continue;
1955 // Don't let 'const int Foo = Foo;' succeed.
1956 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1957 // which in turn causes the 'must be constant' error to be triggered.
1958 constants.Remove (name);
1960 if (!Const.IsConstantTypeValid (variable_type)) {
1961 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1962 continue;
1965 ec.CurrentBlock = this;
1966 Expression e;
1967 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1968 e = cv.Resolve (ec);
1970 if (e == null)
1971 continue;
1973 Constant ce = e as Constant;
1974 if (ce == null) {
1975 Const.Error_ExpressionMustBeConstant (vi.Location, name, ec.Report);
1976 continue;
1979 e = ce.ConvertImplicitly (variable_type);
1980 if (e == null) {
1981 if (TypeManager.IsReferenceType (variable_type))
1982 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name, ec.Report);
1983 else
1984 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1985 continue;
1988 constants.Add (name, e);
1989 vi.IsConstant = true;
1993 protected void ResolveMeta (BlockContext ec, int offset)
1995 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1997 // If some parent block was unsafe, we remain unsafe even if this block
1998 // isn't explicitly marked as such.
1999 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2000 flags |= Flags.VariablesInitialized;
2002 if (variables != null) {
2003 foreach (LocalInfo li in variables.Values) {
2004 if (!li.Resolve (ec))
2005 continue;
2006 li.VariableInfo = new VariableInfo (li, offset);
2007 offset += li.VariableInfo.Length;
2010 assignable_slots = offset;
2012 DoResolveConstants (ec);
2014 if (children == null)
2015 return;
2016 foreach (Block b in children)
2017 b.ResolveMeta (ec, offset);
2022 // Emits the local variable declarations for a block
2024 public virtual void EmitMeta (EmitContext ec)
2026 if (variables != null){
2027 foreach (LocalInfo vi in variables.Values)
2028 vi.ResolveVariable (ec);
2031 if (temporary_variables != null) {
2032 for (int i = 0; i < temporary_variables.Count; i++)
2033 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2036 if (children != null) {
2037 for (int i = 0; i < children.Count; i++)
2038 ((Block)children[i]).EmitMeta(ec);
2042 void UsageWarning (BlockContext ec)
2044 if (variables == null || ec.Report.WarningLevel < 3)
2045 return;
2047 foreach (DictionaryEntry de in variables) {
2048 LocalInfo vi = (LocalInfo) de.Value;
2050 if (!vi.Used) {
2051 string name = (string) de.Key;
2053 // vi.VariableInfo can be null for 'catch' variables
2054 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2055 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2056 else
2057 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2062 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2064 Statement body;
2066 // Some statements are wrapped by a Block. Since
2067 // others' internal could be changed, here I treat
2068 // them as possibly wrapped by Block equally.
2069 Block b = s as Block;
2070 if (b != null && b.statements.Count == 1)
2071 s = (Statement) b.statements [0];
2073 if (s is Lock)
2074 body = ((Lock) s).Statement;
2075 else if (s is For)
2076 body = ((For) s).Statement;
2077 else if (s is Foreach)
2078 body = ((Foreach) s).Statement;
2079 else if (s is While)
2080 body = ((While) s).Statement;
2081 else if (s is Fixed)
2082 body = ((Fixed) s).Statement;
2083 else if (s is Using)
2084 body = ((Using) s).EmbeddedStatement;
2085 else if (s is UsingTemporary)
2086 body = ((UsingTemporary) s).Statement;
2087 else
2088 return;
2090 if (body == null || body is EmptyStatement)
2091 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2094 public override bool Resolve (BlockContext ec)
2096 Block prev_block = ec.CurrentBlock;
2097 bool ok = true;
2099 int errors = ec.Report.Errors;
2101 ec.CurrentBlock = this;
2102 ec.StartFlowBranching (this);
2104 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2107 // Compiler generated scope statements
2109 if (scope_initializers != null) {
2110 foreach (Statement s in scope_initializers)
2111 s.Resolve (ec);
2115 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2116 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2117 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2118 // responsible for handling the situation.
2120 int statement_count = statements.Count;
2121 for (int ix = 0; ix < statement_count; ix++){
2122 Statement s = (Statement) statements [ix];
2123 // Check possible empty statement (CS0642)
2124 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2125 statements [ix + 1] is ExplicitBlock)
2126 CheckPossibleMistakenEmptyStatement (ec, s);
2129 // Warn if we detect unreachable code.
2131 if (unreachable) {
2132 if (s is EmptyStatement)
2133 continue;
2135 if (!unreachable_shown && !(s is LabeledStatement)) {
2136 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2137 unreachable_shown = true;
2140 Block c_block = s as Block;
2141 if (c_block != null)
2142 c_block.unreachable = c_block.unreachable_shown = true;
2146 // Note that we're not using ResolveUnreachable() for unreachable
2147 // statements here. ResolveUnreachable() creates a temporary
2148 // flow branching and kills it afterwards. This leads to problems
2149 // if you have two unreachable statements where the first one
2150 // assigns a variable and the second one tries to access it.
2153 if (!s.Resolve (ec)) {
2154 ok = false;
2155 if (ec.IsInProbingMode)
2156 break;
2158 statements [ix] = EmptyStatement.Value;
2159 continue;
2162 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2163 statements [ix] = EmptyStatement.Value;
2165 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2166 if (unreachable && s is LabeledStatement)
2167 throw new InternalErrorException ("should not happen");
2170 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2171 ec.CurrentBranching, statement_count);
2173 while (ec.CurrentBranching is FlowBranchingLabeled)
2174 ec.EndFlowBranching ();
2176 bool flow_unreachable = ec.EndFlowBranching ();
2178 ec.CurrentBlock = prev_block;
2180 if (flow_unreachable)
2181 flags |= Flags.HasRet;
2183 // If we're a non-static `struct' constructor which doesn't have an
2184 // initializer, then we must initialize all of the struct's fields.
2185 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2186 ok = false;
2188 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2189 foreach (LabeledStatement label in labels.Values)
2190 if (!label.HasBeenReferenced)
2191 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2194 if (ok && errors == ec.Report.Errors)
2195 UsageWarning (ec);
2197 return ok;
2200 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2202 unreachable_shown = true;
2203 unreachable = true;
2205 if (warn)
2206 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2208 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2209 bool ok = Resolve (ec);
2210 ec.KillFlowBranching ();
2212 return ok;
2215 protected override void DoEmit (EmitContext ec)
2217 for (int ix = 0; ix < statements.Count; ix++){
2218 Statement s = (Statement) statements [ix];
2219 s.Emit (ec);
2223 public override void Emit (EmitContext ec)
2225 if (scope_initializers != null)
2226 EmitScopeInitializers (ec);
2228 ec.Mark (StartLocation);
2229 DoEmit (ec);
2231 if (SymbolWriter.HasSymbolWriter)
2232 EmitSymbolInfo (ec);
2235 protected void EmitScopeInitializers (EmitContext ec)
2237 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2239 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2240 foreach (Statement s in scope_initializers)
2241 s.Emit (ec);
2244 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2247 protected virtual void EmitSymbolInfo (EmitContext ec)
2249 if (variables != null) {
2250 foreach (LocalInfo vi in variables.Values) {
2251 vi.EmitSymbolInfo (ec);
2256 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2258 MutateVariables (storey);
2260 if (scope_initializers != null) {
2261 foreach (Statement s in scope_initializers)
2262 s.MutateHoistedGenericType (storey);
2265 foreach (Statement s in statements)
2266 s.MutateHoistedGenericType (storey);
2269 void MutateVariables (AnonymousMethodStorey storey)
2271 if (variables != null) {
2272 foreach (LocalInfo vi in variables.Values) {
2273 vi.VariableType = storey.MutateType (vi.VariableType);
2277 if (temporary_variables != null) {
2278 foreach (LocalInfo vi in temporary_variables)
2279 vi.VariableType = storey.MutateType (vi.VariableType);
2283 public override string ToString ()
2285 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2288 protected override void CloneTo (CloneContext clonectx, Statement t)
2290 Block target = (Block) t;
2292 clonectx.AddBlockMap (this, target);
2294 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2295 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2296 if (Parent != null)
2297 target.Parent = clonectx.RemapBlockCopy (Parent);
2299 if (variables != null){
2300 target.variables = new Hashtable ();
2302 foreach (DictionaryEntry de in variables){
2303 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2304 target.variables [de.Key] = newlocal;
2305 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2309 target.statements = new ArrayList (statements.Count);
2310 foreach (Statement s in statements)
2311 target.statements.Add (s.Clone (clonectx));
2313 if (target.children != null){
2314 target.children = new ArrayList (children.Count);
2315 foreach (Block b in children){
2316 target.children.Add (clonectx.LookupBlock (b));
2321 // TODO: labels, switch_block, constants (?), anonymous_children
2326 public class ExplicitBlock : Block {
2327 HybridDictionary known_variables;
2328 protected AnonymousMethodStorey am_storey;
2330 public ExplicitBlock (Block parent, Location start, Location end)
2331 : this (parent, (Flags) 0, start, end)
2335 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2336 : base (parent, flags, start, end)
2338 this.Explicit = this;
2341 // <summary>
2342 // Marks a variable with name @name as being used in this or a child block.
2343 // If a variable name has been used in a child block, it's illegal to
2344 // declare a variable with the same name in the current block.
2345 // </summary>
2346 internal void AddKnownVariable (string name, IKnownVariable info)
2348 if (known_variables == null)
2349 known_variables = new HybridDictionary();
2351 known_variables [name] = info;
2353 if (Parent != null)
2354 Parent.Explicit.AddKnownVariable (name, info);
2357 public AnonymousMethodStorey AnonymousMethodStorey {
2358 get { return am_storey; }
2362 // Creates anonymous method storey in current block
2364 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2367 // When referencing a variable in iterator storey from children anonymous method
2369 if (Toplevel.am_storey is IteratorStorey) {
2370 return Toplevel.am_storey;
2374 // An iterator has only 1 storey block
2376 if (ec.CurrentIterator != null)
2377 return ec.CurrentIterator.Storey;
2379 if (am_storey == null) {
2380 MemberBase mc = ec.MemberContext as MemberBase;
2381 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2384 // Creates anonymous method storey for this block
2386 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2389 return am_storey;
2392 public override void Emit (EmitContext ec)
2394 if (am_storey != null)
2395 am_storey.EmitStoreyInstantiation (ec);
2397 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2398 if (emit_debug_info)
2399 ec.BeginScope ();
2401 base.Emit (ec);
2403 if (emit_debug_info)
2404 ec.EndScope ();
2407 public override void EmitMeta (EmitContext ec)
2410 // Creates anonymous method storey
2412 if (am_storey != null) {
2413 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2415 // Creates parent storey reference when hoisted this is accessible
2417 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2418 ExplicitBlock parent = Toplevel.Parent.Explicit;
2421 // Hoisted this exists in top-level parent storey only
2423 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2424 parent = parent.Parent.Explicit;
2426 am_storey.AddParentStoreyReference (parent.am_storey);
2429 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2432 am_storey.DefineType ();
2433 am_storey.ResolveType ();
2434 am_storey.Define ();
2435 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2437 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2438 if (ref_blocks != null) {
2439 foreach (ExplicitBlock ref_block in ref_blocks) {
2440 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2441 if (b.am_storey != null) {
2442 b.am_storey.AddParentStoreyReference (am_storey);
2444 // Stop propagation inside same top block
2445 if (b.Toplevel == Toplevel)
2446 break;
2448 b = b.Toplevel;
2450 b.HasCapturedVariable = true;
2456 base.EmitMeta (ec);
2459 internal IKnownVariable GetKnownVariable (string name)
2461 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2464 public bool HasCapturedThis
2466 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2467 get { return (flags & Flags.HasCapturedThis) != 0; }
2470 public bool HasCapturedVariable
2472 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2473 get { return (flags & Flags.HasCapturedVariable) != 0; }
2476 protected override void CloneTo (CloneContext clonectx, Statement t)
2478 ExplicitBlock target = (ExplicitBlock) t;
2479 target.known_variables = null;
2480 base.CloneTo (clonectx, t);
2484 public class ToplevelParameterInfo : IKnownVariable {
2485 public readonly ToplevelBlock Block;
2486 public readonly int Index;
2487 public VariableInfo VariableInfo;
2489 Block IKnownVariable.Block {
2490 get { return Block; }
2492 public Parameter Parameter {
2493 get { return Block.Parameters [Index]; }
2496 public Type ParameterType {
2497 get { return Block.Parameters.Types [Index]; }
2500 public Location Location {
2501 get { return Parameter.Location; }
2504 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2506 this.Block = block;
2507 this.Index = idx;
2512 // A toplevel block contains extra information, the split is done
2513 // only to separate information that would otherwise bloat the more
2514 // lightweight Block.
2516 // In particular, this was introduced when the support for Anonymous
2517 // Methods was implemented.
2519 public class ToplevelBlock : ExplicitBlock
2522 // Block is converted to an expression
2524 sealed class BlockScopeExpression : Expression
2526 Expression child;
2527 readonly ToplevelBlock block;
2529 public BlockScopeExpression (Expression child, ToplevelBlock block)
2531 this.child = child;
2532 this.block = block;
2535 public override Expression CreateExpressionTree (ResolveContext ec)
2537 throw new NotSupportedException ();
2540 public override Expression DoResolve (ResolveContext ec)
2542 if (child == null)
2543 return null;
2545 child = child.Resolve (ec);
2546 if (child == null)
2547 return null;
2549 eclass = child.eclass;
2550 type = child.Type;
2551 return this;
2554 public override void Emit (EmitContext ec)
2556 block.EmitMeta (ec);
2557 block.EmitScopeInitializers (ec);
2558 child.Emit (ec);
2561 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2563 type = storey.MutateType (type);
2564 child.MutateHoistedGenericType (storey);
2565 block.MutateHoistedGenericType (storey);
2569 GenericMethod generic;
2570 protected ParametersCompiled parameters;
2571 ToplevelParameterInfo[] parameter_info;
2572 LocalInfo this_variable;
2573 bool resolved;
2574 bool unreachable;
2575 CompilerContext compiler;
2577 public HoistedVariable HoistedThisVariable;
2579 public bool Resolved {
2580 get {
2581 return resolved;
2586 // The parameters for the block.
2588 public ParametersCompiled Parameters {
2589 get { return parameters; }
2592 public Report Report {
2593 get { return compiler.Report; }
2596 public GenericMethod GenericMethod {
2597 get { return generic; }
2600 public ToplevelBlock Container {
2601 get { return Parent == null ? null : Parent.Toplevel; }
2604 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2605 this (ctx, parent, (Flags) 0, parameters, start)
2609 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2610 this (ctx, parent, parameters, start)
2612 this.generic = generic;
2615 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2616 this (ctx, null, (Flags) 0, parameters, start)
2620 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2621 this (ctx, null, flags, parameters, start)
2625 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2626 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2627 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2628 base (null, flags, start, Location.Null)
2630 this.compiler = ctx;
2631 this.Toplevel = this;
2633 this.parameters = parameters;
2634 this.Parent = parent;
2635 if (parent != null)
2636 parent.AddAnonymousChild (this);
2638 if (!this.parameters.IsEmpty)
2639 ProcessParameters ();
2642 public ToplevelBlock (CompilerContext ctx, Location loc)
2643 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2647 protected override void CloneTo (CloneContext clonectx, Statement t)
2649 ToplevelBlock target = (ToplevelBlock) t;
2650 base.CloneTo (clonectx, t);
2652 if (parameters.Count != 0)
2653 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2654 for (int i = 0; i < parameters.Count; ++i)
2655 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2658 public bool CheckError158 (string name, Location loc)
2660 if (AnonymousChildren != null) {
2661 foreach (ToplevelBlock child in AnonymousChildren) {
2662 if (!child.CheckError158 (name, loc))
2663 return false;
2667 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2668 if (!c.DoCheckError158 (name, loc))
2669 return false;
2672 return true;
2675 void ProcessParameters ()
2677 int n = parameters.Count;
2678 parameter_info = new ToplevelParameterInfo [n];
2679 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2680 for (int i = 0; i < n; ++i) {
2681 parameter_info [i] = new ToplevelParameterInfo (this, i);
2683 Parameter p = parameters [i];
2684 if (p == null)
2685 continue;
2687 string name = p.Name;
2688 if (CheckParentConflictName (top_parent, name, loc))
2689 AddKnownVariable (name, parameter_info [i]);
2692 // mark this block as "used" so that we create local declarations in a sub-block
2693 // FIXME: This appears to uncover a lot of bugs
2694 //this.Use ();
2697 bool DoCheckError158 (string name, Location loc)
2699 LabeledStatement s = LookupLabel (name);
2700 if (s != null) {
2701 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2702 Error_158 (name, loc);
2703 return false;
2706 return true;
2709 public override Expression CreateExpressionTree (ResolveContext ec)
2711 if (statements.Count == 1) {
2712 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2713 if (scope_initializers != null)
2714 expr = new BlockScopeExpression (expr, this);
2716 return expr;
2719 return base.CreateExpressionTree (ec);
2723 // Reformats this block to be top-level iterator block
2725 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2727 IsIterator = true;
2729 // Creates block with original statements
2730 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2732 source.statements = new ArrayList (1);
2733 source.AddStatement (new Return (iterator, iterator.Location));
2734 source.IsIterator = false;
2736 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2737 source.am_storey = iterator_storey;
2738 return iterator_storey;
2742 // Returns a parameter reference expression for the given name,
2743 // or null if there is no such parameter
2745 public Expression GetParameterReference (string name, Location loc)
2747 for (ToplevelBlock t = this; t != null; t = t.Container) {
2748 Expression expr = t.GetParameterReferenceExpression (name, loc);
2749 if (expr != null)
2750 return expr;
2753 return null;
2756 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2758 int idx = parameters.GetParameterIndexByName (name);
2759 return idx < 0 ?
2760 null : new ParameterReference (parameter_info [idx], loc);
2763 // <summary>
2764 // Returns the "this" instance variable of this block.
2765 // See AddThisVariable() for more information.
2766 // </summary>
2767 public LocalInfo ThisVariable {
2768 get { return this_variable; }
2771 // <summary>
2772 // This is used by non-static `struct' constructors which do not have an
2773 // initializer - in this case, the constructor must initialize all of the
2774 // struct's fields. To do this, we add a "this" variable and use the flow
2775 // analysis code to ensure that it's been fully initialized before control
2776 // leaves the constructor.
2777 // </summary>
2778 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2780 if (this_variable == null) {
2781 this_variable = new LocalInfo (ds, this, l);
2782 this_variable.Used = true;
2783 this_variable.IsThis = true;
2785 Variables.Add ("this", this_variable);
2788 return this_variable;
2791 public bool IsIterator {
2792 get { return (flags & Flags.IsIterator) != 0; }
2793 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2796 public bool IsThisAssigned (BlockContext ec)
2798 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2801 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2803 if (resolved)
2804 return true;
2806 resolved = true;
2808 try {
2809 if (!ResolveMeta (rc, ip))
2810 return false;
2812 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2813 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2815 if (!Resolve (rc))
2816 return false;
2818 unreachable = top_level.End ();
2820 } catch (Exception) {
2821 #if PRODUCTION
2822 if (rc.CurrentBlock != null) {
2823 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2824 } else {
2825 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2827 #endif
2828 throw;
2831 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2832 if (rc.CurrentAnonymousMethod == null) {
2833 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2834 return false;
2835 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2836 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2837 rc.CurrentAnonymousMethod.GetSignatureForError ());
2838 return false;
2842 return true;
2845 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2847 int errors = ec.Report.Errors;
2848 int orig_count = parameters.Count;
2850 if (ip != null)
2851 parameters = ip;
2853 // Assert: orig_count != parameter.Count => orig_count == 0
2854 if (orig_count != 0 && orig_count != parameters.Count)
2855 throw new InternalErrorException ("parameter information mismatch");
2857 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2859 for (int i = 0; i < orig_count; ++i) {
2860 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2862 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2863 continue;
2865 VariableInfo vi = new VariableInfo (ip, i, offset);
2866 parameter_info [i].VariableInfo = vi;
2867 offset += vi.Length;
2870 ResolveMeta (ec, offset);
2872 return ec.Report.Errors == errors;
2875 // <summary>
2876 // Check whether all `out' parameters have been assigned.
2877 // </summary>
2878 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2880 if (vector.IsUnreachable)
2881 return;
2883 int n = parameter_info == null ? 0 : parameter_info.Length;
2885 for (int i = 0; i < n; i++) {
2886 VariableInfo var = parameter_info [i].VariableInfo;
2888 if (var == null)
2889 continue;
2891 if (vector.IsAssigned (var, false))
2892 continue;
2894 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2895 var.Name);
2899 public override void Emit (EmitContext ec)
2901 if (Report.Errors > 0)
2902 return;
2904 #if PRODUCTION
2905 try {
2906 #endif
2907 EmitMeta (ec);
2909 if (ec.HasReturnLabel)
2910 ec.ReturnLabel = ec.ig.DefineLabel ();
2912 base.Emit (ec);
2914 ec.Mark (EndLocation);
2916 if (ec.HasReturnLabel)
2917 ec.ig.MarkLabel (ec.ReturnLabel);
2919 if (ec.return_value != null) {
2920 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2921 ec.ig.Emit (OpCodes.Ret);
2922 } else {
2924 // If `HasReturnLabel' is set, then we already emitted a
2925 // jump to the end of the method, so we must emit a `ret'
2926 // there.
2928 // Unfortunately, System.Reflection.Emit automatically emits
2929 // a leave to the end of a finally block. This is a problem
2930 // if no code is following the try/finally block since we may
2931 // jump to a point after the end of the method.
2932 // As a workaround, we're always creating a return label in
2933 // this case.
2936 if (ec.HasReturnLabel || !unreachable) {
2937 if (ec.ReturnType != TypeManager.void_type)
2938 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2939 ec.ig.Emit (OpCodes.Ret);
2943 #if PRODUCTION
2944 } catch (Exception e){
2945 Console.WriteLine ("Exception caught by the compiler while emitting:");
2946 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2948 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2949 throw;
2951 #endif
2954 public override void EmitMeta (EmitContext ec)
2956 parameters.ResolveVariable ();
2958 // Avoid declaring an IL variable for this_variable since it is not accessed
2959 // from the generated IL
2960 if (this_variable != null)
2961 Variables.Remove ("this");
2962 base.EmitMeta (ec);
2965 protected override void EmitSymbolInfo (EmitContext ec)
2967 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2968 if ((ae != null) && (ae.Storey != null))
2969 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2971 base.EmitSymbolInfo (ec);
2975 public class SwitchLabel {
2976 Expression label;
2977 object converted;
2978 Location loc;
2980 Label il_label;
2981 bool il_label_set;
2982 Label il_label_code;
2983 bool il_label_code_set;
2985 public static readonly object NullStringCase = new object ();
2988 // if expr == null, then it is the default case.
2990 public SwitchLabel (Expression expr, Location l)
2992 label = expr;
2993 loc = l;
2996 public Expression Label {
2997 get {
2998 return label;
3002 public Location Location {
3003 get { return loc; }
3006 public object Converted {
3007 get {
3008 return converted;
3012 public Label GetILLabel (EmitContext ec)
3014 if (!il_label_set){
3015 il_label = ec.ig.DefineLabel ();
3016 il_label_set = true;
3018 return il_label;
3021 public Label GetILLabelCode (EmitContext ec)
3023 if (!il_label_code_set){
3024 il_label_code = ec.ig.DefineLabel ();
3025 il_label_code_set = true;
3027 return il_label_code;
3031 // Resolves the expression, reduces it to a literal if possible
3032 // and then converts it to the requested type.
3034 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3036 Expression e = label.Resolve (ec);
3038 if (e == null)
3039 return false;
3041 Constant c = e as Constant;
3042 if (c == null){
3043 ec.Report.Error (150, loc, "A constant value is expected");
3044 return false;
3047 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3048 converted = NullStringCase;
3049 return true;
3052 if (allow_nullable && c.GetValue () == null) {
3053 converted = NullStringCase;
3054 return true;
3057 c = c.ImplicitConversionRequired (ec, required_type, loc);
3058 if (c == null)
3059 return false;
3061 converted = c.GetValue ();
3062 return true;
3065 public void Error_AlreadyOccurs (ResolveContext ec, Type switch_type, SwitchLabel collision_with)
3067 string label;
3068 if (converted == null)
3069 label = "default";
3070 else if (converted == NullStringCase)
3071 label = "null";
3072 else
3073 label = converted.ToString ();
3075 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3076 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3079 public SwitchLabel Clone (CloneContext clonectx)
3081 return new SwitchLabel (label.Clone (clonectx), loc);
3085 public class SwitchSection {
3086 // An array of SwitchLabels.
3087 public readonly ArrayList Labels;
3088 public readonly Block Block;
3090 public SwitchSection (ArrayList labels, Block block)
3092 Labels = labels;
3093 Block = block;
3096 public SwitchSection Clone (CloneContext clonectx)
3098 ArrayList cloned_labels = new ArrayList ();
3100 foreach (SwitchLabel sl in cloned_labels)
3101 cloned_labels.Add (sl.Clone (clonectx));
3103 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3107 public class Switch : Statement {
3108 public ArrayList Sections;
3109 public Expression Expr;
3111 /// <summary>
3112 /// Maps constants whose type type SwitchType to their SwitchLabels.
3113 /// </summary>
3114 public IDictionary Elements;
3116 /// <summary>
3117 /// The governing switch type
3118 /// </summary>
3119 public Type SwitchType;
3122 // Computed
3124 Label default_target;
3125 Label null_target;
3126 Expression new_expr;
3127 bool is_constant;
3128 bool has_null_case;
3129 SwitchSection constant_section;
3130 SwitchSection default_section;
3132 ExpressionStatement string_dictionary;
3133 FieldExpr switch_cache_field;
3134 static int unique_counter;
3137 // Nullable Types support
3139 Nullable.Unwrap unwrap;
3141 protected bool HaveUnwrap {
3142 get { return unwrap != null; }
3146 // The types allowed to be implicitly cast from
3147 // on the governing type
3149 static Type [] allowed_types;
3151 public Switch (Expression e, ArrayList sects, Location l)
3153 Expr = e;
3154 Sections = sects;
3155 loc = l;
3158 public bool GotDefault {
3159 get {
3160 return default_section != null;
3164 public Label DefaultTarget {
3165 get {
3166 return default_target;
3171 // Determines the governing type for a switch. The returned
3172 // expression might be the expression from the switch, or an
3173 // expression that includes any potential conversions to the
3174 // integral types or to string.
3176 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3178 Type t = expr.Type;
3180 if (t == TypeManager.byte_type ||
3181 t == TypeManager.sbyte_type ||
3182 t == TypeManager.ushort_type ||
3183 t == TypeManager.short_type ||
3184 t == TypeManager.uint32_type ||
3185 t == TypeManager.int32_type ||
3186 t == TypeManager.uint64_type ||
3187 t == TypeManager.int64_type ||
3188 t == TypeManager.char_type ||
3189 t == TypeManager.string_type ||
3190 t == TypeManager.bool_type ||
3191 TypeManager.IsEnumType (t))
3192 return expr;
3194 if (allowed_types == null){
3195 allowed_types = new Type [] {
3196 TypeManager.sbyte_type,
3197 TypeManager.byte_type,
3198 TypeManager.short_type,
3199 TypeManager.ushort_type,
3200 TypeManager.int32_type,
3201 TypeManager.uint32_type,
3202 TypeManager.int64_type,
3203 TypeManager.uint64_type,
3204 TypeManager.char_type,
3205 TypeManager.string_type
3210 // Try to find a *user* defined implicit conversion.
3212 // If there is no implicit conversion, or if there are multiple
3213 // conversions, we have to report an error
3215 Expression converted = null;
3216 foreach (Type tt in allowed_types){
3217 Expression e;
3219 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3220 if (e == null)
3221 continue;
3224 // Ignore over-worked ImplicitUserConversions that do
3225 // an implicit conversion in addition to the user conversion.
3227 if (!(e is UserCast))
3228 continue;
3230 if (converted != null){
3231 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3232 return null;
3235 converted = e;
3237 return converted;
3241 // Performs the basic sanity checks on the switch statement
3242 // (looks for duplicate keys and non-constant expressions).
3244 // It also returns a hashtable with the keys that we will later
3245 // use to compute the switch tables
3247 bool CheckSwitch (ResolveContext ec)
3249 bool error = false;
3250 Elements = Sections.Count > 10 ?
3251 (IDictionary)new Hashtable () :
3252 (IDictionary)new ListDictionary ();
3254 foreach (SwitchSection ss in Sections){
3255 foreach (SwitchLabel sl in ss.Labels){
3256 if (sl.Label == null){
3257 if (default_section != null){
3258 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3259 error = true;
3261 default_section = ss;
3262 continue;
3265 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3266 error = true;
3267 continue;
3270 object key = sl.Converted;
3271 if (key == SwitchLabel.NullStringCase)
3272 has_null_case = true;
3274 try {
3275 Elements.Add (key, sl);
3276 } catch (ArgumentException) {
3277 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)Elements [key]);
3278 error = true;
3282 return !error;
3285 void EmitObjectInteger (ILGenerator ig, object k)
3287 if (k is int)
3288 IntConstant.EmitInt (ig, (int) k);
3289 else if (k is Constant) {
3290 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3292 else if (k is uint)
3293 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3294 else if (k is long)
3296 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3298 IntConstant.EmitInt (ig, (int) (long) k);
3299 ig.Emit (OpCodes.Conv_I8);
3301 else
3302 LongConstant.EmitLong (ig, (long) k);
3304 else if (k is ulong)
3306 ulong ul = (ulong) k;
3307 if (ul < (1L<<32))
3309 IntConstant.EmitInt (ig, unchecked ((int) ul));
3310 ig.Emit (OpCodes.Conv_U8);
3312 else
3314 LongConstant.EmitLong (ig, unchecked ((long) ul));
3317 else if (k is char)
3318 IntConstant.EmitInt (ig, (int) ((char) k));
3319 else if (k is sbyte)
3320 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3321 else if (k is byte)
3322 IntConstant.EmitInt (ig, (int) ((byte) k));
3323 else if (k is short)
3324 IntConstant.EmitInt (ig, (int) ((short) k));
3325 else if (k is ushort)
3326 IntConstant.EmitInt (ig, (int) ((ushort) k));
3327 else if (k is bool)
3328 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3329 else
3330 throw new Exception ("Unhandled case");
3333 // structure used to hold blocks of keys while calculating table switch
3334 class KeyBlock : IComparable
3336 public KeyBlock (long _first)
3338 first = last = _first;
3340 public long first;
3341 public long last;
3342 public ArrayList element_keys = null;
3343 // how many items are in the bucket
3344 public int Size = 1;
3345 public int Length
3347 get { return (int) (last - first + 1); }
3349 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3351 return kb_last.last - kb_first.first + 1;
3353 public int CompareTo (object obj)
3355 KeyBlock kb = (KeyBlock) obj;
3356 int nLength = Length;
3357 int nLengthOther = kb.Length;
3358 if (nLengthOther == nLength)
3359 return (int) (kb.first - first);
3360 return nLength - nLengthOther;
3364 /// <summary>
3365 /// This method emits code for a lookup-based switch statement (non-string)
3366 /// Basically it groups the cases into blocks that are at least half full,
3367 /// and then spits out individual lookup opcodes for each block.
3368 /// It emits the longest blocks first, and short blocks are just
3369 /// handled with direct compares.
3370 /// </summary>
3371 /// <param name="ec"></param>
3372 /// <param name="val"></param>
3373 /// <returns></returns>
3374 void TableSwitchEmit (EmitContext ec, Expression val)
3376 int element_count = Elements.Count;
3377 object [] element_keys = new object [element_count];
3378 Elements.Keys.CopyTo (element_keys, 0);
3379 Array.Sort (element_keys);
3381 // initialize the block list with one element per key
3382 ArrayList key_blocks = new ArrayList (element_count);
3383 foreach (object key in element_keys)
3384 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3386 KeyBlock current_kb;
3387 // iteratively merge the blocks while they are at least half full
3388 // there's probably a really cool way to do this with a tree...
3389 while (key_blocks.Count > 1)
3391 ArrayList key_blocks_new = new ArrayList ();
3392 current_kb = (KeyBlock) key_blocks [0];
3393 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3395 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3396 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3398 // merge blocks
3399 current_kb.last = kb.last;
3400 current_kb.Size += kb.Size;
3402 else
3404 // start a new block
3405 key_blocks_new.Add (current_kb);
3406 current_kb = kb;
3409 key_blocks_new.Add (current_kb);
3410 if (key_blocks.Count == key_blocks_new.Count)
3411 break;
3412 key_blocks = key_blocks_new;
3415 // initialize the key lists
3416 foreach (KeyBlock kb in key_blocks)
3417 kb.element_keys = new ArrayList ();
3419 // fill the key lists
3420 int iBlockCurr = 0;
3421 if (key_blocks.Count > 0) {
3422 current_kb = (KeyBlock) key_blocks [0];
3423 foreach (object key in element_keys)
3425 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3426 System.Convert.ToInt64 (key) > current_kb.last;
3427 if (next_block)
3428 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3429 current_kb.element_keys.Add (key);
3433 // sort the blocks so we can tackle the largest ones first
3434 key_blocks.Sort ();
3436 // okay now we can start...
3437 ILGenerator ig = ec.ig;
3438 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3439 Label lbl_default = default_target;
3441 Type type_keys = null;
3442 if (element_keys.Length > 0)
3443 type_keys = element_keys [0].GetType (); // used for conversions
3445 Type compare_type;
3447 if (TypeManager.IsEnumType (SwitchType))
3448 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3449 else
3450 compare_type = SwitchType;
3452 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3454 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3455 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3456 if (kb.Length <= 2)
3458 foreach (object key in kb.element_keys) {
3459 SwitchLabel sl = (SwitchLabel) Elements [key];
3460 if (key is int && (int) key == 0) {
3461 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3462 } else {
3463 val.Emit (ec);
3464 EmitObjectInteger (ig, key);
3465 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3469 else
3471 // TODO: if all the keys in the block are the same and there are
3472 // no gaps/defaults then just use a range-check.
3473 if (compare_type == TypeManager.int64_type ||
3474 compare_type == TypeManager.uint64_type)
3476 // TODO: optimize constant/I4 cases
3478 // check block range (could be > 2^31)
3479 val.Emit (ec);
3480 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3481 ig.Emit (OpCodes.Blt, lbl_default);
3482 val.Emit (ec);
3483 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3484 ig.Emit (OpCodes.Bgt, lbl_default);
3486 // normalize range
3487 val.Emit (ec);
3488 if (kb.first != 0)
3490 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3491 ig.Emit (OpCodes.Sub);
3493 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3495 else
3497 // normalize range
3498 val.Emit (ec);
3499 int first = (int) kb.first;
3500 if (first > 0)
3502 IntConstant.EmitInt (ig, first);
3503 ig.Emit (OpCodes.Sub);
3505 else if (first < 0)
3507 IntConstant.EmitInt (ig, -first);
3508 ig.Emit (OpCodes.Add);
3512 // first, build the list of labels for the switch
3513 int iKey = 0;
3514 int cJumps = kb.Length;
3515 Label [] switch_labels = new Label [cJumps];
3516 for (int iJump = 0; iJump < cJumps; iJump++)
3518 object key = kb.element_keys [iKey];
3519 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3521 SwitchLabel sl = (SwitchLabel) Elements [key];
3522 switch_labels [iJump] = sl.GetILLabel (ec);
3523 iKey++;
3525 else
3526 switch_labels [iJump] = lbl_default;
3528 // emit the switch opcode
3529 ig.Emit (OpCodes.Switch, switch_labels);
3532 // mark the default for this block
3533 if (iBlock != 0)
3534 ig.MarkLabel (lbl_default);
3537 // TODO: find the default case and emit it here,
3538 // to prevent having to do the following jump.
3539 // make sure to mark other labels in the default section
3541 // the last default just goes to the end
3542 if (element_keys.Length > 0)
3543 ig.Emit (OpCodes.Br, lbl_default);
3545 // now emit the code for the sections
3546 bool found_default = false;
3548 foreach (SwitchSection ss in Sections) {
3549 foreach (SwitchLabel sl in ss.Labels) {
3550 if (sl.Converted == SwitchLabel.NullStringCase) {
3551 ig.MarkLabel (null_target);
3552 } else if (sl.Label == null) {
3553 ig.MarkLabel (lbl_default);
3554 found_default = true;
3555 if (!has_null_case)
3556 ig.MarkLabel (null_target);
3558 ig.MarkLabel (sl.GetILLabel (ec));
3559 ig.MarkLabel (sl.GetILLabelCode (ec));
3561 ss.Block.Emit (ec);
3564 if (!found_default) {
3565 ig.MarkLabel (lbl_default);
3566 if (!has_null_case) {
3567 ig.MarkLabel (null_target);
3571 ig.MarkLabel (lbl_end);
3574 SwitchSection FindSection (SwitchLabel label)
3576 foreach (SwitchSection ss in Sections){
3577 foreach (SwitchLabel sl in ss.Labels){
3578 if (label == sl)
3579 return ss;
3583 return null;
3586 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3588 foreach (SwitchSection ss in Sections)
3589 ss.Block.MutateHoistedGenericType (storey);
3592 public static void Reset ()
3594 unique_counter = 0;
3595 allowed_types = null;
3598 public override bool Resolve (BlockContext ec)
3600 Expr = Expr.Resolve (ec);
3601 if (Expr == null)
3602 return false;
3604 new_expr = SwitchGoverningType (ec, Expr);
3606 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3607 unwrap = Nullable.Unwrap.Create (Expr, false);
3608 if (unwrap == null)
3609 return false;
3611 new_expr = SwitchGoverningType (ec, unwrap);
3614 if (new_expr == null){
3615 ec.Report.Error (151, loc,
3616 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3617 TypeManager.CSharpName (Expr.Type));
3618 return false;
3621 // Validate switch.
3622 SwitchType = new_expr.Type;
3624 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3625 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3626 return false;
3629 if (!CheckSwitch (ec))
3630 return false;
3632 if (HaveUnwrap)
3633 Elements.Remove (SwitchLabel.NullStringCase);
3635 Switch old_switch = ec.Switch;
3636 ec.Switch = this;
3637 ec.Switch.SwitchType = SwitchType;
3639 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3640 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3642 is_constant = new_expr is Constant;
3643 if (is_constant) {
3644 object key = ((Constant) new_expr).GetValue ();
3645 SwitchLabel label = (SwitchLabel) Elements [key];
3647 constant_section = FindSection (label);
3648 if (constant_section == null)
3649 constant_section = default_section;
3652 bool first = true;
3653 bool ok = true;
3654 foreach (SwitchSection ss in Sections){
3655 if (!first)
3656 ec.CurrentBranching.CreateSibling (
3657 null, FlowBranching.SiblingType.SwitchSection);
3658 else
3659 first = false;
3661 if (is_constant && (ss != constant_section)) {
3662 // If we're a constant switch, we're only emitting
3663 // one single section - mark all the others as
3664 // unreachable.
3665 ec.CurrentBranching.CurrentUsageVector.Goto ();
3666 if (!ss.Block.ResolveUnreachable (ec, true)) {
3667 ok = false;
3669 } else {
3670 if (!ss.Block.Resolve (ec))
3671 ok = false;
3675 if (default_section == null)
3676 ec.CurrentBranching.CreateSibling (
3677 null, FlowBranching.SiblingType.SwitchSection);
3679 ec.EndFlowBranching ();
3680 ec.Switch = old_switch;
3682 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3684 if (!ok)
3685 return false;
3687 if (SwitchType == TypeManager.string_type && !is_constant) {
3688 // TODO: Optimize single case, and single+default case
3689 ResolveStringSwitchMap (ec);
3692 return true;
3695 void ResolveStringSwitchMap (ResolveContext ec)
3697 FullNamedExpression string_dictionary_type;
3698 if (TypeManager.generic_ienumerable_type != null) {
3699 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3700 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3702 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3703 new TypeArguments (
3704 new TypeExpression (TypeManager.string_type, loc),
3705 new TypeExpression (TypeManager.int32_type, loc)), loc);
3706 } else {
3707 MemberAccess system_collections_generic = new MemberAccess (
3708 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3710 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3713 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3714 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3715 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3716 if (!field.Define ())
3717 return;
3718 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3720 ArrayList init = new ArrayList ();
3721 int counter = 0;
3722 Elements.Clear ();
3723 string value = null;
3724 foreach (SwitchSection section in Sections) {
3725 int last_count = init.Count;
3726 foreach (SwitchLabel sl in section.Labels) {
3727 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3728 continue;
3730 value = (string) sl.Converted;
3731 ArrayList init_args = new ArrayList (2);
3732 init_args.Add (new StringLiteral (value, sl.Location));
3733 init_args.Add (new IntConstant (counter, loc));
3734 init.Add (new CollectionElementInitializer (init_args, loc));
3738 // Don't add empty sections
3740 if (last_count == init.Count)
3741 continue;
3743 Elements.Add (counter, section.Labels [0]);
3744 ++counter;
3747 Arguments args = new Arguments (1);
3748 args.Add (new Argument (new IntConstant (init.Count, loc)));
3749 Expression initializer = new NewInitialize (string_dictionary_type, args,
3750 new CollectionOrObjectInitializers (init, loc), loc);
3752 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3753 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3756 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3758 ILGenerator ig = ec.ig;
3759 Label l_initialized = ig.DefineLabel ();
3762 // Skip initialization when value is null
3764 value.EmitBranchable (ec, null_target, false);
3767 // Check if string dictionary is initialized and initialize
3769 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3770 string_dictionary.EmitStatement (ec);
3771 ig.MarkLabel (l_initialized);
3773 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3775 ResolveContext rc = new ResolveContext (ec.MemberContext);
3777 if (TypeManager.generic_ienumerable_type != null) {
3778 Arguments get_value_args = new Arguments (2);
3779 get_value_args.Add (new Argument (value));
3780 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3781 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3782 if (get_item == null)
3783 return;
3786 // A value was not found, go to default case
3788 get_item.EmitBranchable (ec, default_target, false);
3789 } else {
3790 Arguments get_value_args = new Arguments (1);
3791 get_value_args.Add (new Argument (value));
3793 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3794 if (get_item == null)
3795 return;
3797 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3798 get_item_object.EmitAssign (ec, get_item, true, false);
3799 ec.ig.Emit (OpCodes.Brfalse, default_target);
3801 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3802 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3804 get_item_int.EmitStatement (ec);
3805 get_item_object.Release (ec);
3808 TableSwitchEmit (ec, string_switch_variable);
3809 string_switch_variable.Release (ec);
3812 protected override void DoEmit (EmitContext ec)
3814 ILGenerator ig = ec.ig;
3816 default_target = ig.DefineLabel ();
3817 null_target = ig.DefineLabel ();
3819 // Store variable for comparission purposes
3820 // TODO: Don't duplicate non-captured VariableReference
3821 LocalTemporary value;
3822 if (HaveUnwrap) {
3823 value = new LocalTemporary (SwitchType);
3824 unwrap.EmitCheck (ec);
3825 ig.Emit (OpCodes.Brfalse, null_target);
3826 new_expr.Emit (ec);
3827 value.Store (ec);
3828 } else if (!is_constant) {
3829 value = new LocalTemporary (SwitchType);
3830 new_expr.Emit (ec);
3831 value.Store (ec);
3832 } else
3833 value = null;
3836 // Setup the codegen context
3838 Label old_end = ec.LoopEnd;
3839 Switch old_switch = ec.Switch;
3841 ec.LoopEnd = ig.DefineLabel ();
3842 ec.Switch = this;
3844 // Emit Code.
3845 if (is_constant) {
3846 if (constant_section != null)
3847 constant_section.Block.Emit (ec);
3848 } else if (string_dictionary != null) {
3849 DoEmitStringSwitch (value, ec);
3850 } else {
3851 TableSwitchEmit (ec, value);
3854 if (value != null)
3855 value.Release (ec);
3857 // Restore context state.
3858 ig.MarkLabel (ec.LoopEnd);
3861 // Restore the previous context
3863 ec.LoopEnd = old_end;
3864 ec.Switch = old_switch;
3867 protected override void CloneTo (CloneContext clonectx, Statement t)
3869 Switch target = (Switch) t;
3871 target.Expr = Expr.Clone (clonectx);
3872 target.Sections = new ArrayList ();
3873 foreach (SwitchSection ss in Sections){
3874 target.Sections.Add (ss.Clone (clonectx));
3879 // A place where execution can restart in an iterator
3880 public abstract class ResumableStatement : Statement
3882 bool prepared;
3883 protected Label resume_point;
3885 public Label PrepareForEmit (EmitContext ec)
3887 if (!prepared) {
3888 prepared = true;
3889 resume_point = ec.ig.DefineLabel ();
3891 return resume_point;
3894 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3896 return end;
3898 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3903 // Base class for statements that are implemented in terms of try...finally
3904 public abstract class ExceptionStatement : ResumableStatement
3906 bool code_follows;
3907 Iterator iter;
3909 protected abstract void EmitPreTryBody (EmitContext ec);
3910 protected abstract void EmitTryBody (EmitContext ec);
3911 protected abstract void EmitFinallyBody (EmitContext ec);
3913 protected sealed override void DoEmit (EmitContext ec)
3915 ILGenerator ig = ec.ig;
3917 EmitPreTryBody (ec);
3919 if (resume_points != null) {
3920 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3921 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3924 ig.BeginExceptionBlock ();
3926 if (resume_points != null) {
3927 ig.MarkLabel (resume_point);
3929 // For normal control flow, we want to fall-through the Switch
3930 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3931 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3932 IntConstant.EmitInt (ig, first_resume_pc);
3933 ig.Emit (OpCodes.Sub);
3935 Label [] labels = new Label [resume_points.Count];
3936 for (int i = 0; i < resume_points.Count; ++i)
3937 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3938 ig.Emit (OpCodes.Switch, labels);
3941 EmitTryBody (ec);
3943 ig.BeginFinallyBlock ();
3945 Label start_finally = ec.ig.DefineLabel ();
3946 if (resume_points != null) {
3947 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3948 ig.Emit (OpCodes.Brfalse_S, start_finally);
3949 ig.Emit (OpCodes.Endfinally);
3952 ig.MarkLabel (start_finally);
3953 EmitFinallyBody (ec);
3955 ig.EndExceptionBlock ();
3958 public void SomeCodeFollows ()
3960 code_follows = true;
3963 public override bool Resolve (BlockContext ec)
3965 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3966 // So, ensure there's some IL code after this statement.
3967 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3968 ec.NeedReturnLabel ();
3970 iter = ec.CurrentIterator;
3971 return true;
3974 ArrayList resume_points;
3975 int first_resume_pc;
3976 public void AddResumePoint (ResumableStatement stmt, int pc)
3978 if (resume_points == null) {
3979 resume_points = new ArrayList ();
3980 first_resume_pc = pc;
3983 if (pc != first_resume_pc + resume_points.Count)
3984 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3986 resume_points.Add (stmt);
3989 Label dispose_try_block;
3990 bool prepared_for_dispose, emitted_dispose;
3991 public override Label PrepareForDispose (EmitContext ec, Label end)
3993 if (!prepared_for_dispose) {
3994 prepared_for_dispose = true;
3995 dispose_try_block = ec.ig.DefineLabel ();
3997 return dispose_try_block;
4000 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4002 if (emitted_dispose)
4003 return;
4005 emitted_dispose = true;
4007 ILGenerator ig = ec.ig;
4009 Label end_of_try = ig.DefineLabel ();
4011 // Ensure that the only way we can get into this code is through a dispatcher
4012 if (have_dispatcher)
4013 ig.Emit (OpCodes.Br, end);
4015 ig.BeginExceptionBlock ();
4017 ig.MarkLabel (dispose_try_block);
4019 Label [] labels = null;
4020 for (int i = 0; i < resume_points.Count; ++i) {
4021 ResumableStatement s = (ResumableStatement) resume_points [i];
4022 Label ret = s.PrepareForDispose (ec, end_of_try);
4023 if (ret.Equals (end_of_try) && labels == null)
4024 continue;
4025 if (labels == null) {
4026 labels = new Label [resume_points.Count];
4027 for (int j = 0; j < i; ++j)
4028 labels [j] = end_of_try;
4030 labels [i] = ret;
4033 if (labels != null) {
4034 int j;
4035 for (j = 1; j < labels.Length; ++j)
4036 if (!labels [0].Equals (labels [j]))
4037 break;
4038 bool emit_dispatcher = j < labels.Length;
4040 if (emit_dispatcher) {
4041 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4042 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4043 IntConstant.EmitInt (ig, first_resume_pc);
4044 ig.Emit (OpCodes.Sub);
4045 ig.Emit (OpCodes.Switch, labels);
4046 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4049 foreach (ResumableStatement s in resume_points)
4050 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4053 ig.MarkLabel (end_of_try);
4055 ig.BeginFinallyBlock ();
4057 EmitFinallyBody (ec);
4059 ig.EndExceptionBlock ();
4063 public class Lock : ExceptionStatement {
4064 Expression expr;
4065 public Statement Statement;
4066 TemporaryVariable temp;
4068 public Lock (Expression expr, Statement stmt, Location l)
4070 this.expr = expr;
4071 Statement = stmt;
4072 loc = l;
4075 public override bool Resolve (BlockContext ec)
4077 expr = expr.Resolve (ec);
4078 if (expr == null)
4079 return false;
4081 if (!TypeManager.IsReferenceType (expr.Type)){
4082 ec.Report.Error (185, loc,
4083 "`{0}' is not a reference type as required by the lock statement",
4084 TypeManager.CSharpName (expr.Type));
4085 return false;
4088 ec.StartFlowBranching (this);
4089 bool ok = Statement.Resolve (ec);
4090 ec.EndFlowBranching ();
4092 ok &= base.Resolve (ec);
4094 // Avoid creating libraries that reference the internal
4095 // mcs NullType:
4096 Type t = expr.Type;
4097 if (t == TypeManager.null_type)
4098 t = TypeManager.object_type;
4100 temp = new TemporaryVariable (t, loc);
4101 temp.Resolve (ec);
4103 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4104 Type monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", Kind.Class, true);
4105 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4106 monitor_type, "Enter", loc, TypeManager.object_type);
4107 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4108 monitor_type, "Exit", loc, TypeManager.object_type);
4111 return ok;
4114 protected override void EmitPreTryBody (EmitContext ec)
4116 ILGenerator ig = ec.ig;
4118 temp.EmitAssign (ec, expr);
4119 temp.Emit (ec);
4120 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4123 protected override void EmitTryBody (EmitContext ec)
4125 Statement.Emit (ec);
4128 protected override void EmitFinallyBody (EmitContext ec)
4130 temp.Emit (ec);
4131 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4134 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4136 expr.MutateHoistedGenericType (storey);
4137 temp.MutateHoistedGenericType (storey);
4138 Statement.MutateHoistedGenericType (storey);
4141 protected override void CloneTo (CloneContext clonectx, Statement t)
4143 Lock target = (Lock) t;
4145 target.expr = expr.Clone (clonectx);
4146 target.Statement = Statement.Clone (clonectx);
4150 public class Unchecked : Statement {
4151 public Block Block;
4153 public Unchecked (Block b)
4155 Block = b;
4156 b.Unchecked = true;
4159 public override bool Resolve (BlockContext ec)
4161 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4162 return Block.Resolve (ec);
4165 protected override void DoEmit (EmitContext ec)
4167 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4168 Block.Emit (ec);
4171 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4173 Block.MutateHoistedGenericType (storey);
4176 protected override void CloneTo (CloneContext clonectx, Statement t)
4178 Unchecked target = (Unchecked) t;
4180 target.Block = clonectx.LookupBlock (Block);
4184 public class Checked : Statement {
4185 public Block Block;
4187 public Checked (Block b)
4189 Block = b;
4190 b.Unchecked = false;
4193 public override bool Resolve (BlockContext ec)
4195 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4196 return Block.Resolve (ec);
4199 protected override void DoEmit (EmitContext ec)
4201 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4202 Block.Emit (ec);
4205 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4207 Block.MutateHoistedGenericType (storey);
4210 protected override void CloneTo (CloneContext clonectx, Statement t)
4212 Checked target = (Checked) t;
4214 target.Block = clonectx.LookupBlock (Block);
4218 public class Unsafe : Statement {
4219 public Block Block;
4221 public Unsafe (Block b)
4223 Block = b;
4224 Block.Unsafe = true;
4225 loc = b.StartLocation;
4228 public override bool Resolve (BlockContext ec)
4230 if (ec.CurrentIterator != null)
4231 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4233 using (ec.Set (ResolveContext.Options.UnsafeScope))
4234 return Block.Resolve (ec);
4237 protected override void DoEmit (EmitContext ec)
4239 Block.Emit (ec);
4242 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4244 Block.MutateHoistedGenericType (storey);
4247 protected override void CloneTo (CloneContext clonectx, Statement t)
4249 Unsafe target = (Unsafe) t;
4251 target.Block = clonectx.LookupBlock (Block);
4256 // Fixed statement
4258 public class Fixed : Statement {
4259 Expression type;
4260 ArrayList declarators;
4261 Statement statement;
4262 Type expr_type;
4263 Emitter[] data;
4264 bool has_ret;
4266 abstract class Emitter
4268 protected LocalInfo vi;
4269 protected Expression converted;
4271 protected Emitter (Expression expr, LocalInfo li)
4273 converted = expr;
4274 vi = li;
4277 public abstract void Emit (EmitContext ec);
4278 public abstract void EmitExit (EmitContext ec);
4281 class ExpressionEmitter : Emitter {
4282 public ExpressionEmitter (Expression converted, LocalInfo li) :
4283 base (converted, li)
4287 public override void Emit (EmitContext ec) {
4289 // Store pointer in pinned location
4291 converted.Emit (ec);
4292 vi.EmitAssign (ec);
4295 public override void EmitExit (EmitContext ec)
4297 ec.ig.Emit (OpCodes.Ldc_I4_0);
4298 ec.ig.Emit (OpCodes.Conv_U);
4299 vi.EmitAssign (ec);
4303 class StringEmitter : Emitter
4305 LocalInfo pinned_string;
4307 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4308 base (expr, li)
4310 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4311 pinned_string.Pinned = true;
4314 public StringEmitter Resolve (ResolveContext rc)
4316 pinned_string.Resolve (rc);
4318 if (TypeManager.int_get_offset_to_string_data == null) {
4319 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4320 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4323 return this;
4326 public override void Emit (EmitContext ec)
4328 pinned_string.ResolveVariable (ec);
4330 converted.Emit (ec);
4331 pinned_string.EmitAssign (ec);
4333 // TODO: Should use Binary::Add
4334 pinned_string.Emit (ec);
4335 ec.ig.Emit (OpCodes.Conv_I);
4337 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4338 //pe.InstanceExpression = pinned_string;
4339 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4341 ec.ig.Emit (OpCodes.Add);
4342 vi.EmitAssign (ec);
4345 public override void EmitExit (EmitContext ec)
4347 ec.ig.Emit (OpCodes.Ldnull);
4348 pinned_string.EmitAssign (ec);
4352 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4354 this.type = type;
4355 declarators = decls;
4356 statement = stmt;
4357 loc = l;
4360 public Statement Statement {
4361 get { return statement; }
4364 public override bool Resolve (BlockContext ec)
4366 if (!ec.IsUnsafe){
4367 Expression.UnsafeError (ec, loc);
4368 return false;
4371 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4372 if (texpr == null) {
4373 if (type is VarExpr)
4374 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4376 return false;
4379 expr_type = texpr.Type;
4381 data = new Emitter [declarators.Count];
4383 if (!expr_type.IsPointer){
4384 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4385 return false;
4388 int i = 0;
4389 foreach (Pair p in declarators){
4390 LocalInfo vi = (LocalInfo) p.First;
4391 Expression e = (Expression) p.Second;
4393 vi.VariableInfo.SetAssigned (ec);
4394 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4397 // The rules for the possible declarators are pretty wise,
4398 // but the production on the grammar is more concise.
4400 // So we have to enforce these rules here.
4402 // We do not resolve before doing the case 1 test,
4403 // because the grammar is explicit in that the token &
4404 // is present, so we need to test for this particular case.
4407 if (e is Cast){
4408 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4409 return false;
4412 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4413 e = e.Resolve (ec);
4416 if (e == null)
4417 return false;
4420 // Case 2: Array
4422 if (e.Type.IsArray){
4423 Type array_type = TypeManager.GetElementType (e.Type);
4426 // Provided that array_type is unmanaged,
4428 if (!TypeManager.VerifyUnManaged (array_type, loc))
4429 return false;
4432 // and T* is implicitly convertible to the
4433 // pointer type given in the fixed statement.
4435 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4437 Expression converted = Convert.ImplicitConversionRequired (
4438 ec, array_ptr, vi.VariableType, loc);
4439 if (converted == null)
4440 return false;
4443 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4445 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4446 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4447 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc)))),
4448 new NullPointer (loc),
4449 converted);
4451 converted = converted.Resolve (ec);
4453 data [i] = new ExpressionEmitter (converted, vi);
4454 i++;
4456 continue;
4460 // Case 3: string
4462 if (e.Type == TypeManager.string_type){
4463 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4464 i++;
4465 continue;
4468 // Case 4: fixed buffer
4469 if (e is FixedBufferPtr) {
4470 data [i++] = new ExpressionEmitter (e, vi);
4471 continue;
4475 // Case 1: & object.
4477 Unary u = e as Unary;
4478 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4479 IVariableReference vr = u.Expr as IVariableReference;
4480 if (vr == null || !vr.IsFixed) {
4481 data [i] = new ExpressionEmitter (e, vi);
4485 if (data [i++] == null)
4486 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4488 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4491 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4492 bool ok = statement.Resolve (ec);
4493 bool flow_unreachable = ec.EndFlowBranching ();
4494 has_ret = flow_unreachable;
4496 return ok;
4499 protected override void DoEmit (EmitContext ec)
4501 for (int i = 0; i < data.Length; i++) {
4502 data [i].Emit (ec);
4505 statement.Emit (ec);
4507 if (has_ret)
4508 return;
4511 // Clear the pinned variable
4513 for (int i = 0; i < data.Length; i++) {
4514 data [i].EmitExit (ec);
4518 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4520 // Fixed statement cannot be used inside anonymous methods or lambdas
4521 throw new NotSupportedException ();
4524 protected override void CloneTo (CloneContext clonectx, Statement t)
4526 Fixed target = (Fixed) t;
4528 target.type = type.Clone (clonectx);
4529 target.declarators = new ArrayList (declarators.Count);
4530 foreach (Pair p in declarators) {
4531 LocalInfo vi = (LocalInfo) p.First;
4532 Expression e = (Expression) p.Second;
4534 target.declarators.Add (
4535 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4538 target.statement = statement.Clone (clonectx);
4542 public class Catch : Statement {
4543 public readonly string Name;
4544 public Block Block;
4545 public Block VarBlock;
4547 Expression type_expr;
4548 Type type;
4550 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4552 type_expr = type;
4553 Name = name;
4554 Block = block;
4555 VarBlock = var_block;
4556 loc = l;
4559 public Type CatchType {
4560 get {
4561 return type;
4565 public bool IsGeneral {
4566 get {
4567 return type_expr == null;
4571 protected override void DoEmit (EmitContext ec)
4573 ILGenerator ig = ec.ig;
4575 if (CatchType != null)
4576 ig.BeginCatchBlock (CatchType);
4577 else
4578 ig.BeginCatchBlock (TypeManager.object_type);
4580 if (VarBlock != null)
4581 VarBlock.Emit (ec);
4583 if (Name != null) {
4584 // TODO: Move to resolve
4585 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4586 lvr.Resolve (new ResolveContext (ec.MemberContext));
4588 // Only to make verifier happy
4589 if (TypeManager.IsGenericParameter (lvr.Type))
4590 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4592 Expression source;
4593 if (lvr.IsHoisted) {
4594 LocalTemporary lt = new LocalTemporary (lvr.Type);
4595 lt.Store (ec);
4596 source = lt;
4597 } else {
4598 // Variable is at the top of the stack
4599 source = EmptyExpression.Null;
4602 lvr.EmitAssign (ec, source, false, false);
4603 } else
4604 ig.Emit (OpCodes.Pop);
4606 Block.Emit (ec);
4609 public override bool Resolve (BlockContext ec)
4611 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4612 if (type_expr != null) {
4613 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4614 if (te == null)
4615 return false;
4617 type = te.Type;
4619 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4620 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4621 return false;
4623 } else
4624 type = null;
4626 if (!Block.Resolve (ec))
4627 return false;
4629 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4630 // emit the "unused variable" warnings.
4631 if (VarBlock != null)
4632 return VarBlock.Resolve (ec);
4634 return true;
4638 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4640 if (type != null)
4641 type = storey.MutateType (type);
4642 if (VarBlock != null)
4643 VarBlock.MutateHoistedGenericType (storey);
4644 Block.MutateHoistedGenericType (storey);
4647 protected override void CloneTo (CloneContext clonectx, Statement t)
4649 Catch target = (Catch) t;
4651 if (type_expr != null)
4652 target.type_expr = type_expr.Clone (clonectx);
4653 if (VarBlock != null)
4654 target.VarBlock = clonectx.LookupBlock (VarBlock);
4655 target.Block = clonectx.LookupBlock (Block);
4659 public class TryFinally : ExceptionStatement {
4660 Statement stmt;
4661 Block fini;
4663 public TryFinally (Statement stmt, Block fini, Location l)
4665 this.stmt = stmt;
4666 this.fini = fini;
4667 loc = l;
4670 public override bool Resolve (BlockContext ec)
4672 bool ok = true;
4674 ec.StartFlowBranching (this);
4676 if (!stmt.Resolve (ec))
4677 ok = false;
4679 if (ok)
4680 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4681 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4682 if (!fini.Resolve (ec))
4683 ok = false;
4686 ec.EndFlowBranching ();
4688 ok &= base.Resolve (ec);
4690 return ok;
4693 protected override void EmitPreTryBody (EmitContext ec)
4697 protected override void EmitTryBody (EmitContext ec)
4699 stmt.Emit (ec);
4702 protected override void EmitFinallyBody (EmitContext ec)
4704 fini.Emit (ec);
4707 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4709 stmt.MutateHoistedGenericType (storey);
4710 fini.MutateHoistedGenericType (storey);
4713 protected override void CloneTo (CloneContext clonectx, Statement t)
4715 TryFinally target = (TryFinally) t;
4717 target.stmt = (Statement) stmt.Clone (clonectx);
4718 if (fini != null)
4719 target.fini = clonectx.LookupBlock (fini);
4723 public class TryCatch : Statement {
4724 public Block Block;
4725 public ArrayList Specific;
4726 public Catch General;
4727 bool inside_try_finally, code_follows;
4729 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4731 this.Block = block;
4732 this.Specific = catch_clauses;
4733 this.inside_try_finally = inside_try_finally;
4735 Catch c = (Catch) catch_clauses [0];
4736 if (c.IsGeneral) {
4737 this.General = c;
4738 catch_clauses.RemoveAt (0);
4741 loc = l;
4744 public override bool Resolve (BlockContext ec)
4746 bool ok = true;
4748 ec.StartFlowBranching (this);
4750 if (!Block.Resolve (ec))
4751 ok = false;
4753 Type[] prev_catches = new Type [Specific.Count];
4754 int last_index = 0;
4755 foreach (Catch c in Specific){
4756 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4758 if (c.Name != null) {
4759 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4760 if (vi == null)
4761 throw new Exception ();
4763 vi.VariableInfo = null;
4766 if (!c.Resolve (ec)) {
4767 ok = false;
4768 continue;
4771 Type resolved_type = c.CatchType;
4772 for (int ii = 0; ii < last_index; ++ii) {
4773 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4774 ec.Report.Error (160, c.loc,
4775 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4776 TypeManager.CSharpName (prev_catches [ii]));
4777 ok = false;
4781 prev_catches [last_index++] = resolved_type;
4784 if (General != null) {
4785 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4786 foreach (Catch c in Specific){
4787 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4788 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'");
4793 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4795 if (!General.Resolve (ec))
4796 ok = false;
4799 ec.EndFlowBranching ();
4801 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4802 // So, ensure there's some IL code after this statement
4803 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4804 ec.NeedReturnLabel ();
4806 return ok;
4809 public void SomeCodeFollows ()
4811 code_follows = true;
4814 protected override void DoEmit (EmitContext ec)
4816 ILGenerator ig = ec.ig;
4818 if (!inside_try_finally)
4819 ig.BeginExceptionBlock ();
4821 Block.Emit (ec);
4823 foreach (Catch c in Specific)
4824 c.Emit (ec);
4826 if (General != null)
4827 General.Emit (ec);
4829 if (!inside_try_finally)
4830 ig.EndExceptionBlock ();
4833 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4835 Block.MutateHoistedGenericType (storey);
4837 if (General != null)
4838 General.MutateHoistedGenericType (storey);
4839 if (Specific != null) {
4840 foreach (Catch c in Specific)
4841 c.MutateHoistedGenericType (storey);
4845 protected override void CloneTo (CloneContext clonectx, Statement t)
4847 TryCatch target = (TryCatch) t;
4849 target.Block = clonectx.LookupBlock (Block);
4850 if (General != null)
4851 target.General = (Catch) General.Clone (clonectx);
4852 if (Specific != null){
4853 target.Specific = new ArrayList ();
4854 foreach (Catch c in Specific)
4855 target.Specific.Add (c.Clone (clonectx));
4860 // FIXME: Why is it almost exact copy of Using ??
4861 public class UsingTemporary : ExceptionStatement {
4862 TemporaryVariable local_copy;
4863 public Statement Statement;
4864 Expression expr;
4865 Type expr_type;
4867 public UsingTemporary (Expression expr, Statement stmt, Location l)
4869 this.expr = expr;
4870 Statement = stmt;
4871 loc = l;
4874 public override bool Resolve (BlockContext ec)
4876 expr = expr.Resolve (ec);
4877 if (expr == null)
4878 return false;
4880 expr_type = expr.Type;
4882 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4883 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4884 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4885 return false;
4889 local_copy = new TemporaryVariable (expr_type, loc);
4890 local_copy.Resolve (ec);
4892 ec.StartFlowBranching (this);
4894 bool ok = Statement.Resolve (ec);
4896 ec.EndFlowBranching ();
4898 ok &= base.Resolve (ec);
4900 if (TypeManager.void_dispose_void == null) {
4901 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4902 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4905 return ok;
4908 protected override void EmitPreTryBody (EmitContext ec)
4910 local_copy.EmitAssign (ec, expr);
4913 protected override void EmitTryBody (EmitContext ec)
4915 Statement.Emit (ec);
4918 protected override void EmitFinallyBody (EmitContext ec)
4920 ILGenerator ig = ec.ig;
4921 if (!TypeManager.IsStruct (expr_type)) {
4922 Label skip = ig.DefineLabel ();
4923 local_copy.Emit (ec);
4924 ig.Emit (OpCodes.Brfalse, skip);
4925 local_copy.Emit (ec);
4926 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4927 ig.MarkLabel (skip);
4928 return;
4931 Expression ml = Expression.MemberLookup (RootContext.ToplevelTypes.Compiler,
4932 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4933 "Dispose", Location.Null);
4935 if (!(ml is MethodGroupExpr)) {
4936 local_copy.Emit (ec);
4937 ig.Emit (OpCodes.Box, expr_type);
4938 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4939 return;
4942 MethodInfo mi = null;
4944 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4945 if (TypeManager.GetParameterData (mk).Count == 0) {
4946 mi = mk;
4947 break;
4951 if (mi == null) {
4952 RootContext.ToplevelTypes.Compiler.Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4953 return;
4956 local_copy.AddressOf (ec, AddressOp.Load);
4957 ig.Emit (OpCodes.Call, mi);
4960 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4962 expr_type = storey.MutateType (expr_type);
4963 local_copy.MutateHoistedGenericType (storey);
4964 Statement.MutateHoistedGenericType (storey);
4967 protected override void CloneTo (CloneContext clonectx, Statement t)
4969 UsingTemporary target = (UsingTemporary) t;
4971 target.expr = expr.Clone (clonectx);
4972 target.Statement = Statement.Clone (clonectx);
4976 public class Using : ExceptionStatement {
4977 Statement stmt;
4978 public Statement EmbeddedStatement {
4979 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4982 Expression var;
4983 Expression init;
4985 ExpressionStatement assign;
4987 public Using (Expression var, Expression init, Statement stmt, Location l)
4989 this.var = var;
4990 this.init = init;
4991 this.stmt = stmt;
4992 loc = l;
4995 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4997 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4998 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4999 TypeManager.CSharpName (expr.Type));
5002 protected override void EmitPreTryBody (EmitContext ec)
5004 assign.EmitStatement (ec);
5007 protected override void EmitTryBody (EmitContext ec)
5009 stmt.Emit (ec);
5012 protected override void EmitFinallyBody (EmitContext ec)
5014 ILGenerator ig = ec.ig;
5015 Label skip = ig.DefineLabel ();
5017 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5018 if (emit_null_check) {
5019 var.Emit (ec);
5020 ig.Emit (OpCodes.Brfalse, skip);
5023 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5025 if (emit_null_check)
5026 ig.MarkLabel (skip);
5029 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5031 assign.MutateHoistedGenericType (storey);
5032 var.MutateHoistedGenericType (storey);
5033 stmt.MutateHoistedGenericType (storey);
5036 public override bool Resolve (BlockContext ec)
5038 if (!ResolveVariable (ec))
5039 return false;
5041 ec.StartFlowBranching (this);
5043 bool ok = stmt.Resolve (ec);
5045 ec.EndFlowBranching ();
5047 ok &= base.Resolve (ec);
5049 if (TypeManager.void_dispose_void == null) {
5050 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5051 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5054 return ok;
5057 bool ResolveVariable (BlockContext ec)
5059 assign = new SimpleAssign (var, init, loc);
5060 assign = assign.ResolveStatement (ec);
5061 if (assign == null)
5062 return false;
5064 if (assign.Type == TypeManager.idisposable_type ||
5065 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5066 return true;
5069 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5070 if (e == null) {
5071 Error_IsNotConvertibleToIDisposable (ec, var);
5072 return false;
5075 throw new NotImplementedException ("covariance?");
5078 protected override void CloneTo (CloneContext clonectx, Statement t)
5080 Using target = (Using) t;
5082 target.var = var.Clone (clonectx);
5083 target.init = init.Clone (clonectx);
5084 target.stmt = stmt.Clone (clonectx);
5088 /// <summary>
5089 /// Implementation of the foreach C# statement
5090 /// </summary>
5091 public class Foreach : Statement {
5093 sealed class ArrayForeach : Statement
5095 class ArrayCounter : TemporaryVariable
5097 StatementExpression increment;
5099 public ArrayCounter (Location loc)
5100 : base (TypeManager.int32_type, loc)
5104 public void ResolveIncrement (BlockContext ec)
5106 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5107 increment.Resolve (ec);
5110 public void EmitIncrement (EmitContext ec)
5112 increment.Emit (ec);
5116 readonly Foreach for_each;
5117 readonly Statement statement;
5119 Expression conv;
5120 TemporaryVariable[] lengths;
5121 Expression [] length_exprs;
5122 ArrayCounter[] counter;
5124 TemporaryVariable copy;
5125 Expression access;
5127 public ArrayForeach (Foreach @foreach, int rank)
5129 for_each = @foreach;
5130 statement = for_each.statement;
5131 loc = @foreach.loc;
5133 counter = new ArrayCounter [rank];
5134 length_exprs = new Expression [rank];
5137 // Only use temporary length variables when dealing with
5138 // multi-dimensional arrays
5140 if (rank > 1)
5141 lengths = new TemporaryVariable [rank];
5144 protected override void CloneTo (CloneContext clonectx, Statement target)
5146 throw new NotImplementedException ();
5149 public override bool Resolve (BlockContext ec)
5151 copy = new TemporaryVariable (for_each.expr.Type, loc);
5152 copy.Resolve (ec);
5154 int rank = length_exprs.Length;
5155 Arguments list = new Arguments (rank);
5156 for (int i = 0; i < rank; i++) {
5157 counter [i] = new ArrayCounter (loc);
5158 counter [i].ResolveIncrement (ec);
5160 if (rank == 1) {
5161 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5162 } else {
5163 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5164 lengths [i].Resolve (ec);
5166 Arguments args = new Arguments (1);
5167 args.Add (new Argument (new IntConstant (i, loc)));
5168 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5171 list.Add (new Argument (counter [i]));
5174 access = new ElementAccess (copy, list).Resolve (ec);
5175 if (access == null)
5176 return false;
5178 Expression var_type = for_each.type;
5179 VarExpr ve = var_type as VarExpr;
5180 if (ve != null) {
5181 // Infer implicitly typed local variable from foreach array type
5182 var_type = new TypeExpression (access.Type, ve.Location);
5185 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5186 if (var_type == null)
5187 return false;
5189 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5190 if (conv == null)
5191 return false;
5193 bool ok = true;
5195 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5196 ec.CurrentBranching.CreateSibling ();
5198 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5199 if (for_each.variable == null)
5200 ok = false;
5202 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5203 if (!statement.Resolve (ec))
5204 ok = false;
5205 ec.EndFlowBranching ();
5207 // There's no direct control flow from the end of the embedded statement to the end of the loop
5208 ec.CurrentBranching.CurrentUsageVector.Goto ();
5210 ec.EndFlowBranching ();
5212 return ok;
5215 protected override void DoEmit (EmitContext ec)
5217 ILGenerator ig = ec.ig;
5219 copy.EmitAssign (ec, for_each.expr);
5221 int rank = length_exprs.Length;
5222 Label[] test = new Label [rank];
5223 Label[] loop = new Label [rank];
5225 for (int i = 0; i < rank; i++) {
5226 test [i] = ig.DefineLabel ();
5227 loop [i] = ig.DefineLabel ();
5229 if (lengths != null)
5230 lengths [i].EmitAssign (ec, length_exprs [i]);
5233 IntConstant zero = new IntConstant (0, loc);
5234 for (int i = 0; i < rank; i++) {
5235 counter [i].EmitAssign (ec, zero);
5237 ig.Emit (OpCodes.Br, test [i]);
5238 ig.MarkLabel (loop [i]);
5241 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5243 statement.Emit (ec);
5245 ig.MarkLabel (ec.LoopBegin);
5247 for (int i = rank - 1; i >= 0; i--){
5248 counter [i].EmitIncrement (ec);
5250 ig.MarkLabel (test [i]);
5251 counter [i].Emit (ec);
5253 if (lengths != null)
5254 lengths [i].Emit (ec);
5255 else
5256 length_exprs [i].Emit (ec);
5258 ig.Emit (OpCodes.Blt, loop [i]);
5261 ig.MarkLabel (ec.LoopEnd);
5264 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5266 for_each.expr.MutateHoistedGenericType (storey);
5268 copy.MutateHoistedGenericType (storey);
5269 conv.MutateHoistedGenericType (storey);
5270 statement.MutateHoistedGenericType (storey);
5272 for (int i = 0; i < counter.Length; i++) {
5273 counter [i].MutateHoistedGenericType (storey);
5274 if (lengths != null)
5275 lengths [i].MutateHoistedGenericType (storey);
5280 sealed class CollectionForeach : Statement
5282 class CollectionForeachStatement : Statement
5284 Type type;
5285 Expression variable, current, conv;
5286 Statement statement;
5287 Assign assign;
5289 public CollectionForeachStatement (Type type, Expression variable,
5290 Expression current, Statement statement,
5291 Location loc)
5293 this.type = type;
5294 this.variable = variable;
5295 this.current = current;
5296 this.statement = statement;
5297 this.loc = loc;
5300 protected override void CloneTo (CloneContext clonectx, Statement target)
5302 throw new NotImplementedException ();
5305 public override bool Resolve (BlockContext ec)
5307 current = current.Resolve (ec);
5308 if (current == null)
5309 return false;
5311 conv = Convert.ExplicitConversion (ec, current, type, loc);
5312 if (conv == null)
5313 return false;
5315 assign = new SimpleAssign (variable, conv, loc);
5316 if (assign.Resolve (ec) == null)
5317 return false;
5319 if (!statement.Resolve (ec))
5320 return false;
5322 return true;
5325 protected override void DoEmit (EmitContext ec)
5327 assign.EmitStatement (ec);
5328 statement.Emit (ec);
5331 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5333 assign.MutateHoistedGenericType (storey);
5334 statement.MutateHoistedGenericType (storey);
5338 Expression variable, expr;
5339 Statement statement;
5341 TemporaryVariable enumerator;
5342 Expression init;
5343 Statement loop;
5344 Statement wrapper;
5346 MethodGroupExpr get_enumerator;
5347 PropertyExpr get_current;
5348 MethodInfo move_next;
5349 Expression var_type;
5350 Type enumerator_type;
5351 bool enumerator_found;
5353 public CollectionForeach (Expression var_type, Expression var,
5354 Expression expr, Statement stmt, Location l)
5356 this.var_type = var_type;
5357 this.variable = var;
5358 this.expr = expr;
5359 statement = stmt;
5360 loc = l;
5363 protected override void CloneTo (CloneContext clonectx, Statement target)
5365 throw new NotImplementedException ();
5368 bool GetEnumeratorFilter (ResolveContext ec, MethodInfo mi)
5370 Type return_type = mi.ReturnType;
5373 // Ok, we can access it, now make sure that we can do something
5374 // with this `GetEnumerator'
5377 if (return_type == TypeManager.ienumerator_type ||
5378 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5380 // If it is not an interface, lets try to find the methods ourselves.
5381 // For example, if we have:
5382 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5383 // We can avoid the iface call. This is a runtime perf boost.
5384 // even bigger if we have a ValueType, because we avoid the cost
5385 // of boxing.
5387 // We have to make sure that both methods exist for us to take
5388 // this path. If one of the methods does not exist, we will just
5389 // use the interface. Sadly, this complex if statement is the only
5390 // way I could do this without a goto
5393 if (TypeManager.bool_movenext_void == null) {
5394 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5395 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5398 if (TypeManager.ienumerator_getcurrent == null) {
5399 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5400 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5404 // Prefer a generic enumerator over a non-generic one.
5406 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5407 enumerator_type = return_type;
5408 if (!FetchGetCurrent (ec, return_type))
5409 get_current = new PropertyExpr (
5410 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5411 if (!FetchMoveNext (return_type))
5412 move_next = TypeManager.bool_movenext_void;
5413 return true;
5416 if (return_type.IsInterface ||
5417 !FetchMoveNext (return_type) ||
5418 !FetchGetCurrent (ec, return_type)) {
5419 enumerator_type = return_type;
5420 move_next = TypeManager.bool_movenext_void;
5421 get_current = new PropertyExpr (
5422 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5423 return true;
5425 } else {
5427 // Ok, so they dont return an IEnumerable, we will have to
5428 // find if they support the GetEnumerator pattern.
5431 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5432 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",
5433 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5434 return false;
5438 enumerator_type = return_type;
5440 return true;
5444 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5446 bool FetchMoveNext (Type t)
5448 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5449 MemberTypes.Method,
5450 BindingFlags.Public | BindingFlags.Instance,
5451 "MoveNext", null);
5453 if (move_next_list == null)
5454 return false;
5456 foreach (MemberInfo m in move_next_list){
5457 MethodInfo mi = (MethodInfo) m;
5459 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5460 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5461 move_next = mi;
5462 return true;
5466 return false;
5470 // Retrieves a `public T get_Current ()' method from the Type `t'
5472 bool FetchGetCurrent (ResolveContext ec, Type t)
5474 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5475 ec.CurrentType, t, "Current", MemberTypes.Property,
5476 Expression.AllBindingFlags, loc) as PropertyExpr;
5477 if (pe == null)
5478 return false;
5480 get_current = pe;
5481 return true;
5484 void Error_Enumerator (BlockContext ec)
5486 if (enumerator_found) {
5487 return;
5490 ec.Report.Error (1579, loc,
5491 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5492 TypeManager.CSharpName (expr.Type));
5495 bool IsOverride (MethodInfo m)
5497 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5499 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5500 return false;
5501 if (m is MethodBuilder)
5502 return true;
5504 MethodInfo base_method = m.GetBaseDefinition ();
5505 return base_method != m;
5508 bool TryType (ResolveContext ec, Type t)
5510 MethodGroupExpr mg = Expression.MemberLookup (ec.Compiler,
5511 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5512 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5513 if (mg == null)
5514 return false;
5516 MethodInfo result = null;
5517 MethodInfo tmp_move_next = null;
5518 PropertyExpr tmp_get_cur = null;
5519 Type tmp_enumerator_type = enumerator_type;
5520 foreach (MethodInfo mi in mg.Methods) {
5521 if (TypeManager.GetParameterData (mi).Count != 0)
5522 continue;
5524 // Check whether GetEnumerator is public
5525 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5526 continue;
5528 if (IsOverride (mi))
5529 continue;
5531 enumerator_found = true;
5533 if (!GetEnumeratorFilter (ec, mi))
5534 continue;
5536 if (result != null) {
5537 if (TypeManager.IsGenericType (result.ReturnType)) {
5538 if (!TypeManager.IsGenericType (mi.ReturnType))
5539 continue;
5541 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5542 ec.Report.SymbolRelatedToPreviousError (t);
5543 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5544 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5545 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5546 return false;
5549 // Always prefer generics enumerators
5550 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5551 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5552 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5553 continue;
5555 ec.Report.SymbolRelatedToPreviousError (result);
5556 ec.Report.SymbolRelatedToPreviousError (mi);
5557 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5558 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5559 return false;
5562 result = mi;
5563 tmp_move_next = move_next;
5564 tmp_get_cur = get_current;
5565 tmp_enumerator_type = enumerator_type;
5566 if (mi.DeclaringType == t)
5567 break;
5570 if (result != null) {
5571 move_next = tmp_move_next;
5572 get_current = tmp_get_cur;
5573 enumerator_type = tmp_enumerator_type;
5574 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5575 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5577 if (t != expr.Type) {
5578 expr = Convert.ExplicitConversion (
5579 ec, expr, t, loc);
5580 if (expr == null)
5581 throw new InternalErrorException ();
5584 get_enumerator.InstanceExpression = expr;
5585 get_enumerator.IsBase = t != expr.Type;
5587 return true;
5590 return false;
5593 bool ProbeCollectionType (ResolveContext ec, Type t)
5595 int errors = ec.Report.Errors;
5596 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5597 if (TryType (ec, tt))
5598 return true;
5599 tt = tt.BaseType;
5602 if (ec.Report.Errors > errors)
5603 return false;
5606 // Now try to find the method in the interfaces
5608 Type [] ifaces = TypeManager.GetInterfaces (t);
5609 foreach (Type i in ifaces){
5610 if (TryType (ec, i))
5611 return true;
5614 return false;
5617 public override bool Resolve (BlockContext ec)
5619 enumerator_type = TypeManager.ienumerator_type;
5621 if (!ProbeCollectionType (ec, expr.Type)) {
5622 Error_Enumerator (ec);
5623 return false;
5626 VarExpr ve = var_type as VarExpr;
5627 if (ve != null) {
5628 // Infer implicitly typed local variable from foreach enumerable type
5629 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5632 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5633 if (var_type == null)
5634 return false;
5636 enumerator = new TemporaryVariable (enumerator_type, loc);
5637 enumerator.Resolve (ec);
5639 init = new Invocation (get_enumerator, null);
5640 init = init.Resolve (ec);
5641 if (init == null)
5642 return false;
5644 Expression move_next_expr;
5646 MemberInfo[] mi = new MemberInfo[] { move_next };
5647 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5648 mg.InstanceExpression = enumerator;
5650 move_next_expr = new Invocation (mg, null);
5653 get_current.InstanceExpression = enumerator;
5655 Statement block = new CollectionForeachStatement (
5656 var_type.Type, variable, get_current, statement, loc);
5658 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5661 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5662 if (implements_idisposable || !enumerator_type.IsSealed) {
5663 wrapper = new DisposableWrapper (this, implements_idisposable);
5664 } else {
5665 wrapper = new NonDisposableWrapper (this);
5668 return wrapper.Resolve (ec);
5671 protected override void DoEmit (EmitContext ec)
5673 wrapper.Emit (ec);
5676 class NonDisposableWrapper : Statement {
5677 CollectionForeach parent;
5679 internal NonDisposableWrapper (CollectionForeach parent)
5681 this.parent = parent;
5684 protected override void CloneTo (CloneContext clonectx, Statement target)
5686 throw new NotSupportedException ();
5689 public override bool Resolve (BlockContext ec)
5691 return parent.ResolveLoop (ec);
5694 protected override void DoEmit (EmitContext ec)
5696 parent.EmitLoopInit (ec);
5697 parent.EmitLoopBody (ec);
5700 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5702 throw new NotSupportedException ();
5706 sealed class DisposableWrapper : ExceptionStatement
5708 CollectionForeach parent;
5709 bool implements_idisposable;
5711 internal DisposableWrapper (CollectionForeach parent, bool implements)
5713 this.parent = parent;
5714 this.implements_idisposable = implements;
5717 protected override void CloneTo (CloneContext clonectx, Statement target)
5719 throw new NotSupportedException ();
5722 public override bool Resolve (BlockContext ec)
5724 bool ok = true;
5726 ec.StartFlowBranching (this);
5728 if (!parent.ResolveLoop (ec))
5729 ok = false;
5731 ec.EndFlowBranching ();
5733 ok &= base.Resolve (ec);
5735 if (TypeManager.void_dispose_void == null) {
5736 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5737 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5739 return ok;
5742 protected override void EmitPreTryBody (EmitContext ec)
5744 parent.EmitLoopInit (ec);
5747 protected override void EmitTryBody (EmitContext ec)
5749 parent.EmitLoopBody (ec);
5752 protected override void EmitFinallyBody (EmitContext ec)
5754 Expression instance = parent.enumerator;
5755 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5756 ILGenerator ig = ec.ig;
5758 parent.enumerator.Emit (ec);
5760 Label call_dispose = ig.DefineLabel ();
5762 if (!implements_idisposable) {
5763 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5764 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5765 temp.Store (ec);
5766 temp.Emit (ec);
5767 instance = temp;
5770 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5772 // using 'endfinally' to empty the evaluation stack
5773 ig.Emit (OpCodes.Endfinally);
5774 ig.MarkLabel (call_dispose);
5777 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5780 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5782 throw new NotSupportedException ();
5786 bool ResolveLoop (BlockContext ec)
5788 return loop.Resolve (ec);
5791 void EmitLoopInit (EmitContext ec)
5793 enumerator.EmitAssign (ec, init);
5796 void EmitLoopBody (EmitContext ec)
5798 loop.Emit (ec);
5801 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5803 enumerator_type = storey.MutateType (enumerator_type);
5804 init.MutateHoistedGenericType (storey);
5805 loop.MutateHoistedGenericType (storey);
5809 Expression type;
5810 Expression variable;
5811 Expression expr;
5812 Statement statement;
5814 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5815 Statement stmt, Location l)
5817 this.type = type;
5818 this.variable = var;
5819 this.expr = expr;
5820 statement = stmt;
5821 loc = l;
5824 public Statement Statement {
5825 get { return statement; }
5828 public override bool Resolve (BlockContext ec)
5830 expr = expr.Resolve (ec);
5831 if (expr == null)
5832 return false;
5834 if (expr.IsNull) {
5835 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5836 return false;
5839 if (expr.Type == TypeManager.string_type) {
5840 statement = new ArrayForeach (this, 1);
5841 } else if (expr.Type.IsArray) {
5842 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5843 } else {
5844 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5845 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5846 expr.ExprClassName);
5847 return false;
5850 statement = new CollectionForeach (type, variable, expr, statement, loc);
5853 return statement.Resolve (ec);
5856 protected override void DoEmit (EmitContext ec)
5858 ILGenerator ig = ec.ig;
5860 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5861 ec.LoopBegin = ig.DefineLabel ();
5862 ec.LoopEnd = ig.DefineLabel ();
5864 statement.Emit (ec);
5866 ec.LoopBegin = old_begin;
5867 ec.LoopEnd = old_end;
5870 protected override void CloneTo (CloneContext clonectx, Statement t)
5872 Foreach target = (Foreach) t;
5874 target.type = type.Clone (clonectx);
5875 target.variable = variable.Clone (clonectx);
5876 target.expr = expr.Clone (clonectx);
5877 target.statement = statement.Clone (clonectx);
5880 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5882 statement.MutateHoistedGenericType (storey);