2010-01-12 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / mcs / statement.cs
blob798a213bb9cda0c1ac24355a2cba9cc6a553c71f
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 expr, Statement true_statement, Location l)
139 this.expr = expr;
140 TrueStatement = true_statement;
141 loc = l;
144 public If (Expression expr,
145 Statement true_statement,
146 Statement false_statement,
147 Location l)
149 this.expr = expr;
150 TrueStatement = true_statement;
151 FalseStatement = false_statement;
152 loc = l;
155 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
157 expr.MutateHoistedGenericType (storey);
158 TrueStatement.MutateHoistedGenericType (storey);
159 if (FalseStatement != null)
160 FalseStatement.MutateHoistedGenericType (storey);
163 public override bool Resolve (BlockContext ec)
165 bool ok = true;
167 Report.Debug (1, "START IF BLOCK", loc);
169 expr = Expression.ResolveBoolean (ec, expr, loc);
170 if (expr == null){
171 ok = false;
172 goto skip;
175 Assign ass = expr as Assign;
176 if (ass != null && ass.Source is Constant) {
177 ec.Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
181 // Dead code elimination
183 if (expr is Constant){
184 bool take = !((Constant) expr).IsDefaultValue;
186 if (take){
187 if (!TrueStatement.Resolve (ec))
188 return false;
190 if ((FalseStatement != null) &&
191 !FalseStatement.ResolveUnreachable (ec, true))
192 return false;
193 FalseStatement = null;
194 } else {
195 if (!TrueStatement.ResolveUnreachable (ec, true))
196 return false;
197 TrueStatement = null;
199 if ((FalseStatement != null) &&
200 !FalseStatement.Resolve (ec))
201 return false;
204 return true;
206 skip:
207 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
209 ok &= TrueStatement.Resolve (ec);
211 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
213 ec.CurrentBranching.CreateSibling ();
215 if (FalseStatement != null)
216 ok &= FalseStatement.Resolve (ec);
218 ec.EndFlowBranching ();
220 Report.Debug (1, "END IF BLOCK", loc);
222 return ok;
225 protected override void DoEmit (EmitContext ec)
227 ILGenerator ig = ec.ig;
228 Label false_target = ig.DefineLabel ();
229 Label end;
232 // If we're a boolean constant, Resolve() already
233 // eliminated dead code for us.
235 Constant c = expr as Constant;
236 if (c != null){
237 c.EmitSideEffect (ec);
239 if (!c.IsDefaultValue)
240 TrueStatement.Emit (ec);
241 else if (FalseStatement != null)
242 FalseStatement.Emit (ec);
244 return;
247 expr.EmitBranchable (ec, false_target, false);
249 TrueStatement.Emit (ec);
251 if (FalseStatement != null){
252 bool branch_emitted = false;
254 end = ig.DefineLabel ();
255 if (!is_true_ret){
256 ig.Emit (OpCodes.Br, end);
257 branch_emitted = true;
260 ig.MarkLabel (false_target);
261 FalseStatement.Emit (ec);
263 if (branch_emitted)
264 ig.MarkLabel (end);
265 } else {
266 ig.MarkLabel (false_target);
270 protected override void CloneTo (CloneContext clonectx, Statement t)
272 If target = (If) t;
274 target.expr = expr.Clone (clonectx);
275 target.TrueStatement = TrueStatement.Clone (clonectx);
276 if (FalseStatement != null)
277 target.FalseStatement = FalseStatement.Clone (clonectx);
281 public class Do : Statement {
282 public Expression expr;
283 public Statement EmbeddedStatement;
285 public Do (Statement statement, Expression bool_expr, Location l)
287 expr = bool_expr;
288 EmbeddedStatement = statement;
289 loc = l;
292 public override bool Resolve (BlockContext ec)
294 bool ok = true;
296 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
298 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
300 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
301 if (!EmbeddedStatement.Resolve (ec))
302 ok = false;
303 ec.EndFlowBranching ();
305 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
306 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
308 expr = Expression.ResolveBoolean (ec, expr, loc);
309 if (expr == null)
310 ok = false;
311 else if (expr is Constant){
312 bool infinite = !((Constant) expr).IsDefaultValue;
313 if (infinite)
314 ec.CurrentBranching.CurrentUsageVector.Goto ();
317 ec.EndFlowBranching ();
319 return ok;
322 protected override void DoEmit (EmitContext ec)
324 ILGenerator ig = ec.ig;
325 Label loop = ig.DefineLabel ();
326 Label old_begin = ec.LoopBegin;
327 Label old_end = ec.LoopEnd;
329 ec.LoopBegin = ig.DefineLabel ();
330 ec.LoopEnd = ig.DefineLabel ();
332 ig.MarkLabel (loop);
333 EmbeddedStatement.Emit (ec);
334 ig.MarkLabel (ec.LoopBegin);
337 // Dead code elimination
339 if (expr is Constant){
340 bool res = !((Constant) expr).IsDefaultValue;
342 expr.EmitSideEffect (ec);
343 if (res)
344 ec.ig.Emit (OpCodes.Br, loop);
345 } else
346 expr.EmitBranchable (ec, loop, true);
348 ig.MarkLabel (ec.LoopEnd);
350 ec.LoopBegin = old_begin;
351 ec.LoopEnd = old_end;
354 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
356 expr.MutateHoistedGenericType (storey);
357 EmbeddedStatement.MutateHoistedGenericType (storey);
360 protected override void CloneTo (CloneContext clonectx, Statement t)
362 Do target = (Do) t;
364 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
365 target.expr = expr.Clone (clonectx);
369 public class While : Statement {
370 public Expression expr;
371 public Statement Statement;
372 bool infinite, empty;
374 public While (Expression bool_expr, Statement statement, Location l)
376 this.expr = bool_expr;
377 Statement = statement;
378 loc = l;
381 public override bool Resolve (BlockContext ec)
383 bool ok = true;
385 expr = Expression.ResolveBoolean (ec, expr, loc);
386 if (expr == null)
387 return false;
390 // Inform whether we are infinite or not
392 if (expr is Constant){
393 bool value = !((Constant) expr).IsDefaultValue;
395 if (value == false){
396 if (!Statement.ResolveUnreachable (ec, true))
397 return false;
398 empty = true;
399 return true;
400 } else
401 infinite = true;
404 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
405 if (!infinite)
406 ec.CurrentBranching.CreateSibling ();
408 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
409 if (!Statement.Resolve (ec))
410 ok = false;
411 ec.EndFlowBranching ();
413 // There's no direct control flow from the end of the embedded statement to the end of the loop
414 ec.CurrentBranching.CurrentUsageVector.Goto ();
416 ec.EndFlowBranching ();
418 return ok;
421 protected override void DoEmit (EmitContext ec)
423 if (empty) {
424 expr.EmitSideEffect (ec);
425 return;
428 ILGenerator ig = ec.ig;
429 Label old_begin = ec.LoopBegin;
430 Label old_end = ec.LoopEnd;
432 ec.LoopBegin = ig.DefineLabel ();
433 ec.LoopEnd = ig.DefineLabel ();
436 // Inform whether we are infinite or not
438 if (expr is Constant){
439 // expr is 'true', since the 'empty' case above handles the 'false' case
440 ig.MarkLabel (ec.LoopBegin);
441 expr.EmitSideEffect (ec);
442 Statement.Emit (ec);
443 ig.Emit (OpCodes.Br, ec.LoopBegin);
446 // Inform that we are infinite (ie, `we return'), only
447 // if we do not `break' inside the code.
449 ig.MarkLabel (ec.LoopEnd);
450 } else {
451 Label while_loop = ig.DefineLabel ();
453 ig.Emit (OpCodes.Br, ec.LoopBegin);
454 ig.MarkLabel (while_loop);
456 Statement.Emit (ec);
458 ig.MarkLabel (ec.LoopBegin);
459 ec.Mark (loc);
461 expr.EmitBranchable (ec, while_loop, true);
463 ig.MarkLabel (ec.LoopEnd);
466 ec.LoopBegin = old_begin;
467 ec.LoopEnd = old_end;
470 public override void Emit (EmitContext ec)
472 DoEmit (ec);
475 protected override void CloneTo (CloneContext clonectx, Statement t)
477 While target = (While) t;
479 target.expr = expr.Clone (clonectx);
480 target.Statement = Statement.Clone (clonectx);
483 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
485 expr.MutateHoistedGenericType (storey);
486 Statement.MutateHoistedGenericType (storey);
490 public class For : Statement {
491 Expression Test;
492 Statement InitStatement;
493 Statement Increment;
494 public Statement Statement;
495 bool infinite, empty;
497 public For (Statement init_statement,
498 Expression test,
499 Statement increment,
500 Statement statement,
501 Location l)
503 InitStatement = init_statement;
504 Test = test;
505 Increment = increment;
506 Statement = statement;
507 loc = l;
510 public override bool Resolve (BlockContext ec)
512 bool ok = true;
514 if (InitStatement != null){
515 if (!InitStatement.Resolve (ec))
516 ok = false;
519 if (Test != null){
520 Test = Expression.ResolveBoolean (ec, Test, loc);
521 if (Test == null)
522 ok = false;
523 else if (Test is Constant){
524 bool value = !((Constant) Test).IsDefaultValue;
526 if (value == false){
527 if (!Statement.ResolveUnreachable (ec, true))
528 return false;
529 if ((Increment != null) &&
530 !Increment.ResolveUnreachable (ec, false))
531 return false;
532 empty = true;
533 return true;
534 } else
535 infinite = true;
537 } else
538 infinite = true;
540 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
541 if (!infinite)
542 ec.CurrentBranching.CreateSibling ();
544 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
546 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
547 if (!Statement.Resolve (ec))
548 ok = false;
549 ec.EndFlowBranching ();
551 if (Increment != null){
552 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
553 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
554 ok = false;
555 } else {
556 if (!Increment.Resolve (ec))
557 ok = false;
561 // There's no direct control flow from the end of the embedded statement to the end of the loop
562 ec.CurrentBranching.CurrentUsageVector.Goto ();
564 ec.EndFlowBranching ();
566 return ok;
569 protected override void DoEmit (EmitContext ec)
571 if (InitStatement != null && InitStatement != EmptyStatement.Value)
572 InitStatement.Emit (ec);
574 if (empty) {
575 Test.EmitSideEffect (ec);
576 return;
579 ILGenerator ig = ec.ig;
580 Label old_begin = ec.LoopBegin;
581 Label old_end = ec.LoopEnd;
582 Label loop = ig.DefineLabel ();
583 Label test = ig.DefineLabel ();
585 ec.LoopBegin = ig.DefineLabel ();
586 ec.LoopEnd = ig.DefineLabel ();
588 ig.Emit (OpCodes.Br, test);
589 ig.MarkLabel (loop);
590 Statement.Emit (ec);
592 ig.MarkLabel (ec.LoopBegin);
593 if (Increment != EmptyStatement.Value)
594 Increment.Emit (ec);
596 ig.MarkLabel (test);
598 // If test is null, there is no test, and we are just
599 // an infinite loop
601 if (Test != null){
603 // The Resolve code already catches the case for
604 // Test == Constant (false) so we know that
605 // this is true
607 if (Test is Constant) {
608 Test.EmitSideEffect (ec);
609 ig.Emit (OpCodes.Br, loop);
610 } else {
611 Test.EmitBranchable (ec, loop, true);
614 } else
615 ig.Emit (OpCodes.Br, loop);
616 ig.MarkLabel (ec.LoopEnd);
618 ec.LoopBegin = old_begin;
619 ec.LoopEnd = old_end;
622 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
624 if (InitStatement != null)
625 InitStatement.MutateHoistedGenericType (storey);
626 if (Test != null)
627 Test.MutateHoistedGenericType (storey);
628 if (Increment != null)
629 Increment.MutateHoistedGenericType (storey);
631 Statement.MutateHoistedGenericType (storey);
634 protected override void CloneTo (CloneContext clonectx, Statement t)
636 For target = (For) t;
638 if (InitStatement != null)
639 target.InitStatement = InitStatement.Clone (clonectx);
640 if (Test != null)
641 target.Test = Test.Clone (clonectx);
642 if (Increment != null)
643 target.Increment = Increment.Clone (clonectx);
644 target.Statement = Statement.Clone (clonectx);
648 public class StatementExpression : Statement {
649 ExpressionStatement expr;
651 public StatementExpression (ExpressionStatement expr)
653 this.expr = expr;
654 loc = expr.Location;
657 public override bool Resolve (BlockContext ec)
659 if (expr != null && expr.eclass == ExprClass.Invalid)
660 expr = expr.ResolveStatement (ec);
661 return expr != null;
664 protected override void DoEmit (EmitContext ec)
666 expr.EmitStatement (ec);
669 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
671 expr.MutateHoistedGenericType (storey);
674 public override string ToString ()
676 return "StatementExpression (" + expr + ")";
679 protected override void CloneTo (CloneContext clonectx, Statement t)
681 StatementExpression target = (StatementExpression) t;
683 target.expr = (ExpressionStatement) expr.Clone (clonectx);
687 // A 'return' or a 'yield break'
688 public abstract class ExitStatement : Statement
690 protected bool unwind_protect;
691 protected abstract bool DoResolve (BlockContext ec);
693 public virtual void Error_FinallyClause (Report Report)
695 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
698 public sealed override bool Resolve (BlockContext ec)
700 if (!DoResolve (ec))
701 return false;
703 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
704 if (unwind_protect)
705 ec.NeedReturnLabel ();
706 ec.CurrentBranching.CurrentUsageVector.Goto ();
707 return true;
711 /// <summary>
712 /// Implements the return statement
713 /// </summary>
714 public class Return : ExitStatement {
715 protected Expression Expr;
716 public Return (Expression expr, Location l)
718 Expr = expr;
719 loc = l;
722 protected override bool DoResolve (BlockContext ec)
724 if (Expr == null) {
725 if (ec.ReturnType == TypeManager.void_type)
726 return true;
728 ec.Report.Error (126, loc,
729 "An object of a type convertible to `{0}' is required for the return statement",
730 TypeManager.CSharpName (ec.ReturnType));
731 return false;
734 if (ec.CurrentBlock.Toplevel.IsIterator) {
735 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
736 "statement to return a value, or yield break to end the iteration");
739 AnonymousExpression am = ec.CurrentAnonymousMethod;
740 if (am == null && ec.ReturnType == TypeManager.void_type) {
741 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
742 ec.GetSignatureForError ());
745 Expr = Expr.Resolve (ec);
746 if (Expr == null)
747 return false;
749 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
750 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
751 return true;
754 if (Expr.Type != ec.ReturnType) {
755 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
757 if (Expr == null) {
758 if (am != null) {
759 ec.Report.Error (1662, loc,
760 "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",
761 am.ContainerType, am.GetSignatureForError ());
763 return false;
767 return true;
770 protected override void DoEmit (EmitContext ec)
772 if (Expr != null) {
773 Expr.Emit (ec);
775 if (unwind_protect)
776 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
779 if (unwind_protect)
780 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
781 else
782 ec.ig.Emit (OpCodes.Ret);
785 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
787 if (Expr != null)
788 Expr.MutateHoistedGenericType (storey);
791 protected override void CloneTo (CloneContext clonectx, Statement t)
793 Return target = (Return) t;
794 // It's null for simple return;
795 if (Expr != null)
796 target.Expr = Expr.Clone (clonectx);
800 public class Goto : Statement {
801 string target;
802 LabeledStatement label;
803 bool unwind_protect;
805 public override bool Resolve (BlockContext ec)
807 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
808 ec.CurrentBranching.CurrentUsageVector.Goto ();
809 return true;
812 public Goto (string label, Location l)
814 loc = l;
815 target = label;
818 public string Target {
819 get { return target; }
822 public void SetResolvedTarget (LabeledStatement label)
824 this.label = label;
825 label.AddReference ();
828 protected override void CloneTo (CloneContext clonectx, Statement target)
830 // Nothing to clone
833 protected override void DoEmit (EmitContext ec)
835 if (label == null)
836 throw new InternalErrorException ("goto emitted before target resolved");
837 Label l = label.LabelTarget (ec);
838 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
841 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
846 public class LabeledStatement : Statement {
847 string name;
848 bool defined;
849 bool referenced;
850 Label label;
851 ILGenerator ig;
853 FlowBranching.UsageVector vectors;
855 public LabeledStatement (string name, Location l)
857 this.name = name;
858 this.loc = l;
861 public Label LabelTarget (EmitContext ec)
863 if (defined)
864 return label;
865 ig = ec.ig;
866 label = ec.ig.DefineLabel ();
867 defined = true;
869 return label;
872 public string Name {
873 get { return name; }
876 public bool IsDefined {
877 get { return defined; }
880 public bool HasBeenReferenced {
881 get { return referenced; }
884 public FlowBranching.UsageVector JumpOrigins {
885 get { return vectors; }
888 public void AddUsageVector (FlowBranching.UsageVector vector)
890 vector = vector.Clone ();
891 vector.Next = vectors;
892 vectors = vector;
895 protected override void CloneTo (CloneContext clonectx, Statement target)
897 // nothing to clone
900 public override bool Resolve (BlockContext ec)
902 // this flow-branching will be terminated when the surrounding block ends
903 ec.StartFlowBranching (this);
904 return true;
907 protected override void DoEmit (EmitContext ec)
909 if (ig != null && ig != ec.ig)
910 throw new InternalErrorException ("cannot happen");
911 LabelTarget (ec);
912 ec.ig.MarkLabel (label);
915 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
919 public void AddReference ()
921 referenced = true;
926 /// <summary>
927 /// `goto default' statement
928 /// </summary>
929 public class GotoDefault : Statement {
931 public GotoDefault (Location l)
933 loc = l;
936 protected override void CloneTo (CloneContext clonectx, Statement target)
938 // nothing to clone
941 public override bool Resolve (BlockContext ec)
943 ec.CurrentBranching.CurrentUsageVector.Goto ();
945 if (ec.Switch == null) {
946 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
947 return false;
950 if (!ec.Switch.GotDefault) {
951 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
952 return false;
955 return true;
958 protected override void DoEmit (EmitContext ec)
960 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
963 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
968 /// <summary>
969 /// `goto case' statement
970 /// </summary>
971 public class GotoCase : Statement {
972 Expression expr;
973 SwitchLabel sl;
975 public GotoCase (Expression e, Location l)
977 expr = e;
978 loc = l;
981 public override bool Resolve (BlockContext ec)
983 if (ec.Switch == null){
984 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
985 return false;
988 ec.CurrentBranching.CurrentUsageVector.Goto ();
990 expr = expr.Resolve (ec);
991 if (expr == null)
992 return false;
994 Constant c = expr as Constant;
995 if (c == null) {
996 ec.Report.Error (150, expr.Location, "A constant value is expected");
997 return false;
1000 Type type = ec.Switch.SwitchType;
1001 Constant res = c.TryReduce (ec, type, c.Location);
1002 if (res == null) {
1003 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1004 return false;
1007 if (!Convert.ImplicitStandardConversionExists (c, type))
1008 ec.Report.Warning (469, 2, loc,
1009 "The `goto case' value is not implicitly convertible to type `{0}'",
1010 TypeManager.CSharpName (type));
1012 object val = res.GetValue ();
1013 if (val == null)
1014 val = SwitchLabel.NullStringCase;
1016 sl = (SwitchLabel) ec.Switch.Elements [val];
1018 if (sl == null){
1019 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1020 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1021 return false;
1024 return true;
1027 protected override void DoEmit (EmitContext ec)
1029 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1032 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1034 expr.MutateHoistedGenericType (storey);
1037 protected override void CloneTo (CloneContext clonectx, Statement t)
1039 GotoCase target = (GotoCase) t;
1041 target.expr = expr.Clone (clonectx);
1045 public class Throw : Statement {
1046 Expression expr;
1048 public Throw (Expression expr, Location l)
1050 this.expr = expr;
1051 loc = l;
1054 public override bool Resolve (BlockContext ec)
1056 if (expr == null) {
1057 ec.CurrentBranching.CurrentUsageVector.Goto ();
1058 return ec.CurrentBranching.CheckRethrow (loc);
1061 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1062 ec.CurrentBranching.CurrentUsageVector.Goto ();
1064 if (expr == null)
1065 return false;
1067 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1068 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1069 else
1070 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1072 return true;
1075 protected override void DoEmit (EmitContext ec)
1077 if (expr == null)
1078 ec.ig.Emit (OpCodes.Rethrow);
1079 else {
1080 expr.Emit (ec);
1082 ec.ig.Emit (OpCodes.Throw);
1086 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1088 if (expr != null)
1089 expr.MutateHoistedGenericType (storey);
1092 protected override void CloneTo (CloneContext clonectx, Statement t)
1094 Throw target = (Throw) t;
1096 if (expr != null)
1097 target.expr = expr.Clone (clonectx);
1101 public class Break : Statement {
1103 public Break (Location l)
1105 loc = l;
1108 bool unwind_protect;
1110 public override bool Resolve (BlockContext ec)
1112 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1113 ec.CurrentBranching.CurrentUsageVector.Goto ();
1114 return true;
1117 protected override void DoEmit (EmitContext ec)
1119 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1122 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1126 protected override void CloneTo (CloneContext clonectx, Statement t)
1128 // nothing needed
1132 public class Continue : Statement {
1134 public Continue (Location l)
1136 loc = l;
1139 bool unwind_protect;
1141 public override bool Resolve (BlockContext ec)
1143 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1144 ec.CurrentBranching.CurrentUsageVector.Goto ();
1145 return true;
1148 protected override void DoEmit (EmitContext ec)
1150 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1153 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1157 protected override void CloneTo (CloneContext clonectx, Statement t)
1159 // nothing needed.
1163 public interface ILocalVariable
1165 void Emit (EmitContext ec);
1166 void EmitAssign (EmitContext ec);
1167 void EmitAddressOf (EmitContext ec);
1170 public interface IKnownVariable {
1171 Block Block { get; }
1172 Location Location { get; }
1176 // The information about a user-perceived local variable
1178 public class LocalInfo : IKnownVariable, ILocalVariable {
1179 public readonly FullNamedExpression Type;
1181 public Type VariableType;
1182 public readonly string Name;
1183 public readonly Location Location;
1184 public readonly Block Block;
1186 public VariableInfo VariableInfo;
1187 public HoistedVariable HoistedVariableReference;
1189 [Flags]
1190 enum Flags : byte {
1191 Used = 1,
1192 ReadOnly = 2,
1193 Pinned = 4,
1194 IsThis = 8,
1195 AddressTaken = 32,
1196 CompilerGenerated = 64,
1197 IsConstant = 128
1200 public enum ReadOnlyContext: byte {
1201 Using,
1202 Foreach,
1203 Fixed
1206 Flags flags;
1207 ReadOnlyContext ro_context;
1208 LocalBuilder builder;
1210 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1212 Type = type;
1213 Name = name;
1214 Block = block;
1215 Location = l;
1218 public LocalInfo (DeclSpace ds, Block block, Location l)
1220 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1221 Block = block;
1222 Location = l;
1225 public void ResolveVariable (EmitContext ec)
1227 if (HoistedVariableReference != null)
1228 return;
1230 if (builder == null) {
1231 if (Pinned)
1233 // This is needed to compile on both .NET 1.x and .NET 2.x
1234 // the later introduced `DeclareLocal (Type t, bool pinned)'
1236 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1237 else
1238 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1242 public void Emit (EmitContext ec)
1244 ec.ig.Emit (OpCodes.Ldloc, builder);
1247 public void EmitAssign (EmitContext ec)
1249 ec.ig.Emit (OpCodes.Stloc, builder);
1252 public void EmitAddressOf (EmitContext ec)
1254 ec.ig.Emit (OpCodes.Ldloca, builder);
1257 public void EmitSymbolInfo (EmitContext ec)
1259 if (builder != null)
1260 ec.DefineLocalVariable (Name, builder);
1263 public bool IsThisAssigned (BlockContext ec, Block block)
1265 if (VariableInfo == null)
1266 throw new Exception ();
1268 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1269 return true;
1271 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1274 public bool IsAssigned (BlockContext ec)
1276 if (VariableInfo == null)
1277 throw new Exception ();
1279 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1282 public bool Resolve (ResolveContext ec)
1284 if (VariableType != null)
1285 return true;
1287 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1288 if (texpr == null)
1289 return false;
1291 VariableType = texpr.Type;
1293 if (TypeManager.IsGenericParameter (VariableType))
1294 return true;
1296 if (VariableType.IsAbstract && VariableType.IsSealed) {
1297 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1298 return false;
1301 if (VariableType.IsPointer && !ec.IsUnsafe)
1302 Expression.UnsafeError (ec, Location);
1304 return true;
1307 public bool IsConstant {
1308 get { return (flags & Flags.IsConstant) != 0; }
1309 set { flags |= Flags.IsConstant; }
1312 public bool AddressTaken {
1313 get { return (flags & Flags.AddressTaken) != 0; }
1314 set { flags |= Flags.AddressTaken; }
1317 public bool CompilerGenerated {
1318 get { return (flags & Flags.CompilerGenerated) != 0; }
1319 set { flags |= Flags.CompilerGenerated; }
1322 public override string ToString ()
1324 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1325 Name, Type, VariableInfo, Location);
1328 public bool Used {
1329 get { return (flags & Flags.Used) != 0; }
1330 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1333 public bool ReadOnly {
1334 get { return (flags & Flags.ReadOnly) != 0; }
1337 public void SetReadOnlyContext (ReadOnlyContext context)
1339 flags |= Flags.ReadOnly;
1340 ro_context = context;
1343 public string GetReadOnlyContext ()
1345 if (!ReadOnly)
1346 throw new InternalErrorException ("Variable is not readonly");
1348 switch (ro_context) {
1349 case ReadOnlyContext.Fixed:
1350 return "fixed variable";
1351 case ReadOnlyContext.Foreach:
1352 return "foreach iteration variable";
1353 case ReadOnlyContext.Using:
1354 return "using variable";
1356 throw new NotImplementedException ();
1360 // Whether the variable is pinned, if Pinned the variable has been
1361 // allocated in a pinned slot with DeclareLocal.
1363 public bool Pinned {
1364 get { return (flags & Flags.Pinned) != 0; }
1365 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1368 public bool IsThis {
1369 get { return (flags & Flags.IsThis) != 0; }
1370 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1373 Block IKnownVariable.Block {
1374 get { return Block; }
1377 Location IKnownVariable.Location {
1378 get { return Location; }
1381 public LocalInfo Clone (CloneContext clonectx)
1384 // Variables in anonymous block are not resolved yet
1386 if (VariableType == null)
1387 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1390 // Variables in method block are resolved
1392 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1393 li.VariableType = VariableType;
1394 return li;
1398 /// <summary>
1399 /// Block represents a C# block.
1400 /// </summary>
1402 /// <remarks>
1403 /// This class is used in a number of places: either to represent
1404 /// explicit blocks that the programmer places or implicit blocks.
1406 /// Implicit blocks are used as labels or to introduce variable
1407 /// declarations.
1409 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1410 /// they contain extra information that is not necessary on normal blocks.
1411 /// </remarks>
1412 public class Block : Statement {
1413 public Block Parent;
1414 public Location StartLocation;
1415 public Location EndLocation = Location.Null;
1417 public ExplicitBlock Explicit;
1418 public ToplevelBlock Toplevel; // TODO: Use Explicit
1420 [Flags]
1421 public enum Flags : byte {
1422 Unchecked = 1,
1423 BlockUsed = 2,
1424 VariablesInitialized = 4,
1425 HasRet = 8,
1426 Unsafe = 16,
1427 IsIterator = 32,
1428 HasCapturedVariable = 64,
1429 HasCapturedThis = 128
1431 protected Flags flags;
1433 public bool Unchecked {
1434 get { return (flags & Flags.Unchecked) != 0; }
1435 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1438 public bool Unsafe {
1439 get { return (flags & Flags.Unsafe) != 0; }
1440 set { flags |= Flags.Unsafe; }
1444 // The statements in this block
1446 protected ArrayList statements;
1449 // An array of Blocks. We keep track of children just
1450 // to generate the local variable declarations.
1452 // Statements and child statements are handled through the
1453 // statements.
1455 ArrayList children;
1458 // Labels. (label, block) pairs.
1460 protected HybridDictionary labels;
1463 // Keeps track of (name, type) pairs
1465 IDictionary variables;
1468 // Keeps track of constants
1469 HybridDictionary constants;
1472 // Temporary variables.
1474 ArrayList temporary_variables;
1477 // If this is a switch section, the enclosing switch block.
1479 Block switch_block;
1481 protected ArrayList scope_initializers;
1483 ArrayList anonymous_children;
1485 protected static int id;
1487 int this_id;
1489 int assignable_slots;
1490 bool unreachable_shown;
1491 bool unreachable;
1493 public Block (Block parent)
1494 : this (parent, (Flags) 0, Location.Null, Location.Null)
1497 public Block (Block parent, Flags flags)
1498 : this (parent, flags, Location.Null, Location.Null)
1501 public Block (Block parent, Location start, Location end)
1502 : this (parent, (Flags) 0, start, end)
1506 // Useful when TopLevel block is downgraded to normal block
1508 public Block (ToplevelBlock parent, ToplevelBlock source)
1509 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1511 statements = source.statements;
1512 children = source.children;
1513 labels = source.labels;
1514 variables = source.variables;
1515 constants = source.constants;
1516 switch_block = source.switch_block;
1519 public Block (Block parent, Flags flags, Location start, Location end)
1521 if (parent != null) {
1522 parent.AddChild (this);
1524 // the appropriate constructors will fixup these fields
1525 Toplevel = parent.Toplevel;
1526 Explicit = parent.Explicit;
1529 this.Parent = parent;
1530 this.flags = flags;
1531 this.StartLocation = start;
1532 this.EndLocation = end;
1533 this.loc = start;
1534 this_id = id++;
1535 statements = new ArrayList (4);
1538 public Block CreateSwitchBlock (Location start)
1540 // FIXME: should this be implicit?
1541 Block new_block = new ExplicitBlock (this, start, start);
1542 new_block.switch_block = this;
1543 return new_block;
1546 public int ID {
1547 get { return this_id; }
1550 public IDictionary Variables {
1551 get {
1552 if (variables == null)
1553 variables = new ListDictionary ();
1554 return variables;
1558 void AddChild (Block b)
1560 if (children == null)
1561 children = new ArrayList (1);
1563 children.Add (b);
1566 public void SetEndLocation (Location loc)
1568 EndLocation = loc;
1571 protected void Error_158 (string name, Location loc)
1573 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1574 "by the same name in a contained scope", name);
1577 /// <summary>
1578 /// Adds a label to the current block.
1579 /// </summary>
1581 /// <returns>
1582 /// false if the name already exists in this block. true
1583 /// otherwise.
1584 /// </returns>
1586 public bool AddLabel (LabeledStatement target)
1588 if (switch_block != null)
1589 return switch_block.AddLabel (target);
1591 string name = target.Name;
1593 Block cur = this;
1594 while (cur != null) {
1595 LabeledStatement s = cur.DoLookupLabel (name);
1596 if (s != null) {
1597 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1598 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1599 return false;
1602 if (this == Explicit)
1603 break;
1605 cur = cur.Parent;
1608 while (cur != null) {
1609 if (cur.DoLookupLabel (name) != null) {
1610 Error_158 (name, target.loc);
1611 return false;
1614 if (children != null) {
1615 foreach (Block b in children) {
1616 LabeledStatement s = b.DoLookupLabel (name);
1617 if (s == null)
1618 continue;
1620 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1621 Error_158 (name, target.loc);
1622 return false;
1626 cur = cur.Parent;
1629 Toplevel.CheckError158 (name, target.loc);
1631 if (labels == null)
1632 labels = new HybridDictionary();
1634 labels.Add (name, target);
1635 return true;
1638 public LabeledStatement LookupLabel (string name)
1640 LabeledStatement s = DoLookupLabel (name);
1641 if (s != null)
1642 return s;
1644 if (children == null)
1645 return null;
1647 foreach (Block child in children) {
1648 if (Explicit != child.Explicit)
1649 continue;
1651 s = child.LookupLabel (name);
1652 if (s != null)
1653 return s;
1656 return null;
1659 LabeledStatement DoLookupLabel (string name)
1661 if (switch_block != null)
1662 return switch_block.LookupLabel (name);
1664 if (labels != null)
1665 if (labels.Contains (name))
1666 return ((LabeledStatement) labels [name]);
1668 return null;
1671 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1673 Block b = this;
1674 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1675 while (kvi == null) {
1676 b = b.Explicit.Parent;
1677 if (b == null)
1678 return true;
1679 kvi = b.Explicit.GetKnownVariable (name);
1682 if (kvi.Block == b)
1683 return true;
1685 // Is kvi.Block nested inside 'b'
1686 if (b.Explicit != kvi.Block.Explicit) {
1688 // If a variable by the same name it defined in a nested block of this
1689 // block, we violate the invariant meaning in a block.
1691 if (b == this) {
1692 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1693 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1694 return false;
1698 // It's ok if the definition is in a nested subblock of b, but not
1699 // nested inside this block -- a definition in a sibling block
1700 // should not affect us.
1702 return true;
1706 // Block 'b' and kvi.Block are the same textual block.
1707 // However, different variables are extant.
1709 // Check if the variable is in scope in both blocks. We use
1710 // an indirect check that depends on AddVariable doing its
1711 // part in maintaining the invariant-meaning-in-block property.
1713 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1714 return true;
1716 if (this is ToplevelBlock) {
1717 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1718 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1719 return false;
1723 // Even though we detected the error when the name is used, we
1724 // treat it as if the variable declaration was in error.
1726 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1727 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1728 return false;
1731 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1733 LocalInfo vi = GetLocalInfo (name);
1734 if (vi != null) {
1735 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1736 if (Explicit == vi.Block.Explicit) {
1737 Error_AlreadyDeclared (l, name, null);
1738 } else {
1739 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1740 "parent or current" : "parent");
1742 return false;
1745 if (block != null) {
1746 Expression e = block.GetParameterReference (name, Location.Null);
1747 if (e != null) {
1748 ParameterReference pr = e as ParameterReference;
1749 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1750 Error_AlreadyDeclared (loc, name);
1751 else
1752 Error_AlreadyDeclared (loc, name, "parent or current");
1753 return false;
1757 return true;
1760 public LocalInfo AddVariable (Expression type, string name, Location l)
1762 if (!CheckParentConflictName (Toplevel, name, l))
1763 return null;
1765 if (Toplevel.GenericMethod != null) {
1766 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1767 if (tp.Name == name) {
1768 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1769 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1770 return null;
1775 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1776 if (kvi != null) {
1777 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1778 Error_AlreadyDeclared (l, name, "child");
1779 return null;
1782 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1783 AddVariable (vi);
1785 if ((flags & Flags.VariablesInitialized) != 0)
1786 throw new InternalErrorException ("block has already been resolved");
1788 return vi;
1791 protected virtual void AddVariable (LocalInfo li)
1793 Variables.Add (li.Name, li);
1794 Explicit.AddKnownVariable (li.Name, li);
1797 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1799 if (reason == null) {
1800 Error_AlreadyDeclared (loc, var);
1801 return;
1804 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1805 "in this scope because it would give a different meaning " +
1806 "to `{0}', which is already used in a `{1}' scope " +
1807 "to denote something else", var, reason);
1810 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1812 Toplevel.Report.Error (128, loc,
1813 "A local variable named `{0}' is already defined in this scope", name);
1816 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1818 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1819 name, conflict);
1822 public bool AddConstant (Expression type, string name, Expression value, Location l)
1824 if (AddVariable (type, name, l) == null)
1825 return false;
1827 if (constants == null)
1828 constants = new HybridDictionary();
1830 constants.Add (name, value);
1832 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1833 Use ();
1834 return true;
1837 static int next_temp_id = 0;
1839 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1841 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1843 if (temporary_variables == null)
1844 temporary_variables = new ArrayList ();
1846 int id = ++next_temp_id;
1847 string name = "$s_" + id.ToString ();
1849 LocalInfo li = new LocalInfo (te, name, this, loc);
1850 li.CompilerGenerated = true;
1851 temporary_variables.Add (li);
1852 return li;
1855 public LocalInfo GetLocalInfo (string name)
1857 LocalInfo ret;
1858 for (Block b = this; b != null; b = b.Parent) {
1859 if (b.variables != null) {
1860 ret = (LocalInfo) b.variables [name];
1861 if (ret != null)
1862 return ret;
1866 return null;
1869 public Expression GetVariableType (string name)
1871 LocalInfo vi = GetLocalInfo (name);
1872 return vi == null ? null : vi.Type;
1875 public Expression GetConstantExpression (string name)
1877 for (Block b = this; b != null; b = b.Parent) {
1878 if (b.constants != null) {
1879 Expression ret = b.constants [name] as Expression;
1880 if (ret != null)
1881 return ret;
1884 return null;
1888 // It should be used by expressions which require to
1889 // register a statement during resolve process.
1891 public void AddScopeStatement (Statement s)
1893 if (scope_initializers == null)
1894 scope_initializers = new ArrayList ();
1896 scope_initializers.Add (s);
1899 public void AddStatement (Statement s)
1901 statements.Add (s);
1902 flags |= Flags.BlockUsed;
1905 public bool Used {
1906 get { return (flags & Flags.BlockUsed) != 0; }
1909 public void Use ()
1911 flags |= Flags.BlockUsed;
1914 public bool HasRet {
1915 get { return (flags & Flags.HasRet) != 0; }
1918 public int AssignableSlots {
1919 get {
1920 // TODO: Re-enable
1921 // if ((flags & Flags.VariablesInitialized) == 0)
1922 // throw new Exception ("Variables have not been initialized yet");
1923 return assignable_slots;
1927 public ArrayList AnonymousChildren {
1928 get { return anonymous_children; }
1931 public void AddAnonymousChild (ToplevelBlock b)
1933 if (anonymous_children == null)
1934 anonymous_children = new ArrayList ();
1936 anonymous_children.Add (b);
1939 void DoResolveConstants (BlockContext ec)
1941 if (constants == null)
1942 return;
1944 if (variables == null)
1945 throw new InternalErrorException ("cannot happen");
1947 foreach (DictionaryEntry de in variables) {
1948 string name = (string) de.Key;
1949 LocalInfo vi = (LocalInfo) de.Value;
1950 Type variable_type = vi.VariableType;
1952 if (variable_type == null) {
1953 if (vi.Type is VarExpr)
1954 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1956 continue;
1959 Expression cv = (Expression) constants [name];
1960 if (cv == null)
1961 continue;
1963 // Don't let 'const int Foo = Foo;' succeed.
1964 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1965 // which in turn causes the 'must be constant' error to be triggered.
1966 constants.Remove (name);
1968 if (!Const.IsConstantTypeValid (variable_type)) {
1969 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1970 continue;
1973 ec.CurrentBlock = this;
1974 Expression e;
1975 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1976 e = cv.Resolve (ec);
1978 if (e == null)
1979 continue;
1981 Constant ce = e as Constant;
1982 if (ce == null) {
1983 Const.Error_ExpressionMustBeConstant (vi.Location, name, ec.Report);
1984 continue;
1987 e = ce.ConvertImplicitly (variable_type);
1988 if (e == null) {
1989 if (TypeManager.IsReferenceType (variable_type))
1990 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name, ec.Report);
1991 else
1992 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1993 continue;
1996 constants.Add (name, e);
1997 vi.IsConstant = true;
2001 protected void ResolveMeta (BlockContext ec, int offset)
2003 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2005 // If some parent block was unsafe, we remain unsafe even if this block
2006 // isn't explicitly marked as such.
2007 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2008 flags |= Flags.VariablesInitialized;
2010 if (variables != null) {
2011 foreach (LocalInfo li in variables.Values) {
2012 if (!li.Resolve (ec))
2013 continue;
2014 li.VariableInfo = new VariableInfo (li, offset);
2015 offset += li.VariableInfo.Length;
2018 assignable_slots = offset;
2020 DoResolveConstants (ec);
2022 if (children == null)
2023 return;
2024 foreach (Block b in children)
2025 b.ResolveMeta (ec, offset);
2030 // Emits the local variable declarations for a block
2032 public virtual void EmitMeta (EmitContext ec)
2034 if (variables != null){
2035 foreach (LocalInfo vi in variables.Values)
2036 vi.ResolveVariable (ec);
2039 if (temporary_variables != null) {
2040 for (int i = 0; i < temporary_variables.Count; i++)
2041 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2044 if (children != null) {
2045 for (int i = 0; i < children.Count; i++)
2046 ((Block)children[i]).EmitMeta(ec);
2050 void UsageWarning (BlockContext ec)
2052 if (variables == null || ec.Report.WarningLevel < 3)
2053 return;
2055 foreach (DictionaryEntry de in variables) {
2056 LocalInfo vi = (LocalInfo) de.Value;
2058 if (!vi.Used) {
2059 string name = (string) de.Key;
2061 // vi.VariableInfo can be null for 'catch' variables
2062 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2063 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2064 else
2065 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2070 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2072 Statement body;
2074 // Some statements are wrapped by a Block. Since
2075 // others' internal could be changed, here I treat
2076 // them as possibly wrapped by Block equally.
2077 Block b = s as Block;
2078 if (b != null && b.statements.Count == 1)
2079 s = (Statement) b.statements [0];
2081 if (s is Lock)
2082 body = ((Lock) s).Statement;
2083 else if (s is For)
2084 body = ((For) s).Statement;
2085 else if (s is Foreach)
2086 body = ((Foreach) s).Statement;
2087 else if (s is While)
2088 body = ((While) s).Statement;
2089 else if (s is Fixed)
2090 body = ((Fixed) s).Statement;
2091 else if (s is Using)
2092 body = ((Using) s).EmbeddedStatement;
2093 else if (s is UsingTemporary)
2094 body = ((UsingTemporary) s).Statement;
2095 else
2096 return;
2098 if (body == null || body is EmptyStatement)
2099 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2102 public override bool Resolve (BlockContext ec)
2104 Block prev_block = ec.CurrentBlock;
2105 bool ok = true;
2107 int errors = ec.Report.Errors;
2109 ec.CurrentBlock = this;
2110 ec.StartFlowBranching (this);
2112 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2115 // Compiler generated scope statements
2117 if (scope_initializers != null) {
2118 foreach (Statement s in scope_initializers)
2119 s.Resolve (ec);
2123 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2124 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2125 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2126 // responsible for handling the situation.
2128 int statement_count = statements.Count;
2129 for (int ix = 0; ix < statement_count; ix++){
2130 Statement s = (Statement) statements [ix];
2131 if (s == null)
2132 continue;
2134 // Check possible empty statement (CS0642)
2135 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2136 statements [ix + 1] is ExplicitBlock)
2137 CheckPossibleMistakenEmptyStatement (ec, s);
2140 // Warn if we detect unreachable code.
2142 if (unreachable) {
2143 if (s is EmptyStatement)
2144 continue;
2146 if (!unreachable_shown && !(s is LabeledStatement)) {
2147 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2148 unreachable_shown = true;
2151 Block c_block = s as Block;
2152 if (c_block != null)
2153 c_block.unreachable = c_block.unreachable_shown = true;
2157 // Note that we're not using ResolveUnreachable() for unreachable
2158 // statements here. ResolveUnreachable() creates a temporary
2159 // flow branching and kills it afterwards. This leads to problems
2160 // if you have two unreachable statements where the first one
2161 // assigns a variable and the second one tries to access it.
2164 if (!s.Resolve (ec)) {
2165 ok = false;
2166 if (ec.IsInProbingMode)
2167 break;
2169 statements [ix] = EmptyStatement.Value;
2170 continue;
2173 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2174 statements [ix] = EmptyStatement.Value;
2176 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2177 if (unreachable && s is LabeledStatement)
2178 throw new InternalErrorException ("should not happen");
2181 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2182 ec.CurrentBranching, statement_count);
2184 while (ec.CurrentBranching is FlowBranchingLabeled)
2185 ec.EndFlowBranching ();
2187 bool flow_unreachable = ec.EndFlowBranching ();
2189 ec.CurrentBlock = prev_block;
2191 if (flow_unreachable)
2192 flags |= Flags.HasRet;
2194 // If we're a non-static `struct' constructor which doesn't have an
2195 // initializer, then we must initialize all of the struct's fields.
2196 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2197 ok = false;
2199 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2200 foreach (LabeledStatement label in labels.Values)
2201 if (!label.HasBeenReferenced)
2202 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2205 if (ok && errors == ec.Report.Errors)
2206 UsageWarning (ec);
2208 return ok;
2211 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2213 unreachable_shown = true;
2214 unreachable = true;
2216 if (warn)
2217 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2219 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2220 bool ok = Resolve (ec);
2221 ec.KillFlowBranching ();
2223 return ok;
2226 protected override void DoEmit (EmitContext ec)
2228 for (int ix = 0; ix < statements.Count; ix++){
2229 Statement s = (Statement) statements [ix];
2230 s.Emit (ec);
2234 public override void Emit (EmitContext ec)
2236 if (scope_initializers != null)
2237 EmitScopeInitializers (ec);
2239 ec.Mark (StartLocation);
2240 DoEmit (ec);
2242 if (SymbolWriter.HasSymbolWriter)
2243 EmitSymbolInfo (ec);
2246 protected void EmitScopeInitializers (EmitContext ec)
2248 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2250 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2251 foreach (Statement s in scope_initializers)
2252 s.Emit (ec);
2255 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2258 protected virtual void EmitSymbolInfo (EmitContext ec)
2260 if (variables != null) {
2261 foreach (LocalInfo vi in variables.Values) {
2262 vi.EmitSymbolInfo (ec);
2267 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2269 MutateVariables (storey);
2271 if (scope_initializers != null) {
2272 foreach (Statement s in scope_initializers)
2273 s.MutateHoistedGenericType (storey);
2276 foreach (Statement s in statements)
2277 s.MutateHoistedGenericType (storey);
2280 void MutateVariables (AnonymousMethodStorey storey)
2282 if (variables != null) {
2283 foreach (LocalInfo vi in variables.Values) {
2284 vi.VariableType = storey.MutateType (vi.VariableType);
2288 if (temporary_variables != null) {
2289 foreach (LocalInfo vi in temporary_variables)
2290 vi.VariableType = storey.MutateType (vi.VariableType);
2294 public override string ToString ()
2296 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2299 protected override void CloneTo (CloneContext clonectx, Statement t)
2301 Block target = (Block) t;
2303 clonectx.AddBlockMap (this, target);
2305 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2306 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2307 if (Parent != null)
2308 target.Parent = clonectx.RemapBlockCopy (Parent);
2310 if (variables != null){
2311 target.variables = new Hashtable ();
2313 foreach (DictionaryEntry de in variables){
2314 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2315 target.variables [de.Key] = newlocal;
2316 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2320 target.statements = new ArrayList (statements.Count);
2321 foreach (Statement s in statements)
2322 target.statements.Add (s.Clone (clonectx));
2324 if (target.children != null){
2325 target.children = new ArrayList (children.Count);
2326 foreach (Block b in children){
2327 target.children.Add (clonectx.LookupBlock (b));
2332 // TODO: labels, switch_block, constants (?), anonymous_children
2337 public class ExplicitBlock : Block {
2338 HybridDictionary known_variables;
2339 protected AnonymousMethodStorey am_storey;
2341 public ExplicitBlock (Block parent, Location start, Location end)
2342 : this (parent, (Flags) 0, start, end)
2346 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2347 : base (parent, flags, start, end)
2349 this.Explicit = this;
2352 // <summary>
2353 // Marks a variable with name @name as being used in this or a child block.
2354 // If a variable name has been used in a child block, it's illegal to
2355 // declare a variable with the same name in the current block.
2356 // </summary>
2357 internal void AddKnownVariable (string name, IKnownVariable info)
2359 if (known_variables == null)
2360 known_variables = new HybridDictionary();
2362 known_variables [name] = info;
2364 if (Parent != null)
2365 Parent.Explicit.AddKnownVariable (name, info);
2368 public AnonymousMethodStorey AnonymousMethodStorey {
2369 get { return am_storey; }
2373 // Creates anonymous method storey in current block
2375 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2378 // When referencing a variable in iterator storey from children anonymous method
2380 if (Toplevel.am_storey is IteratorStorey) {
2381 return Toplevel.am_storey;
2385 // An iterator has only 1 storey block
2387 if (ec.CurrentIterator != null)
2388 return ec.CurrentIterator.Storey;
2390 if (am_storey == null) {
2391 MemberBase mc = ec.MemberContext as MemberBase;
2392 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2395 // Creates anonymous method storey for this block
2397 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2400 return am_storey;
2403 public override void Emit (EmitContext ec)
2405 if (am_storey != null)
2406 am_storey.EmitStoreyInstantiation (ec);
2408 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2409 if (emit_debug_info)
2410 ec.BeginScope ();
2412 base.Emit (ec);
2414 if (emit_debug_info)
2415 ec.EndScope ();
2418 public override void EmitMeta (EmitContext ec)
2421 // Creates anonymous method storey
2423 if (am_storey != null) {
2424 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2426 // Creates parent storey reference when hoisted this is accessible
2428 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2429 ExplicitBlock parent = Toplevel.Parent.Explicit;
2432 // Hoisted this exists in top-level parent storey only
2434 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2435 parent = parent.Parent.Explicit;
2437 am_storey.AddParentStoreyReference (parent.am_storey);
2440 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2443 am_storey.DefineType ();
2444 am_storey.ResolveType ();
2445 am_storey.Define ();
2446 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2448 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2449 if (ref_blocks != null) {
2450 foreach (ExplicitBlock ref_block in ref_blocks) {
2451 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2452 if (b.am_storey != null) {
2453 b.am_storey.AddParentStoreyReference (am_storey);
2455 // Stop propagation inside same top block
2456 if (b.Toplevel == Toplevel)
2457 break;
2459 b = b.Toplevel;
2461 b.HasCapturedVariable = true;
2467 base.EmitMeta (ec);
2470 internal IKnownVariable GetKnownVariable (string name)
2472 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2475 public bool HasCapturedThis
2477 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2478 get { return (flags & Flags.HasCapturedThis) != 0; }
2481 public bool HasCapturedVariable
2483 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2484 get { return (flags & Flags.HasCapturedVariable) != 0; }
2487 protected override void CloneTo (CloneContext clonectx, Statement t)
2489 ExplicitBlock target = (ExplicitBlock) t;
2490 target.known_variables = null;
2491 base.CloneTo (clonectx, t);
2495 public class ToplevelParameterInfo : IKnownVariable {
2496 public readonly ToplevelBlock Block;
2497 public readonly int Index;
2498 public VariableInfo VariableInfo;
2500 Block IKnownVariable.Block {
2501 get { return Block; }
2503 public Parameter Parameter {
2504 get { return Block.Parameters [Index]; }
2507 public Type ParameterType {
2508 get { return Block.Parameters.Types [Index]; }
2511 public Location Location {
2512 get { return Parameter.Location; }
2515 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2517 this.Block = block;
2518 this.Index = idx;
2523 // A toplevel block contains extra information, the split is done
2524 // only to separate information that would otherwise bloat the more
2525 // lightweight Block.
2527 // In particular, this was introduced when the support for Anonymous
2528 // Methods was implemented.
2530 public class ToplevelBlock : ExplicitBlock
2533 // Block is converted to an expression
2535 sealed class BlockScopeExpression : Expression
2537 Expression child;
2538 readonly ToplevelBlock block;
2540 public BlockScopeExpression (Expression child, ToplevelBlock block)
2542 this.child = child;
2543 this.block = block;
2546 public override Expression CreateExpressionTree (ResolveContext ec)
2548 throw new NotSupportedException ();
2551 public override Expression DoResolve (ResolveContext ec)
2553 if (child == null)
2554 return null;
2556 child = child.Resolve (ec);
2557 if (child == null)
2558 return null;
2560 eclass = child.eclass;
2561 type = child.Type;
2562 return this;
2565 public override void Emit (EmitContext ec)
2567 block.EmitMeta (ec);
2568 block.EmitScopeInitializers (ec);
2569 child.Emit (ec);
2572 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2574 type = storey.MutateType (type);
2575 child.MutateHoistedGenericType (storey);
2576 block.MutateHoistedGenericType (storey);
2580 GenericMethod generic;
2581 protected ParametersCompiled parameters;
2582 ToplevelParameterInfo[] parameter_info;
2583 LocalInfo this_variable;
2584 bool resolved;
2585 bool unreachable;
2586 CompilerContext compiler;
2588 public HoistedVariable HoistedThisVariable;
2590 public bool Resolved {
2591 get {
2592 return resolved;
2597 // The parameters for the block.
2599 public ParametersCompiled Parameters {
2600 get { return parameters; }
2603 public Report Report {
2604 get { return compiler.Report; }
2607 public GenericMethod GenericMethod {
2608 get { return generic; }
2611 public ToplevelBlock Container {
2612 get { return Parent == null ? null : Parent.Toplevel; }
2615 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2616 this (ctx, parent, (Flags) 0, parameters, start)
2620 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2621 this (ctx, parent, parameters, start)
2623 this.generic = generic;
2626 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2627 this (ctx, null, (Flags) 0, parameters, start)
2631 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2632 this (ctx, null, flags, parameters, start)
2636 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2637 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2638 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2639 base (null, flags, start, Location.Null)
2641 this.compiler = ctx;
2642 this.Toplevel = this;
2644 this.parameters = parameters;
2645 this.Parent = parent;
2646 if (parent != null)
2647 parent.AddAnonymousChild (this);
2649 if (!this.parameters.IsEmpty)
2650 ProcessParameters ();
2653 public ToplevelBlock (CompilerContext ctx, Location loc)
2654 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2658 protected override void CloneTo (CloneContext clonectx, Statement t)
2660 ToplevelBlock target = (ToplevelBlock) t;
2661 base.CloneTo (clonectx, t);
2663 if (parameters.Count != 0)
2664 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2665 for (int i = 0; i < parameters.Count; ++i)
2666 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2669 public bool CheckError158 (string name, Location loc)
2671 if (AnonymousChildren != null) {
2672 foreach (ToplevelBlock child in AnonymousChildren) {
2673 if (!child.CheckError158 (name, loc))
2674 return false;
2678 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2679 if (!c.DoCheckError158 (name, loc))
2680 return false;
2683 return true;
2686 void ProcessParameters ()
2688 int n = parameters.Count;
2689 parameter_info = new ToplevelParameterInfo [n];
2690 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2691 for (int i = 0; i < n; ++i) {
2692 parameter_info [i] = new ToplevelParameterInfo (this, i);
2694 Parameter p = parameters [i];
2695 if (p == null)
2696 continue;
2698 string name = p.Name;
2699 if (CheckParentConflictName (top_parent, name, loc))
2700 AddKnownVariable (name, parameter_info [i]);
2703 // mark this block as "used" so that we create local declarations in a sub-block
2704 // FIXME: This appears to uncover a lot of bugs
2705 //this.Use ();
2708 bool DoCheckError158 (string name, Location loc)
2710 LabeledStatement s = LookupLabel (name);
2711 if (s != null) {
2712 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2713 Error_158 (name, loc);
2714 return false;
2717 return true;
2720 public override Expression CreateExpressionTree (ResolveContext ec)
2722 if (statements.Count == 1) {
2723 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2724 if (scope_initializers != null)
2725 expr = new BlockScopeExpression (expr, this);
2727 return expr;
2730 return base.CreateExpressionTree (ec);
2734 // Reformats this block to be top-level iterator block
2736 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2738 IsIterator = true;
2740 // Creates block with original statements
2741 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2743 source.statements = new ArrayList (1);
2744 source.AddStatement (new Return (iterator, iterator.Location));
2745 source.IsIterator = false;
2747 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2748 source.am_storey = iterator_storey;
2749 return iterator_storey;
2753 // Returns a parameter reference expression for the given name,
2754 // or null if there is no such parameter
2756 public Expression GetParameterReference (string name, Location loc)
2758 for (ToplevelBlock t = this; t != null; t = t.Container) {
2759 Expression expr = t.GetParameterReferenceExpression (name, loc);
2760 if (expr != null)
2761 return expr;
2764 return null;
2767 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2769 int idx = parameters.GetParameterIndexByName (name);
2770 return idx < 0 ?
2771 null : new ParameterReference (parameter_info [idx], loc);
2774 // <summary>
2775 // Returns the "this" instance variable of this block.
2776 // See AddThisVariable() for more information.
2777 // </summary>
2778 public LocalInfo ThisVariable {
2779 get { return this_variable; }
2782 // <summary>
2783 // This is used by non-static `struct' constructors which do not have an
2784 // initializer - in this case, the constructor must initialize all of the
2785 // struct's fields. To do this, we add a "this" variable and use the flow
2786 // analysis code to ensure that it's been fully initialized before control
2787 // leaves the constructor.
2788 // </summary>
2789 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2791 if (this_variable == null) {
2792 this_variable = new LocalInfo (ds, this, l);
2793 this_variable.Used = true;
2794 this_variable.IsThis = true;
2796 Variables.Add ("this", this_variable);
2799 return this_variable;
2802 public bool IsIterator {
2803 get { return (flags & Flags.IsIterator) != 0; }
2804 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2807 public bool IsThisAssigned (BlockContext ec)
2809 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2812 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2814 if (resolved)
2815 return true;
2817 resolved = true;
2819 try {
2820 if (!ResolveMeta (rc, ip))
2821 return false;
2823 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2824 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2826 if (!Resolve (rc))
2827 return false;
2829 unreachable = top_level.End ();
2831 } catch (Exception) {
2832 #if PRODUCTION
2833 if (rc.CurrentBlock != null) {
2834 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2835 } else {
2836 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2838 #endif
2839 throw;
2842 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2843 if (rc.CurrentAnonymousMethod == null) {
2844 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2845 return false;
2846 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2847 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2848 rc.CurrentAnonymousMethod.GetSignatureForError ());
2849 return false;
2853 return true;
2856 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2858 int errors = ec.Report.Errors;
2859 int orig_count = parameters.Count;
2861 if (ip != null)
2862 parameters = ip;
2864 // Assert: orig_count != parameter.Count => orig_count == 0
2865 if (orig_count != 0 && orig_count != parameters.Count)
2866 throw new InternalErrorException ("parameter information mismatch");
2868 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2870 for (int i = 0; i < orig_count; ++i) {
2871 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2873 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2874 continue;
2876 VariableInfo vi = new VariableInfo (ip, i, offset);
2877 parameter_info [i].VariableInfo = vi;
2878 offset += vi.Length;
2881 ResolveMeta (ec, offset);
2883 return ec.Report.Errors == errors;
2886 // <summary>
2887 // Check whether all `out' parameters have been assigned.
2888 // </summary>
2889 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2891 if (vector.IsUnreachable)
2892 return;
2894 int n = parameter_info == null ? 0 : parameter_info.Length;
2896 for (int i = 0; i < n; i++) {
2897 VariableInfo var = parameter_info [i].VariableInfo;
2899 if (var == null)
2900 continue;
2902 if (vector.IsAssigned (var, false))
2903 continue;
2905 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2906 var.Name);
2910 public override void Emit (EmitContext ec)
2912 if (Report.Errors > 0)
2913 return;
2915 #if PRODUCTION
2916 try {
2917 #endif
2918 EmitMeta (ec);
2920 if (ec.HasReturnLabel)
2921 ec.ReturnLabel = ec.ig.DefineLabel ();
2923 base.Emit (ec);
2925 ec.Mark (EndLocation);
2927 if (ec.HasReturnLabel)
2928 ec.ig.MarkLabel (ec.ReturnLabel);
2930 if (ec.return_value != null) {
2931 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2932 ec.ig.Emit (OpCodes.Ret);
2933 } else {
2935 // If `HasReturnLabel' is set, then we already emitted a
2936 // jump to the end of the method, so we must emit a `ret'
2937 // there.
2939 // Unfortunately, System.Reflection.Emit automatically emits
2940 // a leave to the end of a finally block. This is a problem
2941 // if no code is following the try/finally block since we may
2942 // jump to a point after the end of the method.
2943 // As a workaround, we're always creating a return label in
2944 // this case.
2947 if (ec.HasReturnLabel || !unreachable) {
2948 if (ec.ReturnType != TypeManager.void_type)
2949 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2950 ec.ig.Emit (OpCodes.Ret);
2954 #if PRODUCTION
2955 } catch (Exception e){
2956 Console.WriteLine ("Exception caught by the compiler while emitting:");
2957 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2959 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2960 throw;
2962 #endif
2965 public override void EmitMeta (EmitContext ec)
2967 parameters.ResolveVariable ();
2969 // Avoid declaring an IL variable for this_variable since it is not accessed
2970 // from the generated IL
2971 if (this_variable != null)
2972 Variables.Remove ("this");
2973 base.EmitMeta (ec);
2976 protected override void EmitSymbolInfo (EmitContext ec)
2978 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2979 if ((ae != null) && (ae.Storey != null))
2980 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2982 base.EmitSymbolInfo (ec);
2986 public class SwitchLabel {
2987 Expression label;
2988 object converted;
2989 Location loc;
2991 Label il_label;
2992 bool il_label_set;
2993 Label il_label_code;
2994 bool il_label_code_set;
2996 public static readonly object NullStringCase = new object ();
2999 // if expr == null, then it is the default case.
3001 public SwitchLabel (Expression expr, Location l)
3003 label = expr;
3004 loc = l;
3007 public Expression Label {
3008 get {
3009 return label;
3013 public Location Location {
3014 get { return loc; }
3017 public object Converted {
3018 get {
3019 return converted;
3023 public Label GetILLabel (EmitContext ec)
3025 if (!il_label_set){
3026 il_label = ec.ig.DefineLabel ();
3027 il_label_set = true;
3029 return il_label;
3032 public Label GetILLabelCode (EmitContext ec)
3034 if (!il_label_code_set){
3035 il_label_code = ec.ig.DefineLabel ();
3036 il_label_code_set = true;
3038 return il_label_code;
3042 // Resolves the expression, reduces it to a literal if possible
3043 // and then converts it to the requested type.
3045 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3047 Expression e = label.Resolve (ec);
3049 if (e == null)
3050 return false;
3052 Constant c = e as Constant;
3053 if (c == null){
3054 ec.Report.Error (150, loc, "A constant value is expected");
3055 return false;
3058 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3059 converted = NullStringCase;
3060 return true;
3063 if (allow_nullable && c.GetValue () == null) {
3064 converted = NullStringCase;
3065 return true;
3068 c = c.ImplicitConversionRequired (ec, required_type, loc);
3069 if (c == null)
3070 return false;
3072 converted = c.GetValue ();
3073 return true;
3076 public void Error_AlreadyOccurs (ResolveContext ec, Type switch_type, SwitchLabel collision_with)
3078 string label;
3079 if (converted == null)
3080 label = "default";
3081 else if (converted == NullStringCase)
3082 label = "null";
3083 else
3084 label = converted.ToString ();
3086 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3087 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3090 public SwitchLabel Clone (CloneContext clonectx)
3092 return new SwitchLabel (label.Clone (clonectx), loc);
3096 public class SwitchSection {
3097 // An array of SwitchLabels.
3098 public readonly ArrayList Labels;
3099 public readonly Block Block;
3101 public SwitchSection (ArrayList labels, Block block)
3103 Labels = labels;
3104 Block = block;
3107 public SwitchSection Clone (CloneContext clonectx)
3109 ArrayList cloned_labels = new ArrayList ();
3111 foreach (SwitchLabel sl in cloned_labels)
3112 cloned_labels.Add (sl.Clone (clonectx));
3114 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3118 public class Switch : Statement {
3119 public ArrayList Sections;
3120 public Expression Expr;
3122 /// <summary>
3123 /// Maps constants whose type type SwitchType to their SwitchLabels.
3124 /// </summary>
3125 public IDictionary Elements;
3127 /// <summary>
3128 /// The governing switch type
3129 /// </summary>
3130 public Type SwitchType;
3133 // Computed
3135 Label default_target;
3136 Label null_target;
3137 Expression new_expr;
3138 bool is_constant;
3139 bool has_null_case;
3140 SwitchSection constant_section;
3141 SwitchSection default_section;
3143 ExpressionStatement string_dictionary;
3144 FieldExpr switch_cache_field;
3145 static int unique_counter;
3148 // Nullable Types support
3150 Nullable.Unwrap unwrap;
3152 protected bool HaveUnwrap {
3153 get { return unwrap != null; }
3157 // The types allowed to be implicitly cast from
3158 // on the governing type
3160 static Type [] allowed_types;
3162 public Switch (Expression e, ArrayList sects, Location l)
3164 Expr = e;
3165 Sections = sects;
3166 loc = l;
3169 public bool GotDefault {
3170 get {
3171 return default_section != null;
3175 public Label DefaultTarget {
3176 get {
3177 return default_target;
3182 // Determines the governing type for a switch. The returned
3183 // expression might be the expression from the switch, or an
3184 // expression that includes any potential conversions to the
3185 // integral types or to string.
3187 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3189 Type t = expr.Type;
3191 if (t == TypeManager.byte_type ||
3192 t == TypeManager.sbyte_type ||
3193 t == TypeManager.ushort_type ||
3194 t == TypeManager.short_type ||
3195 t == TypeManager.uint32_type ||
3196 t == TypeManager.int32_type ||
3197 t == TypeManager.uint64_type ||
3198 t == TypeManager.int64_type ||
3199 t == TypeManager.char_type ||
3200 t == TypeManager.string_type ||
3201 t == TypeManager.bool_type ||
3202 TypeManager.IsEnumType (t))
3203 return expr;
3205 if (allowed_types == null){
3206 allowed_types = new Type [] {
3207 TypeManager.sbyte_type,
3208 TypeManager.byte_type,
3209 TypeManager.short_type,
3210 TypeManager.ushort_type,
3211 TypeManager.int32_type,
3212 TypeManager.uint32_type,
3213 TypeManager.int64_type,
3214 TypeManager.uint64_type,
3215 TypeManager.char_type,
3216 TypeManager.string_type
3221 // Try to find a *user* defined implicit conversion.
3223 // If there is no implicit conversion, or if there are multiple
3224 // conversions, we have to report an error
3226 Expression converted = null;
3227 foreach (Type tt in allowed_types){
3228 Expression e;
3230 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3231 if (e == null)
3232 continue;
3235 // Ignore over-worked ImplicitUserConversions that do
3236 // an implicit conversion in addition to the user conversion.
3238 if (!(e is UserCast))
3239 continue;
3241 if (converted != null){
3242 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3243 return null;
3246 converted = e;
3248 return converted;
3252 // Performs the basic sanity checks on the switch statement
3253 // (looks for duplicate keys and non-constant expressions).
3255 // It also returns a hashtable with the keys that we will later
3256 // use to compute the switch tables
3258 bool CheckSwitch (ResolveContext ec)
3260 bool error = false;
3261 Elements = Sections.Count > 10 ?
3262 (IDictionary)new Hashtable () :
3263 (IDictionary)new ListDictionary ();
3265 foreach (SwitchSection ss in Sections){
3266 foreach (SwitchLabel sl in ss.Labels){
3267 if (sl.Label == null){
3268 if (default_section != null){
3269 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3270 error = true;
3272 default_section = ss;
3273 continue;
3276 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3277 error = true;
3278 continue;
3281 object key = sl.Converted;
3282 if (key == SwitchLabel.NullStringCase)
3283 has_null_case = true;
3285 try {
3286 Elements.Add (key, sl);
3287 } catch (ArgumentException) {
3288 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)Elements [key]);
3289 error = true;
3293 return !error;
3296 void EmitObjectInteger (ILGenerator ig, object k)
3298 if (k is int)
3299 IntConstant.EmitInt (ig, (int) k);
3300 else if (k is Constant) {
3301 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3303 else if (k is uint)
3304 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3305 else if (k is long)
3307 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3309 IntConstant.EmitInt (ig, (int) (long) k);
3310 ig.Emit (OpCodes.Conv_I8);
3312 else
3313 LongConstant.EmitLong (ig, (long) k);
3315 else if (k is ulong)
3317 ulong ul = (ulong) k;
3318 if (ul < (1L<<32))
3320 IntConstant.EmitInt (ig, unchecked ((int) ul));
3321 ig.Emit (OpCodes.Conv_U8);
3323 else
3325 LongConstant.EmitLong (ig, unchecked ((long) ul));
3328 else if (k is char)
3329 IntConstant.EmitInt (ig, (int) ((char) k));
3330 else if (k is sbyte)
3331 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3332 else if (k is byte)
3333 IntConstant.EmitInt (ig, (int) ((byte) k));
3334 else if (k is short)
3335 IntConstant.EmitInt (ig, (int) ((short) k));
3336 else if (k is ushort)
3337 IntConstant.EmitInt (ig, (int) ((ushort) k));
3338 else if (k is bool)
3339 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3340 else
3341 throw new Exception ("Unhandled case");
3344 // structure used to hold blocks of keys while calculating table switch
3345 class KeyBlock : IComparable
3347 public KeyBlock (long _first)
3349 first = last = _first;
3351 public long first;
3352 public long last;
3353 public ArrayList element_keys = null;
3354 // how many items are in the bucket
3355 public int Size = 1;
3356 public int Length
3358 get { return (int) (last - first + 1); }
3360 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3362 return kb_last.last - kb_first.first + 1;
3364 public int CompareTo (object obj)
3366 KeyBlock kb = (KeyBlock) obj;
3367 int nLength = Length;
3368 int nLengthOther = kb.Length;
3369 if (nLengthOther == nLength)
3370 return (int) (kb.first - first);
3371 return nLength - nLengthOther;
3375 /// <summary>
3376 /// This method emits code for a lookup-based switch statement (non-string)
3377 /// Basically it groups the cases into blocks that are at least half full,
3378 /// and then spits out individual lookup opcodes for each block.
3379 /// It emits the longest blocks first, and short blocks are just
3380 /// handled with direct compares.
3381 /// </summary>
3382 /// <param name="ec"></param>
3383 /// <param name="val"></param>
3384 /// <returns></returns>
3385 void TableSwitchEmit (EmitContext ec, Expression val)
3387 int element_count = Elements.Count;
3388 object [] element_keys = new object [element_count];
3389 Elements.Keys.CopyTo (element_keys, 0);
3390 Array.Sort (element_keys);
3392 // initialize the block list with one element per key
3393 ArrayList key_blocks = new ArrayList (element_count);
3394 foreach (object key in element_keys)
3395 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3397 KeyBlock current_kb;
3398 // iteratively merge the blocks while they are at least half full
3399 // there's probably a really cool way to do this with a tree...
3400 while (key_blocks.Count > 1)
3402 ArrayList key_blocks_new = new ArrayList ();
3403 current_kb = (KeyBlock) key_blocks [0];
3404 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3406 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3407 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3409 // merge blocks
3410 current_kb.last = kb.last;
3411 current_kb.Size += kb.Size;
3413 else
3415 // start a new block
3416 key_blocks_new.Add (current_kb);
3417 current_kb = kb;
3420 key_blocks_new.Add (current_kb);
3421 if (key_blocks.Count == key_blocks_new.Count)
3422 break;
3423 key_blocks = key_blocks_new;
3426 // initialize the key lists
3427 foreach (KeyBlock kb in key_blocks)
3428 kb.element_keys = new ArrayList ();
3430 // fill the key lists
3431 int iBlockCurr = 0;
3432 if (key_blocks.Count > 0) {
3433 current_kb = (KeyBlock) key_blocks [0];
3434 foreach (object key in element_keys)
3436 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3437 System.Convert.ToInt64 (key) > current_kb.last;
3438 if (next_block)
3439 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3440 current_kb.element_keys.Add (key);
3444 // sort the blocks so we can tackle the largest ones first
3445 key_blocks.Sort ();
3447 // okay now we can start...
3448 ILGenerator ig = ec.ig;
3449 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3450 Label lbl_default = default_target;
3452 Type type_keys = null;
3453 if (element_keys.Length > 0)
3454 type_keys = element_keys [0].GetType (); // used for conversions
3456 Type compare_type;
3458 if (TypeManager.IsEnumType (SwitchType))
3459 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3460 else
3461 compare_type = SwitchType;
3463 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3465 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3466 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3467 if (kb.Length <= 2)
3469 foreach (object key in kb.element_keys) {
3470 SwitchLabel sl = (SwitchLabel) Elements [key];
3471 if (key is int && (int) key == 0) {
3472 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3473 } else {
3474 val.Emit (ec);
3475 EmitObjectInteger (ig, key);
3476 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3480 else
3482 // TODO: if all the keys in the block are the same and there are
3483 // no gaps/defaults then just use a range-check.
3484 if (compare_type == TypeManager.int64_type ||
3485 compare_type == TypeManager.uint64_type)
3487 // TODO: optimize constant/I4 cases
3489 // check block range (could be > 2^31)
3490 val.Emit (ec);
3491 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3492 ig.Emit (OpCodes.Blt, lbl_default);
3493 val.Emit (ec);
3494 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3495 ig.Emit (OpCodes.Bgt, lbl_default);
3497 // normalize range
3498 val.Emit (ec);
3499 if (kb.first != 0)
3501 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3502 ig.Emit (OpCodes.Sub);
3504 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3506 else
3508 // normalize range
3509 val.Emit (ec);
3510 int first = (int) kb.first;
3511 if (first > 0)
3513 IntConstant.EmitInt (ig, first);
3514 ig.Emit (OpCodes.Sub);
3516 else if (first < 0)
3518 IntConstant.EmitInt (ig, -first);
3519 ig.Emit (OpCodes.Add);
3523 // first, build the list of labels for the switch
3524 int iKey = 0;
3525 int cJumps = kb.Length;
3526 Label [] switch_labels = new Label [cJumps];
3527 for (int iJump = 0; iJump < cJumps; iJump++)
3529 object key = kb.element_keys [iKey];
3530 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3532 SwitchLabel sl = (SwitchLabel) Elements [key];
3533 switch_labels [iJump] = sl.GetILLabel (ec);
3534 iKey++;
3536 else
3537 switch_labels [iJump] = lbl_default;
3539 // emit the switch opcode
3540 ig.Emit (OpCodes.Switch, switch_labels);
3543 // mark the default for this block
3544 if (iBlock != 0)
3545 ig.MarkLabel (lbl_default);
3548 // TODO: find the default case and emit it here,
3549 // to prevent having to do the following jump.
3550 // make sure to mark other labels in the default section
3552 // the last default just goes to the end
3553 if (element_keys.Length > 0)
3554 ig.Emit (OpCodes.Br, lbl_default);
3556 // now emit the code for the sections
3557 bool found_default = false;
3559 foreach (SwitchSection ss in Sections) {
3560 foreach (SwitchLabel sl in ss.Labels) {
3561 if (sl.Converted == SwitchLabel.NullStringCase) {
3562 ig.MarkLabel (null_target);
3563 } else if (sl.Label == null) {
3564 ig.MarkLabel (lbl_default);
3565 found_default = true;
3566 if (!has_null_case)
3567 ig.MarkLabel (null_target);
3569 ig.MarkLabel (sl.GetILLabel (ec));
3570 ig.MarkLabel (sl.GetILLabelCode (ec));
3572 ss.Block.Emit (ec);
3575 if (!found_default) {
3576 ig.MarkLabel (lbl_default);
3577 if (!has_null_case) {
3578 ig.MarkLabel (null_target);
3582 ig.MarkLabel (lbl_end);
3585 SwitchSection FindSection (SwitchLabel label)
3587 foreach (SwitchSection ss in Sections){
3588 foreach (SwitchLabel sl in ss.Labels){
3589 if (label == sl)
3590 return ss;
3594 return null;
3597 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3599 foreach (SwitchSection ss in Sections)
3600 ss.Block.MutateHoistedGenericType (storey);
3603 public static void Reset ()
3605 unique_counter = 0;
3606 allowed_types = null;
3609 public override bool Resolve (BlockContext ec)
3611 Expr = Expr.Resolve (ec);
3612 if (Expr == null)
3613 return false;
3615 new_expr = SwitchGoverningType (ec, Expr);
3617 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3618 unwrap = Nullable.Unwrap.Create (Expr, false);
3619 if (unwrap == null)
3620 return false;
3622 new_expr = SwitchGoverningType (ec, unwrap);
3625 if (new_expr == null){
3626 ec.Report.Error (151, loc,
3627 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3628 TypeManager.CSharpName (Expr.Type));
3629 return false;
3632 // Validate switch.
3633 SwitchType = new_expr.Type;
3635 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3636 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3637 return false;
3640 if (!CheckSwitch (ec))
3641 return false;
3643 if (HaveUnwrap)
3644 Elements.Remove (SwitchLabel.NullStringCase);
3646 Switch old_switch = ec.Switch;
3647 ec.Switch = this;
3648 ec.Switch.SwitchType = SwitchType;
3650 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3651 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3653 is_constant = new_expr is Constant;
3654 if (is_constant) {
3655 object key = ((Constant) new_expr).GetValue ();
3656 SwitchLabel label = (SwitchLabel) Elements [key];
3658 constant_section = FindSection (label);
3659 if (constant_section == null)
3660 constant_section = default_section;
3663 bool first = true;
3664 bool ok = true;
3665 foreach (SwitchSection ss in Sections){
3666 if (!first)
3667 ec.CurrentBranching.CreateSibling (
3668 null, FlowBranching.SiblingType.SwitchSection);
3669 else
3670 first = false;
3672 if (is_constant && (ss != constant_section)) {
3673 // If we're a constant switch, we're only emitting
3674 // one single section - mark all the others as
3675 // unreachable.
3676 ec.CurrentBranching.CurrentUsageVector.Goto ();
3677 if (!ss.Block.ResolveUnreachable (ec, true)) {
3678 ok = false;
3680 } else {
3681 if (!ss.Block.Resolve (ec))
3682 ok = false;
3686 if (default_section == null)
3687 ec.CurrentBranching.CreateSibling (
3688 null, FlowBranching.SiblingType.SwitchSection);
3690 ec.EndFlowBranching ();
3691 ec.Switch = old_switch;
3693 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3695 if (!ok)
3696 return false;
3698 if (SwitchType == TypeManager.string_type && !is_constant) {
3699 // TODO: Optimize single case, and single+default case
3700 ResolveStringSwitchMap (ec);
3703 return true;
3706 void ResolveStringSwitchMap (ResolveContext ec)
3708 FullNamedExpression string_dictionary_type;
3709 if (TypeManager.generic_ienumerable_type != null) {
3710 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3711 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3713 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3714 new TypeArguments (
3715 new TypeExpression (TypeManager.string_type, loc),
3716 new TypeExpression (TypeManager.int32_type, loc)), loc);
3717 } else {
3718 MemberAccess system_collections_generic = new MemberAccess (
3719 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3721 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3724 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3725 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3726 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3727 if (!field.Define ())
3728 return;
3729 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3731 ArrayList init = new ArrayList ();
3732 int counter = 0;
3733 Elements.Clear ();
3734 string value = null;
3735 foreach (SwitchSection section in Sections) {
3736 int last_count = init.Count;
3737 foreach (SwitchLabel sl in section.Labels) {
3738 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3739 continue;
3741 value = (string) sl.Converted;
3742 ArrayList init_args = new ArrayList (2);
3743 init_args.Add (new StringLiteral (value, sl.Location));
3744 init_args.Add (new IntConstant (counter, loc));
3745 init.Add (new CollectionElementInitializer (init_args, loc));
3749 // Don't add empty sections
3751 if (last_count == init.Count)
3752 continue;
3754 Elements.Add (counter, section.Labels [0]);
3755 ++counter;
3758 Arguments args = new Arguments (1);
3759 args.Add (new Argument (new IntConstant (init.Count, loc)));
3760 Expression initializer = new NewInitialize (string_dictionary_type, args,
3761 new CollectionOrObjectInitializers (init, loc), loc);
3763 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3764 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3767 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3769 ILGenerator ig = ec.ig;
3770 Label l_initialized = ig.DefineLabel ();
3773 // Skip initialization when value is null
3775 value.EmitBranchable (ec, null_target, false);
3778 // Check if string dictionary is initialized and initialize
3780 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3781 string_dictionary.EmitStatement (ec);
3782 ig.MarkLabel (l_initialized);
3784 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3786 ResolveContext rc = new ResolveContext (ec.MemberContext);
3788 if (TypeManager.generic_ienumerable_type != null) {
3789 Arguments get_value_args = new Arguments (2);
3790 get_value_args.Add (new Argument (value));
3791 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3792 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3793 if (get_item == null)
3794 return;
3797 // A value was not found, go to default case
3799 get_item.EmitBranchable (ec, default_target, false);
3800 } else {
3801 Arguments get_value_args = new Arguments (1);
3802 get_value_args.Add (new Argument (value));
3804 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3805 if (get_item == null)
3806 return;
3808 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3809 get_item_object.EmitAssign (ec, get_item, true, false);
3810 ec.ig.Emit (OpCodes.Brfalse, default_target);
3812 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3813 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3815 get_item_int.EmitStatement (ec);
3816 get_item_object.Release (ec);
3819 TableSwitchEmit (ec, string_switch_variable);
3820 string_switch_variable.Release (ec);
3823 protected override void DoEmit (EmitContext ec)
3825 ILGenerator ig = ec.ig;
3827 default_target = ig.DefineLabel ();
3828 null_target = ig.DefineLabel ();
3830 // Store variable for comparission purposes
3831 // TODO: Don't duplicate non-captured VariableReference
3832 LocalTemporary value;
3833 if (HaveUnwrap) {
3834 value = new LocalTemporary (SwitchType);
3835 unwrap.EmitCheck (ec);
3836 ig.Emit (OpCodes.Brfalse, null_target);
3837 new_expr.Emit (ec);
3838 value.Store (ec);
3839 } else if (!is_constant) {
3840 value = new LocalTemporary (SwitchType);
3841 new_expr.Emit (ec);
3842 value.Store (ec);
3843 } else
3844 value = null;
3847 // Setup the codegen context
3849 Label old_end = ec.LoopEnd;
3850 Switch old_switch = ec.Switch;
3852 ec.LoopEnd = ig.DefineLabel ();
3853 ec.Switch = this;
3855 // Emit Code.
3856 if (is_constant) {
3857 if (constant_section != null)
3858 constant_section.Block.Emit (ec);
3859 } else if (string_dictionary != null) {
3860 DoEmitStringSwitch (value, ec);
3861 } else {
3862 TableSwitchEmit (ec, value);
3865 if (value != null)
3866 value.Release (ec);
3868 // Restore context state.
3869 ig.MarkLabel (ec.LoopEnd);
3872 // Restore the previous context
3874 ec.LoopEnd = old_end;
3875 ec.Switch = old_switch;
3878 protected override void CloneTo (CloneContext clonectx, Statement t)
3880 Switch target = (Switch) t;
3882 target.Expr = Expr.Clone (clonectx);
3883 target.Sections = new ArrayList ();
3884 foreach (SwitchSection ss in Sections){
3885 target.Sections.Add (ss.Clone (clonectx));
3890 // A place where execution can restart in an iterator
3891 public abstract class ResumableStatement : Statement
3893 bool prepared;
3894 protected Label resume_point;
3896 public Label PrepareForEmit (EmitContext ec)
3898 if (!prepared) {
3899 prepared = true;
3900 resume_point = ec.ig.DefineLabel ();
3902 return resume_point;
3905 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3907 return end;
3909 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3914 // Base class for statements that are implemented in terms of try...finally
3915 public abstract class ExceptionStatement : ResumableStatement
3917 bool code_follows;
3918 Iterator iter;
3920 protected abstract void EmitPreTryBody (EmitContext ec);
3921 protected abstract void EmitTryBody (EmitContext ec);
3922 protected abstract void EmitFinallyBody (EmitContext ec);
3924 protected sealed override void DoEmit (EmitContext ec)
3926 ILGenerator ig = ec.ig;
3928 EmitPreTryBody (ec);
3930 if (resume_points != null) {
3931 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3932 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3935 ig.BeginExceptionBlock ();
3937 if (resume_points != null) {
3938 ig.MarkLabel (resume_point);
3940 // For normal control flow, we want to fall-through the Switch
3941 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3942 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3943 IntConstant.EmitInt (ig, first_resume_pc);
3944 ig.Emit (OpCodes.Sub);
3946 Label [] labels = new Label [resume_points.Count];
3947 for (int i = 0; i < resume_points.Count; ++i)
3948 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3949 ig.Emit (OpCodes.Switch, labels);
3952 EmitTryBody (ec);
3954 ig.BeginFinallyBlock ();
3956 Label start_finally = ec.ig.DefineLabel ();
3957 if (resume_points != null) {
3958 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3959 ig.Emit (OpCodes.Brfalse_S, start_finally);
3960 ig.Emit (OpCodes.Endfinally);
3963 ig.MarkLabel (start_finally);
3964 EmitFinallyBody (ec);
3966 ig.EndExceptionBlock ();
3969 public void SomeCodeFollows ()
3971 code_follows = true;
3974 public override bool Resolve (BlockContext ec)
3976 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3977 // So, ensure there's some IL code after this statement.
3978 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3979 ec.NeedReturnLabel ();
3981 iter = ec.CurrentIterator;
3982 return true;
3985 ArrayList resume_points;
3986 int first_resume_pc;
3987 public void AddResumePoint (ResumableStatement stmt, int pc)
3989 if (resume_points == null) {
3990 resume_points = new ArrayList ();
3991 first_resume_pc = pc;
3994 if (pc != first_resume_pc + resume_points.Count)
3995 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3997 resume_points.Add (stmt);
4000 Label dispose_try_block;
4001 bool prepared_for_dispose, emitted_dispose;
4002 public override Label PrepareForDispose (EmitContext ec, Label end)
4004 if (!prepared_for_dispose) {
4005 prepared_for_dispose = true;
4006 dispose_try_block = ec.ig.DefineLabel ();
4008 return dispose_try_block;
4011 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4013 if (emitted_dispose)
4014 return;
4016 emitted_dispose = true;
4018 ILGenerator ig = ec.ig;
4020 Label end_of_try = ig.DefineLabel ();
4022 // Ensure that the only way we can get into this code is through a dispatcher
4023 if (have_dispatcher)
4024 ig.Emit (OpCodes.Br, end);
4026 ig.BeginExceptionBlock ();
4028 ig.MarkLabel (dispose_try_block);
4030 Label [] labels = null;
4031 for (int i = 0; i < resume_points.Count; ++i) {
4032 ResumableStatement s = (ResumableStatement) resume_points [i];
4033 Label ret = s.PrepareForDispose (ec, end_of_try);
4034 if (ret.Equals (end_of_try) && labels == null)
4035 continue;
4036 if (labels == null) {
4037 labels = new Label [resume_points.Count];
4038 for (int j = 0; j < i; ++j)
4039 labels [j] = end_of_try;
4041 labels [i] = ret;
4044 if (labels != null) {
4045 int j;
4046 for (j = 1; j < labels.Length; ++j)
4047 if (!labels [0].Equals (labels [j]))
4048 break;
4049 bool emit_dispatcher = j < labels.Length;
4051 if (emit_dispatcher) {
4052 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4053 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4054 IntConstant.EmitInt (ig, first_resume_pc);
4055 ig.Emit (OpCodes.Sub);
4056 ig.Emit (OpCodes.Switch, labels);
4057 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4060 foreach (ResumableStatement s in resume_points)
4061 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4064 ig.MarkLabel (end_of_try);
4066 ig.BeginFinallyBlock ();
4068 EmitFinallyBody (ec);
4070 ig.EndExceptionBlock ();
4074 public class Lock : ExceptionStatement {
4075 Expression expr;
4076 public Statement Statement;
4077 TemporaryVariable temp;
4079 public Lock (Expression expr, Statement stmt, Location l)
4081 this.expr = expr;
4082 Statement = stmt;
4083 loc = l;
4086 public override bool Resolve (BlockContext ec)
4088 expr = expr.Resolve (ec);
4089 if (expr == null)
4090 return false;
4092 if (!TypeManager.IsReferenceType (expr.Type)){
4093 ec.Report.Error (185, loc,
4094 "`{0}' is not a reference type as required by the lock statement",
4095 TypeManager.CSharpName (expr.Type));
4096 return false;
4099 ec.StartFlowBranching (this);
4100 bool ok = Statement.Resolve (ec);
4101 ec.EndFlowBranching ();
4103 ok &= base.Resolve (ec);
4105 // Avoid creating libraries that reference the internal
4106 // mcs NullType:
4107 Type t = expr.Type;
4108 if (t == TypeManager.null_type)
4109 t = TypeManager.object_type;
4111 temp = new TemporaryVariable (t, loc);
4112 temp.Resolve (ec);
4114 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4115 Type monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", Kind.Class, true);
4116 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4117 monitor_type, "Enter", loc, TypeManager.object_type);
4118 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4119 monitor_type, "Exit", loc, TypeManager.object_type);
4122 return ok;
4125 protected override void EmitPreTryBody (EmitContext ec)
4127 ILGenerator ig = ec.ig;
4129 temp.EmitAssign (ec, expr);
4130 temp.Emit (ec);
4131 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4134 protected override void EmitTryBody (EmitContext ec)
4136 Statement.Emit (ec);
4139 protected override void EmitFinallyBody (EmitContext ec)
4141 temp.Emit (ec);
4142 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4145 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4147 expr.MutateHoistedGenericType (storey);
4148 temp.MutateHoistedGenericType (storey);
4149 Statement.MutateHoistedGenericType (storey);
4152 protected override void CloneTo (CloneContext clonectx, Statement t)
4154 Lock target = (Lock) t;
4156 target.expr = expr.Clone (clonectx);
4157 target.Statement = Statement.Clone (clonectx);
4161 public class Unchecked : Statement {
4162 public Block Block;
4164 public Unchecked (Block b)
4166 Block = b;
4167 b.Unchecked = true;
4170 public override bool Resolve (BlockContext ec)
4172 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4173 return Block.Resolve (ec);
4176 protected override void DoEmit (EmitContext ec)
4178 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4179 Block.Emit (ec);
4182 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4184 Block.MutateHoistedGenericType (storey);
4187 protected override void CloneTo (CloneContext clonectx, Statement t)
4189 Unchecked target = (Unchecked) t;
4191 target.Block = clonectx.LookupBlock (Block);
4195 public class Checked : Statement {
4196 public Block Block;
4198 public Checked (Block b)
4200 Block = b;
4201 b.Unchecked = false;
4204 public override bool Resolve (BlockContext ec)
4206 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4207 return Block.Resolve (ec);
4210 protected override void DoEmit (EmitContext ec)
4212 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4213 Block.Emit (ec);
4216 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4218 Block.MutateHoistedGenericType (storey);
4221 protected override void CloneTo (CloneContext clonectx, Statement t)
4223 Checked target = (Checked) t;
4225 target.Block = clonectx.LookupBlock (Block);
4229 public class Unsafe : Statement {
4230 public Block Block;
4232 public Unsafe (Block b)
4234 Block = b;
4235 Block.Unsafe = true;
4236 loc = b.StartLocation;
4239 public override bool Resolve (BlockContext ec)
4241 if (ec.CurrentIterator != null)
4242 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4244 using (ec.Set (ResolveContext.Options.UnsafeScope))
4245 return Block.Resolve (ec);
4248 protected override void DoEmit (EmitContext ec)
4250 Block.Emit (ec);
4253 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4255 Block.MutateHoistedGenericType (storey);
4258 protected override void CloneTo (CloneContext clonectx, Statement t)
4260 Unsafe target = (Unsafe) t;
4262 target.Block = clonectx.LookupBlock (Block);
4267 // Fixed statement
4269 public class Fixed : Statement {
4270 Expression type;
4271 ArrayList declarators;
4272 Statement statement;
4273 Type expr_type;
4274 Emitter[] data;
4275 bool has_ret;
4277 abstract class Emitter
4279 protected LocalInfo vi;
4280 protected Expression converted;
4282 protected Emitter (Expression expr, LocalInfo li)
4284 converted = expr;
4285 vi = li;
4288 public abstract void Emit (EmitContext ec);
4289 public abstract void EmitExit (EmitContext ec);
4292 class ExpressionEmitter : Emitter {
4293 public ExpressionEmitter (Expression converted, LocalInfo li) :
4294 base (converted, li)
4298 public override void Emit (EmitContext ec) {
4300 // Store pointer in pinned location
4302 converted.Emit (ec);
4303 vi.EmitAssign (ec);
4306 public override void EmitExit (EmitContext ec)
4308 ec.ig.Emit (OpCodes.Ldc_I4_0);
4309 ec.ig.Emit (OpCodes.Conv_U);
4310 vi.EmitAssign (ec);
4314 class StringEmitter : Emitter
4316 LocalInfo pinned_string;
4318 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4319 base (expr, li)
4321 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4322 pinned_string.Pinned = true;
4325 public StringEmitter Resolve (ResolveContext rc)
4327 pinned_string.Resolve (rc);
4329 if (TypeManager.int_get_offset_to_string_data == null) {
4330 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4331 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4334 return this;
4337 public override void Emit (EmitContext ec)
4339 pinned_string.ResolveVariable (ec);
4341 converted.Emit (ec);
4342 pinned_string.EmitAssign (ec);
4344 // TODO: Should use Binary::Add
4345 pinned_string.Emit (ec);
4346 ec.ig.Emit (OpCodes.Conv_I);
4348 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4349 //pe.InstanceExpression = pinned_string;
4350 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4352 ec.ig.Emit (OpCodes.Add);
4353 vi.EmitAssign (ec);
4356 public override void EmitExit (EmitContext ec)
4358 ec.ig.Emit (OpCodes.Ldnull);
4359 pinned_string.EmitAssign (ec);
4363 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4365 this.type = type;
4366 declarators = decls;
4367 statement = stmt;
4368 loc = l;
4371 public Statement Statement {
4372 get { return statement; }
4375 public override bool Resolve (BlockContext ec)
4377 if (!ec.IsUnsafe){
4378 Expression.UnsafeError (ec, loc);
4379 return false;
4382 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4383 if (texpr == null) {
4384 if (type is VarExpr)
4385 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4387 return false;
4390 expr_type = texpr.Type;
4392 data = new Emitter [declarators.Count];
4394 if (!expr_type.IsPointer){
4395 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4396 return false;
4399 int i = 0;
4400 foreach (Pair p in declarators){
4401 LocalInfo vi = (LocalInfo) p.First;
4402 Expression e = (Expression) p.Second;
4404 vi.VariableInfo.SetAssigned (ec);
4405 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4408 // The rules for the possible declarators are pretty wise,
4409 // but the production on the grammar is more concise.
4411 // So we have to enforce these rules here.
4413 // We do not resolve before doing the case 1 test,
4414 // because the grammar is explicit in that the token &
4415 // is present, so we need to test for this particular case.
4418 if (e is Cast){
4419 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4420 return false;
4423 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4424 e = e.Resolve (ec);
4427 if (e == null)
4428 return false;
4431 // Case 2: Array
4433 if (e.Type.IsArray){
4434 Type array_type = TypeManager.GetElementType (e.Type);
4437 // Provided that array_type is unmanaged,
4439 if (!TypeManager.VerifyUnManaged (array_type, loc))
4440 return false;
4443 // and T* is implicitly convertible to the
4444 // pointer type given in the fixed statement.
4446 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4448 Expression converted = Convert.ImplicitConversionRequired (
4449 ec, array_ptr, vi.VariableType, loc);
4450 if (converted == null)
4451 return false;
4454 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4456 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4457 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4458 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4459 new NullPointer (loc),
4460 converted);
4462 converted = converted.Resolve (ec);
4464 data [i] = new ExpressionEmitter (converted, vi);
4465 i++;
4467 continue;
4471 // Case 3: string
4473 if (e.Type == TypeManager.string_type){
4474 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4475 i++;
4476 continue;
4479 // Case 4: fixed buffer
4480 if (e is FixedBufferPtr) {
4481 data [i++] = new ExpressionEmitter (e, vi);
4482 continue;
4486 // Case 1: & object.
4488 Unary u = e as Unary;
4489 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4490 IVariableReference vr = u.Expr as IVariableReference;
4491 if (vr == null || !vr.IsFixed) {
4492 data [i] = new ExpressionEmitter (e, vi);
4496 if (data [i++] == null)
4497 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4499 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4502 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4503 bool ok = statement.Resolve (ec);
4504 bool flow_unreachable = ec.EndFlowBranching ();
4505 has_ret = flow_unreachable;
4507 return ok;
4510 protected override void DoEmit (EmitContext ec)
4512 for (int i = 0; i < data.Length; i++) {
4513 data [i].Emit (ec);
4516 statement.Emit (ec);
4518 if (has_ret)
4519 return;
4522 // Clear the pinned variable
4524 for (int i = 0; i < data.Length; i++) {
4525 data [i].EmitExit (ec);
4529 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4531 // Fixed statement cannot be used inside anonymous methods or lambdas
4532 throw new NotSupportedException ();
4535 protected override void CloneTo (CloneContext clonectx, Statement t)
4537 Fixed target = (Fixed) t;
4539 target.type = type.Clone (clonectx);
4540 target.declarators = new ArrayList (declarators.Count);
4541 foreach (Pair p in declarators) {
4542 LocalInfo vi = (LocalInfo) p.First;
4543 Expression e = (Expression) p.Second;
4545 target.declarators.Add (
4546 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4549 target.statement = statement.Clone (clonectx);
4553 public class Catch : Statement {
4554 public readonly string Name;
4555 public Block Block;
4556 public Block VarBlock;
4558 Expression type_expr;
4559 Type type;
4561 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4563 type_expr = type;
4564 Name = name;
4565 Block = block;
4566 VarBlock = var_block;
4567 loc = l;
4570 public Type CatchType {
4571 get {
4572 return type;
4576 public bool IsGeneral {
4577 get {
4578 return type_expr == null;
4582 protected override void DoEmit (EmitContext ec)
4584 ILGenerator ig = ec.ig;
4586 if (CatchType != null)
4587 ig.BeginCatchBlock (CatchType);
4588 else
4589 ig.BeginCatchBlock (TypeManager.object_type);
4591 if (VarBlock != null)
4592 VarBlock.Emit (ec);
4594 if (Name != null) {
4595 // TODO: Move to resolve
4596 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4597 lvr.Resolve (new ResolveContext (ec.MemberContext));
4599 #if GMCS_SOURCE
4600 // Only to make verifier happy
4601 if (TypeManager.IsGenericParameter (lvr.Type))
4602 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4603 #endif
4605 Expression source;
4606 if (lvr.IsHoisted) {
4607 LocalTemporary lt = new LocalTemporary (lvr.Type);
4608 lt.Store (ec);
4609 source = lt;
4610 } else {
4611 // Variable is at the top of the stack
4612 source = EmptyExpression.Null;
4615 lvr.EmitAssign (ec, source, false, false);
4616 } else
4617 ig.Emit (OpCodes.Pop);
4619 Block.Emit (ec);
4622 public override bool Resolve (BlockContext ec)
4624 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4625 if (type_expr != null) {
4626 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4627 if (te == null)
4628 return false;
4630 type = te.Type;
4632 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4633 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4634 return false;
4636 } else
4637 type = null;
4639 if (!Block.Resolve (ec))
4640 return false;
4642 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4643 // emit the "unused variable" warnings.
4644 if (VarBlock != null)
4645 return VarBlock.Resolve (ec);
4647 return true;
4651 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4653 if (type != null)
4654 type = storey.MutateType (type);
4655 if (VarBlock != null)
4656 VarBlock.MutateHoistedGenericType (storey);
4657 Block.MutateHoistedGenericType (storey);
4660 protected override void CloneTo (CloneContext clonectx, Statement t)
4662 Catch target = (Catch) t;
4664 if (type_expr != null)
4665 target.type_expr = type_expr.Clone (clonectx);
4666 if (VarBlock != null)
4667 target.VarBlock = clonectx.LookupBlock (VarBlock);
4668 target.Block = clonectx.LookupBlock (Block);
4672 public class TryFinally : ExceptionStatement {
4673 Statement stmt;
4674 Block fini;
4676 public TryFinally (Statement stmt, Block fini, Location l)
4678 this.stmt = stmt;
4679 this.fini = fini;
4680 loc = l;
4683 public override bool Resolve (BlockContext ec)
4685 bool ok = true;
4687 ec.StartFlowBranching (this);
4689 if (!stmt.Resolve (ec))
4690 ok = false;
4692 if (ok)
4693 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4694 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4695 if (!fini.Resolve (ec))
4696 ok = false;
4699 ec.EndFlowBranching ();
4701 ok &= base.Resolve (ec);
4703 return ok;
4706 protected override void EmitPreTryBody (EmitContext ec)
4710 protected override void EmitTryBody (EmitContext ec)
4712 stmt.Emit (ec);
4715 protected override void EmitFinallyBody (EmitContext ec)
4717 fini.Emit (ec);
4720 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4722 stmt.MutateHoistedGenericType (storey);
4723 fini.MutateHoistedGenericType (storey);
4726 protected override void CloneTo (CloneContext clonectx, Statement t)
4728 TryFinally target = (TryFinally) t;
4730 target.stmt = (Statement) stmt.Clone (clonectx);
4731 if (fini != null)
4732 target.fini = clonectx.LookupBlock (fini);
4736 public class TryCatch : Statement {
4737 public Block Block;
4738 public ArrayList Specific;
4739 public Catch General;
4740 bool inside_try_finally, code_follows;
4742 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4744 this.Block = block;
4745 this.Specific = catch_clauses;
4746 this.inside_try_finally = inside_try_finally;
4748 Catch c = (Catch) catch_clauses [0];
4749 if (c.IsGeneral) {
4750 this.General = c;
4751 catch_clauses.RemoveAt (0);
4754 loc = l;
4757 public override bool Resolve (BlockContext ec)
4759 bool ok = true;
4761 ec.StartFlowBranching (this);
4763 if (!Block.Resolve (ec))
4764 ok = false;
4766 Type[] prev_catches = new Type [Specific.Count];
4767 int last_index = 0;
4768 foreach (Catch c in Specific){
4769 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4771 if (c.Name != null) {
4772 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4773 if (vi == null)
4774 throw new Exception ();
4776 vi.VariableInfo = null;
4779 if (!c.Resolve (ec)) {
4780 ok = false;
4781 continue;
4784 Type resolved_type = c.CatchType;
4785 for (int ii = 0; ii < last_index; ++ii) {
4786 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4787 ec.Report.Error (160, c.loc,
4788 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4789 TypeManager.CSharpName (prev_catches [ii]));
4790 ok = false;
4794 prev_catches [last_index++] = resolved_type;
4797 if (General != null) {
4798 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4799 foreach (Catch c in Specific){
4800 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4801 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'");
4806 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4808 if (!General.Resolve (ec))
4809 ok = false;
4812 ec.EndFlowBranching ();
4814 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4815 // So, ensure there's some IL code after this statement
4816 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4817 ec.NeedReturnLabel ();
4819 return ok;
4822 public void SomeCodeFollows ()
4824 code_follows = true;
4827 protected override void DoEmit (EmitContext ec)
4829 ILGenerator ig = ec.ig;
4831 if (!inside_try_finally)
4832 ig.BeginExceptionBlock ();
4834 Block.Emit (ec);
4836 foreach (Catch c in Specific)
4837 c.Emit (ec);
4839 if (General != null)
4840 General.Emit (ec);
4842 if (!inside_try_finally)
4843 ig.EndExceptionBlock ();
4846 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4848 Block.MutateHoistedGenericType (storey);
4850 if (General != null)
4851 General.MutateHoistedGenericType (storey);
4852 if (Specific != null) {
4853 foreach (Catch c in Specific)
4854 c.MutateHoistedGenericType (storey);
4858 protected override void CloneTo (CloneContext clonectx, Statement t)
4860 TryCatch target = (TryCatch) t;
4862 target.Block = clonectx.LookupBlock (Block);
4863 if (General != null)
4864 target.General = (Catch) General.Clone (clonectx);
4865 if (Specific != null){
4866 target.Specific = new ArrayList ();
4867 foreach (Catch c in Specific)
4868 target.Specific.Add (c.Clone (clonectx));
4873 // FIXME: Why is it almost exact copy of Using ??
4874 public class UsingTemporary : ExceptionStatement {
4875 TemporaryVariable local_copy;
4876 public Statement Statement;
4877 Expression expr;
4878 Type expr_type;
4880 public UsingTemporary (Expression expr, Statement stmt, Location l)
4882 this.expr = expr;
4883 Statement = stmt;
4884 loc = l;
4887 public override bool Resolve (BlockContext ec)
4889 expr = expr.Resolve (ec);
4890 if (expr == null)
4891 return false;
4893 expr_type = expr.Type;
4895 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4896 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4897 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4898 return false;
4902 local_copy = new TemporaryVariable (expr_type, loc);
4903 local_copy.Resolve (ec);
4905 ec.StartFlowBranching (this);
4907 bool ok = Statement.Resolve (ec);
4909 ec.EndFlowBranching ();
4911 ok &= base.Resolve (ec);
4913 if (TypeManager.void_dispose_void == null) {
4914 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4915 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4918 return ok;
4921 protected override void EmitPreTryBody (EmitContext ec)
4923 local_copy.EmitAssign (ec, expr);
4926 protected override void EmitTryBody (EmitContext ec)
4928 Statement.Emit (ec);
4931 protected override void EmitFinallyBody (EmitContext ec)
4933 ILGenerator ig = ec.ig;
4934 if (!TypeManager.IsStruct (expr_type)) {
4935 Label skip = ig.DefineLabel ();
4936 local_copy.Emit (ec);
4937 ig.Emit (OpCodes.Brfalse, skip);
4938 local_copy.Emit (ec);
4939 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4940 ig.MarkLabel (skip);
4941 return;
4944 Expression ml = Expression.MemberLookup (RootContext.ToplevelTypes.Compiler,
4945 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4946 "Dispose", Location.Null);
4948 if (!(ml is MethodGroupExpr)) {
4949 local_copy.Emit (ec);
4950 ig.Emit (OpCodes.Box, expr_type);
4951 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4952 return;
4955 MethodInfo mi = null;
4957 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4958 if (TypeManager.GetParameterData (mk).Count == 0) {
4959 mi = mk;
4960 break;
4964 if (mi == null) {
4965 RootContext.ToplevelTypes.Compiler.Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4966 return;
4969 local_copy.AddressOf (ec, AddressOp.Load);
4970 ig.Emit (OpCodes.Call, mi);
4973 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4975 expr_type = storey.MutateType (expr_type);
4976 local_copy.MutateHoistedGenericType (storey);
4977 Statement.MutateHoistedGenericType (storey);
4980 protected override void CloneTo (CloneContext clonectx, Statement t)
4982 UsingTemporary target = (UsingTemporary) t;
4984 target.expr = expr.Clone (clonectx);
4985 target.Statement = Statement.Clone (clonectx);
4989 public class Using : ExceptionStatement {
4990 Statement stmt;
4991 public Statement EmbeddedStatement {
4992 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4995 Expression var;
4996 Expression init;
4998 ExpressionStatement assign;
5000 public Using (Expression var, Expression init, Statement stmt, Location l)
5002 this.var = var;
5003 this.init = init;
5004 this.stmt = stmt;
5005 loc = l;
5008 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
5010 ec.Report.SymbolRelatedToPreviousError (expr.Type);
5011 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5012 expr.GetSignatureForError ());
5015 protected override void EmitPreTryBody (EmitContext ec)
5017 assign.EmitStatement (ec);
5020 protected override void EmitTryBody (EmitContext ec)
5022 stmt.Emit (ec);
5025 protected override void EmitFinallyBody (EmitContext ec)
5027 ILGenerator ig = ec.ig;
5028 Label skip = ig.DefineLabel ();
5030 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5031 if (emit_null_check) {
5032 var.Emit (ec);
5033 ig.Emit (OpCodes.Brfalse, skip);
5036 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5038 if (emit_null_check)
5039 ig.MarkLabel (skip);
5042 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5044 assign.MutateHoistedGenericType (storey);
5045 var.MutateHoistedGenericType (storey);
5046 stmt.MutateHoistedGenericType (storey);
5049 public override bool Resolve (BlockContext ec)
5051 if (!ResolveVariable (ec))
5052 return false;
5054 ec.StartFlowBranching (this);
5056 bool ok = stmt.Resolve (ec);
5058 ec.EndFlowBranching ();
5060 ok &= base.Resolve (ec);
5062 if (TypeManager.void_dispose_void == null) {
5063 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5064 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5067 return ok;
5070 bool ResolveVariable (BlockContext ec)
5072 assign = new SimpleAssign (var, init, loc);
5073 assign = assign.ResolveStatement (ec);
5074 if (assign == null)
5075 return false;
5077 if (assign.Type == TypeManager.idisposable_type ||
5078 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5079 return true;
5082 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5083 if (e == null) {
5084 Error_IsNotConvertibleToIDisposable (ec, var);
5085 return false;
5088 throw new NotImplementedException ("covariance?");
5091 protected override void CloneTo (CloneContext clonectx, Statement t)
5093 Using target = (Using) t;
5095 target.var = var.Clone (clonectx);
5096 target.init = init.Clone (clonectx);
5097 target.stmt = stmt.Clone (clonectx);
5101 /// <summary>
5102 /// Implementation of the foreach C# statement
5103 /// </summary>
5104 public class Foreach : Statement {
5106 sealed class ArrayForeach : Statement
5108 class ArrayCounter : TemporaryVariable
5110 StatementExpression increment;
5112 public ArrayCounter (Location loc)
5113 : base (TypeManager.int32_type, loc)
5117 public void ResolveIncrement (BlockContext ec)
5119 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5120 increment.Resolve (ec);
5123 public void EmitIncrement (EmitContext ec)
5125 increment.Emit (ec);
5129 readonly Foreach for_each;
5130 readonly Statement statement;
5132 Expression conv;
5133 TemporaryVariable[] lengths;
5134 Expression [] length_exprs;
5135 ArrayCounter[] counter;
5137 TemporaryVariable copy;
5138 Expression access;
5140 public ArrayForeach (Foreach @foreach, int rank)
5142 for_each = @foreach;
5143 statement = for_each.statement;
5144 loc = @foreach.loc;
5146 counter = new ArrayCounter [rank];
5147 length_exprs = new Expression [rank];
5150 // Only use temporary length variables when dealing with
5151 // multi-dimensional arrays
5153 if (rank > 1)
5154 lengths = new TemporaryVariable [rank];
5157 protected override void CloneTo (CloneContext clonectx, Statement target)
5159 throw new NotImplementedException ();
5162 public override bool Resolve (BlockContext ec)
5164 copy = new TemporaryVariable (for_each.expr.Type, loc);
5165 copy.Resolve (ec);
5167 int rank = length_exprs.Length;
5168 Arguments list = new Arguments (rank);
5169 for (int i = 0; i < rank; i++) {
5170 counter [i] = new ArrayCounter (loc);
5171 counter [i].ResolveIncrement (ec);
5173 if (rank == 1) {
5174 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5175 } else {
5176 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5177 lengths [i].Resolve (ec);
5179 Arguments args = new Arguments (1);
5180 args.Add (new Argument (new IntConstant (i, loc)));
5181 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5184 list.Add (new Argument (counter [i]));
5187 access = new ElementAccess (copy, list).Resolve (ec);
5188 if (access == null)
5189 return false;
5191 Expression var_type = for_each.type;
5192 VarExpr ve = var_type as VarExpr;
5193 if (ve != null) {
5194 // Infer implicitly typed local variable from foreach array type
5195 var_type = new TypeExpression (access.Type, ve.Location);
5198 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5199 if (var_type == null)
5200 return false;
5202 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5203 if (conv == null)
5204 return false;
5206 bool ok = true;
5208 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5209 ec.CurrentBranching.CreateSibling ();
5211 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5212 if (for_each.variable == null)
5213 ok = false;
5215 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5216 if (!statement.Resolve (ec))
5217 ok = false;
5218 ec.EndFlowBranching ();
5220 // There's no direct control flow from the end of the embedded statement to the end of the loop
5221 ec.CurrentBranching.CurrentUsageVector.Goto ();
5223 ec.EndFlowBranching ();
5225 return ok;
5228 protected override void DoEmit (EmitContext ec)
5230 ILGenerator ig = ec.ig;
5232 copy.EmitAssign (ec, for_each.expr);
5234 int rank = length_exprs.Length;
5235 Label[] test = new Label [rank];
5236 Label[] loop = new Label [rank];
5238 for (int i = 0; i < rank; i++) {
5239 test [i] = ig.DefineLabel ();
5240 loop [i] = ig.DefineLabel ();
5242 if (lengths != null)
5243 lengths [i].EmitAssign (ec, length_exprs [i]);
5246 IntConstant zero = new IntConstant (0, loc);
5247 for (int i = 0; i < rank; i++) {
5248 counter [i].EmitAssign (ec, zero);
5250 ig.Emit (OpCodes.Br, test [i]);
5251 ig.MarkLabel (loop [i]);
5254 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5256 statement.Emit (ec);
5258 ig.MarkLabel (ec.LoopBegin);
5260 for (int i = rank - 1; i >= 0; i--){
5261 counter [i].EmitIncrement (ec);
5263 ig.MarkLabel (test [i]);
5264 counter [i].Emit (ec);
5266 if (lengths != null)
5267 lengths [i].Emit (ec);
5268 else
5269 length_exprs [i].Emit (ec);
5271 ig.Emit (OpCodes.Blt, loop [i]);
5274 ig.MarkLabel (ec.LoopEnd);
5277 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5279 for_each.expr.MutateHoistedGenericType (storey);
5281 copy.MutateHoistedGenericType (storey);
5282 conv.MutateHoistedGenericType (storey);
5283 statement.MutateHoistedGenericType (storey);
5285 for (int i = 0; i < counter.Length; i++) {
5286 counter [i].MutateHoistedGenericType (storey);
5287 if (lengths != null)
5288 lengths [i].MutateHoistedGenericType (storey);
5293 sealed class CollectionForeach : Statement
5295 class CollectionForeachStatement : Statement
5297 Type type;
5298 Expression variable, current, conv;
5299 Statement statement;
5300 Assign assign;
5302 public CollectionForeachStatement (Type type, Expression variable,
5303 Expression current, Statement statement,
5304 Location loc)
5306 this.type = type;
5307 this.variable = variable;
5308 this.current = current;
5309 this.statement = statement;
5310 this.loc = loc;
5313 protected override void CloneTo (CloneContext clonectx, Statement target)
5315 throw new NotImplementedException ();
5318 public override bool Resolve (BlockContext ec)
5320 current = current.Resolve (ec);
5321 if (current == null)
5322 return false;
5324 conv = Convert.ExplicitConversion (ec, current, type, loc);
5325 if (conv == null)
5326 return false;
5328 assign = new SimpleAssign (variable, conv, loc);
5329 if (assign.Resolve (ec) == null)
5330 return false;
5332 if (!statement.Resolve (ec))
5333 return false;
5335 return true;
5338 protected override void DoEmit (EmitContext ec)
5340 assign.EmitStatement (ec);
5341 statement.Emit (ec);
5344 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5346 assign.MutateHoistedGenericType (storey);
5347 statement.MutateHoistedGenericType (storey);
5351 Expression variable, expr;
5352 Statement statement;
5354 TemporaryVariable enumerator;
5355 Expression init;
5356 Statement loop;
5357 Statement wrapper;
5359 MethodGroupExpr get_enumerator;
5360 PropertyExpr get_current;
5361 MethodInfo move_next;
5362 Expression var_type;
5363 Type enumerator_type;
5364 bool enumerator_found;
5366 public CollectionForeach (Expression var_type, Expression var,
5367 Expression expr, Statement stmt, Location l)
5369 this.var_type = var_type;
5370 this.variable = var;
5371 this.expr = expr;
5372 statement = stmt;
5373 loc = l;
5376 protected override void CloneTo (CloneContext clonectx, Statement target)
5378 throw new NotImplementedException ();
5381 bool GetEnumeratorFilter (ResolveContext ec, MethodInfo mi)
5383 Type return_type = mi.ReturnType;
5386 // Ok, we can access it, now make sure that we can do something
5387 // with this `GetEnumerator'
5390 if (return_type == TypeManager.ienumerator_type ||
5391 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5393 // If it is not an interface, lets try to find the methods ourselves.
5394 // For example, if we have:
5395 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5396 // We can avoid the iface call. This is a runtime perf boost.
5397 // even bigger if we have a ValueType, because we avoid the cost
5398 // of boxing.
5400 // We have to make sure that both methods exist for us to take
5401 // this path. If one of the methods does not exist, we will just
5402 // use the interface. Sadly, this complex if statement is the only
5403 // way I could do this without a goto
5406 if (TypeManager.bool_movenext_void == null) {
5407 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5408 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5411 if (TypeManager.ienumerator_getcurrent == null) {
5412 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5413 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5417 // Prefer a generic enumerator over a non-generic one.
5419 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5420 enumerator_type = return_type;
5421 if (!FetchGetCurrent (ec, return_type))
5422 get_current = new PropertyExpr (
5423 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5424 if (!FetchMoveNext (return_type))
5425 move_next = TypeManager.bool_movenext_void;
5426 return true;
5429 if (return_type.IsInterface ||
5430 !FetchMoveNext (return_type) ||
5431 !FetchGetCurrent (ec, return_type)) {
5432 enumerator_type = return_type;
5433 move_next = TypeManager.bool_movenext_void;
5434 get_current = new PropertyExpr (
5435 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5436 return true;
5438 } else {
5440 // Ok, so they dont return an IEnumerable, we will have to
5441 // find if they support the GetEnumerator pattern.
5444 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5445 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",
5446 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5447 return false;
5451 enumerator_type = return_type;
5453 return true;
5457 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5459 bool FetchMoveNext (Type t)
5461 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5462 MemberTypes.Method,
5463 BindingFlags.Public | BindingFlags.Instance,
5464 "MoveNext", null);
5466 if (move_next_list == null)
5467 return false;
5469 foreach (MemberInfo m in move_next_list){
5470 MethodInfo mi = (MethodInfo) m;
5472 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5473 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5474 move_next = mi;
5475 return true;
5479 return false;
5483 // Retrieves a `public T get_Current ()' method from the Type `t'
5485 bool FetchGetCurrent (ResolveContext ec, Type t)
5487 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5488 ec.CurrentType, t, "Current", MemberTypes.Property,
5489 Expression.AllBindingFlags, loc) as PropertyExpr;
5490 if (pe == null)
5491 return false;
5493 get_current = pe;
5494 return true;
5497 void Error_Enumerator (BlockContext ec)
5499 if (enumerator_found) {
5500 return;
5503 ec.Report.Error (1579, loc,
5504 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5505 TypeManager.CSharpName (expr.Type));
5508 bool IsOverride (MethodInfo m)
5510 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5512 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5513 return false;
5514 if (m is MethodBuilder)
5515 return true;
5517 MethodInfo base_method = m.GetBaseDefinition ();
5518 return base_method != m;
5521 bool TryType (ResolveContext ec, Type t)
5523 MethodGroupExpr mg = Expression.MemberLookup (ec.Compiler,
5524 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5525 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5526 if (mg == null)
5527 return false;
5529 MethodInfo result = null;
5530 MethodInfo tmp_move_next = null;
5531 PropertyExpr tmp_get_cur = null;
5532 Type tmp_enumerator_type = enumerator_type;
5533 foreach (MethodInfo mi in mg.Methods) {
5534 if (TypeManager.GetParameterData (mi).Count != 0)
5535 continue;
5537 // Check whether GetEnumerator is public
5538 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5539 continue;
5541 if (IsOverride (mi))
5542 continue;
5544 enumerator_found = true;
5546 if (!GetEnumeratorFilter (ec, mi))
5547 continue;
5549 if (result != null) {
5550 if (TypeManager.IsGenericType (result.ReturnType)) {
5551 if (!TypeManager.IsGenericType (mi.ReturnType))
5552 continue;
5554 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5555 ec.Report.SymbolRelatedToPreviousError (t);
5556 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5557 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5558 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5559 return false;
5562 // Always prefer generics enumerators
5563 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5564 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5565 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5566 continue;
5568 ec.Report.SymbolRelatedToPreviousError (result);
5569 ec.Report.SymbolRelatedToPreviousError (mi);
5570 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5571 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5572 return false;
5575 result = mi;
5576 tmp_move_next = move_next;
5577 tmp_get_cur = get_current;
5578 tmp_enumerator_type = enumerator_type;
5579 if (mi.DeclaringType == t)
5580 break;
5583 if (result != null) {
5584 move_next = tmp_move_next;
5585 get_current = tmp_get_cur;
5586 enumerator_type = tmp_enumerator_type;
5587 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5588 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5590 if (t != expr.Type) {
5591 expr = Convert.ExplicitConversion (
5592 ec, expr, t, loc);
5593 if (expr == null)
5594 throw new InternalErrorException ();
5597 get_enumerator.InstanceExpression = expr;
5598 get_enumerator.IsBase = t != expr.Type;
5600 return true;
5603 return false;
5606 bool ProbeCollectionType (ResolveContext ec, Type t)
5608 int errors = ec.Report.Errors;
5609 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5610 if (TryType (ec, tt))
5611 return true;
5612 tt = tt.BaseType;
5615 if (ec.Report.Errors > errors)
5616 return false;
5619 // Now try to find the method in the interfaces
5621 Type [] ifaces = TypeManager.GetInterfaces (t);
5622 foreach (Type i in ifaces){
5623 if (TryType (ec, i))
5624 return true;
5627 return false;
5630 public override bool Resolve (BlockContext ec)
5632 enumerator_type = TypeManager.ienumerator_type;
5634 if (!ProbeCollectionType (ec, expr.Type)) {
5635 Error_Enumerator (ec);
5636 return false;
5639 VarExpr ve = var_type as VarExpr;
5640 if (ve != null) {
5641 // Infer implicitly typed local variable from foreach enumerable type
5642 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5645 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5646 if (var_type == null)
5647 return false;
5649 enumerator = new TemporaryVariable (enumerator_type, loc);
5650 enumerator.Resolve (ec);
5652 init = new Invocation (get_enumerator, null);
5653 init = init.Resolve (ec);
5654 if (init == null)
5655 return false;
5657 Expression move_next_expr;
5659 MemberInfo[] mi = new MemberInfo[] { move_next };
5660 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5661 mg.InstanceExpression = enumerator;
5663 move_next_expr = new Invocation (mg, null);
5666 get_current.InstanceExpression = enumerator;
5668 Statement block = new CollectionForeachStatement (
5669 var_type.Type, variable, get_current, statement, loc);
5671 loop = new While (move_next_expr, block, loc);
5674 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5675 if (implements_idisposable || !enumerator_type.IsSealed) {
5676 wrapper = new DisposableWrapper (this, implements_idisposable);
5677 } else {
5678 wrapper = new NonDisposableWrapper (this);
5681 return wrapper.Resolve (ec);
5684 protected override void DoEmit (EmitContext ec)
5686 wrapper.Emit (ec);
5689 class NonDisposableWrapper : Statement {
5690 CollectionForeach parent;
5692 internal NonDisposableWrapper (CollectionForeach parent)
5694 this.parent = parent;
5697 protected override void CloneTo (CloneContext clonectx, Statement target)
5699 throw new NotSupportedException ();
5702 public override bool Resolve (BlockContext ec)
5704 return parent.ResolveLoop (ec);
5707 protected override void DoEmit (EmitContext ec)
5709 parent.EmitLoopInit (ec);
5710 parent.EmitLoopBody (ec);
5713 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5715 throw new NotSupportedException ();
5719 sealed class DisposableWrapper : ExceptionStatement
5721 CollectionForeach parent;
5722 bool implements_idisposable;
5724 internal DisposableWrapper (CollectionForeach parent, bool implements)
5726 this.parent = parent;
5727 this.implements_idisposable = implements;
5730 protected override void CloneTo (CloneContext clonectx, Statement target)
5732 throw new NotSupportedException ();
5735 public override bool Resolve (BlockContext ec)
5737 bool ok = true;
5739 ec.StartFlowBranching (this);
5741 if (!parent.ResolveLoop (ec))
5742 ok = false;
5744 ec.EndFlowBranching ();
5746 ok &= base.Resolve (ec);
5748 if (TypeManager.void_dispose_void == null) {
5749 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5750 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5752 return ok;
5755 protected override void EmitPreTryBody (EmitContext ec)
5757 parent.EmitLoopInit (ec);
5760 protected override void EmitTryBody (EmitContext ec)
5762 parent.EmitLoopBody (ec);
5765 protected override void EmitFinallyBody (EmitContext ec)
5767 Expression instance = parent.enumerator;
5768 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5769 ILGenerator ig = ec.ig;
5771 parent.enumerator.Emit (ec);
5773 Label call_dispose = ig.DefineLabel ();
5775 if (!implements_idisposable) {
5776 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5777 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5778 temp.Store (ec);
5779 temp.Emit (ec);
5780 instance = temp;
5783 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5785 // using 'endfinally' to empty the evaluation stack
5786 ig.Emit (OpCodes.Endfinally);
5787 ig.MarkLabel (call_dispose);
5790 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5793 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5795 throw new NotSupportedException ();
5799 bool ResolveLoop (BlockContext ec)
5801 return loop.Resolve (ec);
5804 void EmitLoopInit (EmitContext ec)
5806 enumerator.EmitAssign (ec, init);
5809 void EmitLoopBody (EmitContext ec)
5811 loop.Emit (ec);
5814 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5816 enumerator_type = storey.MutateType (enumerator_type);
5817 init.MutateHoistedGenericType (storey);
5818 loop.MutateHoistedGenericType (storey);
5822 Expression type;
5823 Expression variable;
5824 Expression expr;
5825 Statement statement;
5827 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5828 Statement stmt, Location l)
5830 this.type = type;
5831 this.variable = var;
5832 this.expr = expr;
5833 statement = stmt;
5834 loc = l;
5837 public Statement Statement {
5838 get { return statement; }
5841 public override bool Resolve (BlockContext ec)
5843 expr = expr.Resolve (ec);
5844 if (expr == null)
5845 return false;
5847 if (expr.IsNull) {
5848 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5849 return false;
5852 if (expr.Type == TypeManager.string_type) {
5853 statement = new ArrayForeach (this, 1);
5854 } else if (expr.Type.IsArray) {
5855 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5856 } else {
5857 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5858 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5859 expr.ExprClassName);
5860 return false;
5863 statement = new CollectionForeach (type, variable, expr, statement, loc);
5866 return statement.Resolve (ec);
5869 protected override void DoEmit (EmitContext ec)
5871 ILGenerator ig = ec.ig;
5873 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5874 ec.LoopBegin = ig.DefineLabel ();
5875 ec.LoopEnd = ig.DefineLabel ();
5877 statement.Emit (ec);
5879 ec.LoopBegin = old_begin;
5880 ec.LoopEnd = old_end;
5883 protected override void CloneTo (CloneContext clonectx, Statement t)
5885 Foreach target = (Foreach) t;
5887 target.type = type.Clone (clonectx);
5888 target.variable = variable.Clone (clonectx);
5889 target.expr = expr.Clone (clonectx);
5890 target.statement = statement.Clone (clonectx);
5893 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5895 statement.MutateHoistedGenericType (storey);