2010-05-25 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / statement.cs
blob3c0159937e436b094e1ba59c6e68dafa65d2d71d
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.Generic;
20 namespace Mono.CSharp {
22 public abstract class Statement {
23 public Location loc;
25 /// <summary>
26 /// Resolves the statement, true means that all sub-statements
27 /// did resolve ok.
28 // </summary>
29 public virtual bool Resolve (BlockContext ec)
31 return true;
34 /// <summary>
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
37 /// </summary>
38 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
49 if (warn)
50 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
52 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53 bool ok = Resolve (ec);
54 ec.KillFlowBranching ();
56 return ok;
59 /// <summary>
60 /// Return value indicates whether all code paths emitted return.
61 /// </summary>
62 protected abstract void DoEmit (EmitContext ec);
64 public virtual void Emit (EmitContext ec)
66 ec.Mark (loc);
67 DoEmit (ec);
71 // This routine must be overrided in derived classes and make copies
72 // of all the data that might be modified if resolved
73 //
74 protected abstract void CloneTo (CloneContext clonectx, Statement target);
76 public Statement Clone (CloneContext clonectx)
78 Statement s = (Statement) this.MemberwiseClone ();
79 CloneTo (clonectx, s);
80 return s;
83 public virtual Expression CreateExpressionTree (ResolveContext ec)
85 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
86 return null;
89 public Statement PerformClone ()
91 CloneContext clonectx = new CloneContext ();
93 return Clone (clonectx);
97 public sealed class EmptyStatement : Statement
99 public EmptyStatement (Location loc)
101 this.loc = loc;
104 public override bool Resolve (BlockContext ec)
106 return true;
109 public override bool ResolveUnreachable (BlockContext ec, bool warn)
111 return true;
114 public override void Emit (EmitContext ec)
118 protected override void DoEmit (EmitContext ec)
120 throw new NotSupportedException ();
123 protected override void CloneTo (CloneContext clonectx, Statement target)
125 // nothing needed.
129 public class If : Statement {
130 Expression expr;
131 public Statement TrueStatement;
132 public Statement FalseStatement;
134 bool is_true_ret;
136 public If (Expression bool_expr, Statement true_statement, Location l)
137 : this (bool_expr, true_statement, null, l)
141 public If (Expression bool_expr,
142 Statement true_statement,
143 Statement false_statement,
144 Location l)
146 this.expr = bool_expr;
147 TrueStatement = true_statement;
148 FalseStatement = false_statement;
149 loc = l;
152 public override bool Resolve (BlockContext ec)
154 bool ok = true;
156 Report.Debug (1, "START IF BLOCK", loc);
158 expr = expr.Resolve (ec);
159 if (expr == null) {
160 ok = false;
161 } else {
163 // Dead code elimination
165 if (expr is Constant) {
166 bool take = !((Constant) expr).IsDefaultValue;
168 if (take) {
169 if (!TrueStatement.Resolve (ec))
170 return false;
172 if ((FalseStatement != null) &&
173 !FalseStatement.ResolveUnreachable (ec, true))
174 return false;
175 FalseStatement = null;
176 } else {
177 if (!TrueStatement.ResolveUnreachable (ec, true))
178 return false;
179 TrueStatement = null;
181 if ((FalseStatement != null) &&
182 !FalseStatement.Resolve (ec))
183 return false;
186 return true;
190 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
192 ok &= TrueStatement.Resolve (ec);
194 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
196 ec.CurrentBranching.CreateSibling ();
198 if (FalseStatement != null)
199 ok &= FalseStatement.Resolve (ec);
201 ec.EndFlowBranching ();
203 Report.Debug (1, "END IF BLOCK", loc);
205 return ok;
208 protected override void DoEmit (EmitContext ec)
210 Label false_target = ec.DefineLabel ();
211 Label end;
214 // If we're a boolean constant, Resolve() already
215 // eliminated dead code for us.
217 Constant c = expr as Constant;
218 if (c != null){
219 c.EmitSideEffect (ec);
221 if (!c.IsDefaultValue)
222 TrueStatement.Emit (ec);
223 else if (FalseStatement != null)
224 FalseStatement.Emit (ec);
226 return;
229 expr.EmitBranchable (ec, false_target, false);
231 TrueStatement.Emit (ec);
233 if (FalseStatement != null){
234 bool branch_emitted = false;
236 end = ec.DefineLabel ();
237 if (!is_true_ret){
238 ec.Emit (OpCodes.Br, end);
239 branch_emitted = true;
242 ec.MarkLabel (false_target);
243 FalseStatement.Emit (ec);
245 if (branch_emitted)
246 ec.MarkLabel (end);
247 } else {
248 ec.MarkLabel (false_target);
252 protected override void CloneTo (CloneContext clonectx, Statement t)
254 If target = (If) t;
256 target.expr = expr.Clone (clonectx);
257 target.TrueStatement = TrueStatement.Clone (clonectx);
258 if (FalseStatement != null)
259 target.FalseStatement = FalseStatement.Clone (clonectx);
263 public class Do : Statement {
264 public Expression expr;
265 public Statement EmbeddedStatement;
267 public Do (Statement statement, BooleanExpression bool_expr, Location l)
269 expr = bool_expr;
270 EmbeddedStatement = statement;
271 loc = l;
274 public override bool Resolve (BlockContext ec)
276 bool ok = true;
278 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
280 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
282 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
283 if (!EmbeddedStatement.Resolve (ec))
284 ok = false;
285 ec.EndFlowBranching ();
287 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
288 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
290 expr = expr.Resolve (ec);
291 if (expr == null)
292 ok = false;
293 else if (expr is Constant){
294 bool infinite = !((Constant) expr).IsDefaultValue;
295 if (infinite)
296 ec.CurrentBranching.CurrentUsageVector.Goto ();
299 ec.EndFlowBranching ();
301 return ok;
304 protected override void DoEmit (EmitContext ec)
306 Label loop = ec.DefineLabel ();
307 Label old_begin = ec.LoopBegin;
308 Label old_end = ec.LoopEnd;
310 ec.LoopBegin = ec.DefineLabel ();
311 ec.LoopEnd = ec.DefineLabel ();
313 ec.MarkLabel (loop);
314 EmbeddedStatement.Emit (ec);
315 ec.MarkLabel (ec.LoopBegin);
318 // Dead code elimination
320 if (expr is Constant){
321 bool res = !((Constant) expr).IsDefaultValue;
323 expr.EmitSideEffect (ec);
324 if (res)
325 ec.Emit (OpCodes.Br, loop);
326 } else
327 expr.EmitBranchable (ec, loop, true);
329 ec.MarkLabel (ec.LoopEnd);
331 ec.LoopBegin = old_begin;
332 ec.LoopEnd = old_end;
335 protected override void CloneTo (CloneContext clonectx, Statement t)
337 Do target = (Do) t;
339 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
340 target.expr = expr.Clone (clonectx);
344 public class While : Statement {
345 public Expression expr;
346 public Statement Statement;
347 bool infinite, empty;
349 public While (BooleanExpression bool_expr, Statement statement, Location l)
351 this.expr = bool_expr;
352 Statement = statement;
353 loc = l;
356 public override bool Resolve (BlockContext ec)
358 bool ok = true;
360 expr = expr.Resolve (ec);
361 if (expr == null)
362 ok = false;
365 // Inform whether we are infinite or not
367 if (expr is Constant){
368 bool value = !((Constant) expr).IsDefaultValue;
370 if (value == false){
371 if (!Statement.ResolveUnreachable (ec, true))
372 return false;
373 empty = true;
374 return true;
375 } else
376 infinite = true;
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
380 if (!infinite)
381 ec.CurrentBranching.CreateSibling ();
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!Statement.Resolve (ec))
385 ok = false;
386 ec.EndFlowBranching ();
388 // There's no direct control flow from the end of the embedded statement to the end of the loop
389 ec.CurrentBranching.CurrentUsageVector.Goto ();
391 ec.EndFlowBranching ();
393 return ok;
396 protected override void DoEmit (EmitContext ec)
398 if (empty) {
399 expr.EmitSideEffect (ec);
400 return;
403 Label old_begin = ec.LoopBegin;
404 Label old_end = ec.LoopEnd;
406 ec.LoopBegin = ec.DefineLabel ();
407 ec.LoopEnd = ec.DefineLabel ();
410 // Inform whether we are infinite or not
412 if (expr is Constant){
413 // expr is 'true', since the 'empty' case above handles the 'false' case
414 ec.MarkLabel (ec.LoopBegin);
415 expr.EmitSideEffect (ec);
416 Statement.Emit (ec);
417 ec.Emit (OpCodes.Br, ec.LoopBegin);
420 // Inform that we are infinite (ie, `we return'), only
421 // if we do not `break' inside the code.
423 ec.MarkLabel (ec.LoopEnd);
424 } else {
425 Label while_loop = ec.DefineLabel ();
427 ec.Emit (OpCodes.Br, ec.LoopBegin);
428 ec.MarkLabel (while_loop);
430 Statement.Emit (ec);
432 ec.MarkLabel (ec.LoopBegin);
433 ec.Mark (loc);
435 expr.EmitBranchable (ec, while_loop, true);
437 ec.MarkLabel (ec.LoopEnd);
440 ec.LoopBegin = old_begin;
441 ec.LoopEnd = old_end;
444 public override void Emit (EmitContext ec)
446 DoEmit (ec);
449 protected override void CloneTo (CloneContext clonectx, Statement t)
451 While target = (While) t;
453 target.expr = expr.Clone (clonectx);
454 target.Statement = Statement.Clone (clonectx);
458 public class For : Statement {
459 Expression Test;
460 Statement InitStatement;
461 Statement Increment;
462 public Statement Statement;
463 bool infinite, empty;
465 public For (Statement init_statement,
466 BooleanExpression test,
467 Statement increment,
468 Statement statement,
469 Location l)
471 InitStatement = init_statement;
472 Test = test;
473 Increment = increment;
474 Statement = statement;
475 loc = l;
478 public override bool Resolve (BlockContext ec)
480 bool ok = true;
482 if (InitStatement != null){
483 if (!InitStatement.Resolve (ec))
484 ok = false;
487 if (Test != null){
488 Test = Test.Resolve (ec);
489 if (Test == null)
490 ok = false;
491 else if (Test is Constant){
492 bool value = !((Constant) Test).IsDefaultValue;
494 if (value == false){
495 if (!Statement.ResolveUnreachable (ec, true))
496 return false;
497 if ((Increment != null) &&
498 !Increment.ResolveUnreachable (ec, false))
499 return false;
500 empty = true;
501 return true;
502 } else
503 infinite = true;
505 } else
506 infinite = true;
508 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
509 if (!infinite)
510 ec.CurrentBranching.CreateSibling ();
512 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
514 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
515 if (!Statement.Resolve (ec))
516 ok = false;
517 ec.EndFlowBranching ();
519 if (Increment != null){
520 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
521 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
522 ok = false;
523 } else {
524 if (!Increment.Resolve (ec))
525 ok = false;
529 // There's no direct control flow from the end of the embedded statement to the end of the loop
530 ec.CurrentBranching.CurrentUsageVector.Goto ();
532 ec.EndFlowBranching ();
534 return ok;
537 protected override void DoEmit (EmitContext ec)
539 if (InitStatement != null)
540 InitStatement.Emit (ec);
542 if (empty) {
543 Test.EmitSideEffect (ec);
544 return;
547 Label old_begin = ec.LoopBegin;
548 Label old_end = ec.LoopEnd;
549 Label loop = ec.DefineLabel ();
550 Label test = ec.DefineLabel ();
552 ec.LoopBegin = ec.DefineLabel ();
553 ec.LoopEnd = ec.DefineLabel ();
555 ec.Emit (OpCodes.Br, test);
556 ec.MarkLabel (loop);
557 Statement.Emit (ec);
559 ec.MarkLabel (ec.LoopBegin);
560 Increment.Emit (ec);
562 ec.MarkLabel (test);
564 // If test is null, there is no test, and we are just
565 // an infinite loop
567 if (Test != null){
569 // The Resolve code already catches the case for
570 // Test == Constant (false) so we know that
571 // this is true
573 if (Test is Constant) {
574 Test.EmitSideEffect (ec);
575 ec.Emit (OpCodes.Br, loop);
576 } else {
577 Test.EmitBranchable (ec, loop, true);
580 } else
581 ec.Emit (OpCodes.Br, loop);
582 ec.MarkLabel (ec.LoopEnd);
584 ec.LoopBegin = old_begin;
585 ec.LoopEnd = old_end;
588 protected override void CloneTo (CloneContext clonectx, Statement t)
590 For target = (For) t;
592 if (InitStatement != null)
593 target.InitStatement = InitStatement.Clone (clonectx);
594 if (Test != null)
595 target.Test = Test.Clone (clonectx);
596 if (Increment != null)
597 target.Increment = Increment.Clone (clonectx);
598 target.Statement = Statement.Clone (clonectx);
602 public class StatementExpression : Statement {
603 ExpressionStatement expr;
605 public StatementExpression (ExpressionStatement expr)
607 this.expr = expr;
608 loc = expr.Location;
611 public override bool Resolve (BlockContext ec)
613 expr = expr.ResolveStatement (ec);
614 return expr != null;
617 protected override void DoEmit (EmitContext ec)
619 expr.EmitStatement (ec);
622 public override string ToString ()
624 return "StatementExpression (" + expr + ")";
627 protected override void CloneTo (CloneContext clonectx, Statement t)
629 StatementExpression target = (StatementExpression) t;
631 target.expr = (ExpressionStatement) expr.Clone (clonectx);
635 // A 'return' or a 'yield break'
636 public abstract class ExitStatement : Statement
638 protected bool unwind_protect;
639 protected abstract bool DoResolve (BlockContext ec);
641 public virtual void Error_FinallyClause (Report Report)
643 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
646 public sealed override bool Resolve (BlockContext ec)
648 if (!DoResolve (ec))
649 return false;
651 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
652 if (unwind_protect)
653 ec.NeedReturnLabel ();
654 ec.CurrentBranching.CurrentUsageVector.Goto ();
655 return true;
659 /// <summary>
660 /// Implements the return statement
661 /// </summary>
662 public class Return : ExitStatement {
663 protected Expression Expr;
664 public Return (Expression expr, Location l)
666 Expr = expr;
667 loc = l;
670 protected override bool DoResolve (BlockContext ec)
672 if (Expr == null) {
673 if (ec.ReturnType == TypeManager.void_type)
674 return true;
676 ec.Report.Error (126, loc,
677 "An object of a type convertible to `{0}' is required for the return statement",
678 TypeManager.CSharpName (ec.ReturnType));
679 return false;
682 if (ec.CurrentBlock.Toplevel.IsIterator) {
683 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
684 "statement to return a value, or yield break to end the iteration");
687 AnonymousExpression am = ec.CurrentAnonymousMethod;
688 if (am == null && ec.ReturnType == TypeManager.void_type) {
689 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
690 ec.GetSignatureForError ());
693 Expr = Expr.Resolve (ec);
694 if (Expr == null)
695 return false;
697 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
698 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
699 return true;
702 if (Expr.Type != ec.ReturnType) {
703 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
705 if (Expr == null) {
706 if (am != null) {
707 ec.Report.Error (1662, loc,
708 "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",
709 am.ContainerType, am.GetSignatureForError ());
711 return false;
715 return true;
718 protected override void DoEmit (EmitContext ec)
720 if (Expr != null) {
721 Expr.Emit (ec);
723 if (unwind_protect)
724 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
727 if (unwind_protect)
728 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
729 else
730 ec.Emit (OpCodes.Ret);
733 protected override void CloneTo (CloneContext clonectx, Statement t)
735 Return target = (Return) t;
736 // It's null for simple return;
737 if (Expr != null)
738 target.Expr = Expr.Clone (clonectx);
742 public class Goto : Statement {
743 string target;
744 LabeledStatement label;
745 bool unwind_protect;
747 public override bool Resolve (BlockContext ec)
749 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
750 ec.CurrentBranching.CurrentUsageVector.Goto ();
751 return true;
754 public Goto (string label, Location l)
756 loc = l;
757 target = label;
760 public string Target {
761 get { return target; }
764 public void SetResolvedTarget (LabeledStatement label)
766 this.label = label;
767 label.AddReference ();
770 protected override void CloneTo (CloneContext clonectx, Statement target)
772 // Nothing to clone
775 protected override void DoEmit (EmitContext ec)
777 if (label == null)
778 throw new InternalErrorException ("goto emitted before target resolved");
779 Label l = label.LabelTarget (ec);
780 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
784 public class LabeledStatement : Statement {
785 string name;
786 bool defined;
787 bool referenced;
788 Label label;
790 FlowBranching.UsageVector vectors;
792 public LabeledStatement (string name, Location l)
794 this.name = name;
795 this.loc = l;
798 public Label LabelTarget (EmitContext ec)
800 if (defined)
801 return label;
803 label = ec.DefineLabel ();
804 defined = true;
805 return label;
808 public string Name {
809 get { return name; }
812 public bool IsDefined {
813 get { return defined; }
816 public bool HasBeenReferenced {
817 get { return referenced; }
820 public FlowBranching.UsageVector JumpOrigins {
821 get { return vectors; }
824 public void AddUsageVector (FlowBranching.UsageVector vector)
826 vector = vector.Clone ();
827 vector.Next = vectors;
828 vectors = vector;
831 protected override void CloneTo (CloneContext clonectx, Statement target)
833 // nothing to clone
836 public override bool Resolve (BlockContext ec)
838 // this flow-branching will be terminated when the surrounding block ends
839 ec.StartFlowBranching (this);
840 return true;
843 protected override void DoEmit (EmitContext ec)
845 LabelTarget (ec);
846 ec.MarkLabel (label);
849 public void AddReference ()
851 referenced = true;
856 /// <summary>
857 /// `goto default' statement
858 /// </summary>
859 public class GotoDefault : Statement {
861 public GotoDefault (Location l)
863 loc = l;
866 protected override void CloneTo (CloneContext clonectx, Statement target)
868 // nothing to clone
871 public override bool Resolve (BlockContext ec)
873 ec.CurrentBranching.CurrentUsageVector.Goto ();
875 if (ec.Switch == null) {
876 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
877 return false;
880 if (!ec.Switch.GotDefault) {
881 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
882 return false;
885 return true;
888 protected override void DoEmit (EmitContext ec)
890 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
894 /// <summary>
895 /// `goto case' statement
896 /// </summary>
897 public class GotoCase : Statement {
898 Expression expr;
899 SwitchLabel sl;
901 public GotoCase (Expression e, Location l)
903 expr = e;
904 loc = l;
907 public override bool Resolve (BlockContext ec)
909 if (ec.Switch == null){
910 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
911 return false;
914 ec.CurrentBranching.CurrentUsageVector.Goto ();
916 expr = expr.Resolve (ec);
917 if (expr == null)
918 return false;
920 Constant c = expr as Constant;
921 if (c == null) {
922 ec.Report.Error (150, expr.Location, "A constant value is expected");
923 return false;
926 TypeSpec type = ec.Switch.SwitchType;
927 Constant res = c.TryReduce (ec, type, c.Location);
928 if (res == null) {
929 c.Error_ValueCannotBeConverted (ec, loc, type, true);
930 return false;
933 if (!Convert.ImplicitStandardConversionExists (c, type))
934 ec.Report.Warning (469, 2, loc,
935 "The `goto case' value is not implicitly convertible to type `{0}'",
936 TypeManager.CSharpName (type));
938 object val = res.GetValue ();
939 if (val == null)
940 val = SwitchLabel.NullStringCase;
942 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
943 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
944 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
945 return false;
948 return true;
951 protected override void DoEmit (EmitContext ec)
953 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
956 protected override void CloneTo (CloneContext clonectx, Statement t)
958 GotoCase target = (GotoCase) t;
960 target.expr = expr.Clone (clonectx);
964 public class Throw : Statement {
965 Expression expr;
967 public Throw (Expression expr, Location l)
969 this.expr = expr;
970 loc = l;
973 public override bool Resolve (BlockContext ec)
975 if (expr == null) {
976 ec.CurrentBranching.CurrentUsageVector.Goto ();
977 return ec.CurrentBranching.CheckRethrow (loc);
980 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
981 ec.CurrentBranching.CurrentUsageVector.Goto ();
983 if (expr == null)
984 return false;
986 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
987 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
988 else
989 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
991 return true;
994 protected override void DoEmit (EmitContext ec)
996 if (expr == null)
997 ec.Emit (OpCodes.Rethrow);
998 else {
999 expr.Emit (ec);
1001 ec.Emit (OpCodes.Throw);
1005 protected override void CloneTo (CloneContext clonectx, Statement t)
1007 Throw target = (Throw) t;
1009 if (expr != null)
1010 target.expr = expr.Clone (clonectx);
1014 public class Break : Statement {
1016 public Break (Location l)
1018 loc = l;
1021 bool unwind_protect;
1023 public override bool Resolve (BlockContext ec)
1025 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1026 ec.CurrentBranching.CurrentUsageVector.Goto ();
1027 return true;
1030 protected override void DoEmit (EmitContext ec)
1032 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1035 protected override void CloneTo (CloneContext clonectx, Statement t)
1037 // nothing needed
1041 public class Continue : Statement {
1043 public Continue (Location l)
1045 loc = l;
1048 bool unwind_protect;
1050 public override bool Resolve (BlockContext ec)
1052 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1053 ec.CurrentBranching.CurrentUsageVector.Goto ();
1054 return true;
1057 protected override void DoEmit (EmitContext ec)
1059 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1062 protected override void CloneTo (CloneContext clonectx, Statement t)
1064 // nothing needed.
1068 public interface ILocalVariable
1070 void Emit (EmitContext ec);
1071 void EmitAssign (EmitContext ec);
1072 void EmitAddressOf (EmitContext ec);
1075 public interface IKnownVariable {
1076 Block Block { get; }
1077 Location Location { get; }
1081 // The information about a user-perceived local variable
1083 public class LocalInfo : IKnownVariable, ILocalVariable {
1084 public readonly FullNamedExpression Type;
1086 public TypeSpec VariableType;
1087 public readonly string Name;
1088 public readonly Location Location;
1089 public readonly Block Block;
1091 public VariableInfo VariableInfo;
1092 HoistedVariable hoisted_variant;
1094 [Flags]
1095 enum Flags : byte {
1096 Used = 1,
1097 ReadOnly = 2,
1098 Pinned = 4,
1099 IsThis = 8,
1100 AddressTaken = 32,
1101 CompilerGenerated = 64,
1102 IsConstant = 128
1105 public enum ReadOnlyContext: byte {
1106 Using,
1107 Foreach,
1108 Fixed
1111 Flags flags;
1112 ReadOnlyContext ro_context;
1113 LocalBuilder builder;
1115 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1117 Type = type;
1118 Name = name;
1119 Block = block;
1120 Location = l;
1123 public LocalInfo (TypeContainer ds, Block block, Location l)
1125 VariableType = ds.IsGeneric ? ds.CurrentType : ds.Definition;
1126 Block = block;
1127 Location = l;
1130 public void ResolveVariable (EmitContext ec)
1132 if (HoistedVariant != null)
1133 return;
1135 if (builder == null) {
1136 builder = ec.DeclareLocal (VariableType, Pinned);
1140 public void Emit (EmitContext ec)
1142 ec.Emit (OpCodes.Ldloc, builder);
1145 public void EmitAssign (EmitContext ec)
1147 ec.Emit (OpCodes.Stloc, builder);
1150 public void EmitAddressOf (EmitContext ec)
1152 ec.Emit (OpCodes.Ldloca, builder);
1155 public void EmitSymbolInfo (EmitContext ec)
1157 if (builder != null)
1158 ec.DefineLocalVariable (Name, builder);
1162 // Hoisted local variable variant
1164 public HoistedVariable HoistedVariant {
1165 get {
1166 return hoisted_variant;
1168 set {
1169 hoisted_variant = value;
1173 public bool IsThisAssigned (BlockContext ec, Block block)
1175 if (VariableInfo == null)
1176 throw new Exception ();
1178 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1179 return true;
1181 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1184 public bool IsAssigned (BlockContext ec)
1186 if (VariableInfo == null)
1187 throw new Exception ();
1189 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1192 public bool Resolve (ResolveContext ec)
1194 if (VariableType != null)
1195 return true;
1197 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1198 if (texpr == null)
1199 return false;
1201 VariableType = texpr.Type;
1203 if (VariableType.IsStatic) {
1204 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1205 return false;
1208 if (VariableType.IsPointer && !ec.IsUnsafe)
1209 Expression.UnsafeError (ec, Location);
1211 return true;
1214 public bool IsConstant {
1215 get { return (flags & Flags.IsConstant) != 0; }
1216 set { flags |= Flags.IsConstant; }
1219 public bool AddressTaken {
1220 get { return (flags & Flags.AddressTaken) != 0; }
1221 set { flags |= Flags.AddressTaken; }
1224 public bool CompilerGenerated {
1225 get { return (flags & Flags.CompilerGenerated) != 0; }
1226 set { flags |= Flags.CompilerGenerated; }
1229 public override string ToString ()
1231 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1232 Name, Type, VariableInfo, Location);
1235 public bool Used {
1236 get { return (flags & Flags.Used) != 0; }
1237 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1240 public bool ReadOnly {
1241 get { return (flags & Flags.ReadOnly) != 0; }
1244 public void SetReadOnlyContext (ReadOnlyContext context)
1246 flags |= Flags.ReadOnly;
1247 ro_context = context;
1250 public string GetReadOnlyContext ()
1252 if (!ReadOnly)
1253 throw new InternalErrorException ("Variable is not readonly");
1255 switch (ro_context) {
1256 case ReadOnlyContext.Fixed:
1257 return "fixed variable";
1258 case ReadOnlyContext.Foreach:
1259 return "foreach iteration variable";
1260 case ReadOnlyContext.Using:
1261 return "using variable";
1263 throw new NotImplementedException ();
1267 // Whether the variable is pinned, if Pinned the variable has been
1268 // allocated in a pinned slot with DeclareLocal.
1270 public bool Pinned {
1271 get { return (flags & Flags.Pinned) != 0; }
1272 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1275 public bool IsThis {
1276 get { return (flags & Flags.IsThis) != 0; }
1277 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1280 Block IKnownVariable.Block {
1281 get { return Block; }
1284 Location IKnownVariable.Location {
1285 get { return Location; }
1288 public LocalInfo Clone (CloneContext clonectx)
1291 // Variables in anonymous block are not resolved yet
1293 if (VariableType == null)
1294 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1297 // Variables in method block are resolved
1299 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1300 li.VariableType = VariableType;
1301 return li;
1305 /// <summary>
1306 /// Block represents a C# block.
1307 /// </summary>
1309 /// <remarks>
1310 /// This class is used in a number of places: either to represent
1311 /// explicit blocks that the programmer places or implicit blocks.
1313 /// Implicit blocks are used as labels or to introduce variable
1314 /// declarations.
1316 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1317 /// they contain extra information that is not necessary on normal blocks.
1318 /// </remarks>
1319 public class Block : Statement {
1320 public Block Parent;
1321 public Location StartLocation;
1322 public Location EndLocation = Location.Null;
1324 public ExplicitBlock Explicit;
1325 public ToplevelBlock Toplevel; // TODO: Use Explicit
1327 [Flags]
1328 public enum Flags
1330 Unchecked = 1,
1331 BlockUsed = 2,
1332 VariablesInitialized = 4,
1333 HasRet = 8,
1334 Unsafe = 16,
1335 IsIterator = 32,
1336 HasCapturedVariable = 64,
1337 HasCapturedThis = 1 << 7,
1338 IsExpressionTree = 1 << 8
1341 protected Flags flags;
1343 public bool Unchecked {
1344 get { return (flags & Flags.Unchecked) != 0; }
1345 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1348 public bool Unsafe {
1349 get { return (flags & Flags.Unsafe) != 0; }
1350 set { flags |= Flags.Unsafe; }
1354 // The statements in this block
1356 protected List<Statement> statements;
1359 // An array of Blocks. We keep track of children just
1360 // to generate the local variable declarations.
1362 // Statements and child statements are handled through the
1363 // statements.
1365 List<Block> children;
1368 // Labels. (label, block) pairs.
1370 protected Dictionary<string, LabeledStatement> labels;
1373 // Keeps track of (name, type) pairs
1375 Dictionary<string, LocalInfo> variables;
1378 // Keeps track of constants
1379 Dictionary<string, Expression> constants;
1382 // Temporary variables.
1384 List<LocalInfo> temporary_variables;
1387 // If this is a switch section, the enclosing switch block.
1389 Block switch_block;
1391 protected List<Statement> scope_initializers;
1393 List<ToplevelBlock> anonymous_children;
1395 int? resolving_init_idx;
1397 protected static int id;
1399 int this_id;
1401 int assignable_slots;
1402 bool unreachable_shown;
1403 bool unreachable;
1405 public Block (Block parent)
1406 : this (parent, (Flags) 0, Location.Null, Location.Null)
1409 public Block (Block parent, Flags flags)
1410 : this (parent, flags, Location.Null, Location.Null)
1413 public Block (Block parent, Location start, Location end)
1414 : this (parent, (Flags) 0, start, end)
1418 // Useful when TopLevel block is downgraded to normal block
1420 public Block (ToplevelBlock parent, ToplevelBlock source)
1421 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1423 statements = source.statements;
1424 children = source.children;
1425 labels = source.labels;
1426 variables = source.variables;
1427 constants = source.constants;
1428 switch_block = source.switch_block;
1431 public Block (Block parent, Flags flags, Location start, Location end)
1433 if (parent != null) {
1434 parent.AddChild (this);
1436 // the appropriate constructors will fixup these fields
1437 Toplevel = parent.Toplevel;
1438 Explicit = parent.Explicit;
1441 this.Parent = parent;
1442 this.flags = flags;
1443 this.StartLocation = start;
1444 this.EndLocation = end;
1445 this.loc = start;
1446 this_id = id++;
1447 statements = new List<Statement> (4);
1450 public Block CreateSwitchBlock (Location start)
1452 // FIXME: should this be implicit?
1453 Block new_block = new ExplicitBlock (this, start, start);
1454 new_block.switch_block = this;
1455 return new_block;
1458 public int ID {
1459 get { return this_id; }
1462 public IDictionary<string, LocalInfo> Variables {
1463 get {
1464 if (variables == null)
1465 variables = new Dictionary<string, LocalInfo> ();
1466 return variables;
1470 void AddChild (Block b)
1472 if (children == null)
1473 children = new List<Block> (1);
1475 children.Add (b);
1478 public void SetEndLocation (Location loc)
1480 EndLocation = loc;
1483 protected void Error_158 (string name, Location loc)
1485 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1486 "by the same name in a contained scope", name);
1489 /// <summary>
1490 /// Adds a label to the current block.
1491 /// </summary>
1493 /// <returns>
1494 /// false if the name already exists in this block. true
1495 /// otherwise.
1496 /// </returns>
1498 public bool AddLabel (LabeledStatement target)
1500 if (switch_block != null)
1501 return switch_block.AddLabel (target);
1503 string name = target.Name;
1505 Block cur = this;
1506 while (cur != null) {
1507 LabeledStatement s = cur.DoLookupLabel (name);
1508 if (s != null) {
1509 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1510 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1511 return false;
1514 if (this == Explicit)
1515 break;
1517 cur = cur.Parent;
1520 while (cur != null) {
1521 if (cur.DoLookupLabel (name) != null) {
1522 Error_158 (name, target.loc);
1523 return false;
1526 if (children != null) {
1527 foreach (Block b in children) {
1528 LabeledStatement s = b.DoLookupLabel (name);
1529 if (s == null)
1530 continue;
1532 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1533 Error_158 (name, target.loc);
1534 return false;
1538 cur = cur.Parent;
1541 Toplevel.CheckError158 (name, target.loc);
1543 if (labels == null)
1544 labels = new Dictionary<string, LabeledStatement> ();
1546 labels.Add (name, target);
1547 return true;
1550 public LabeledStatement LookupLabel (string name)
1552 LabeledStatement s = DoLookupLabel (name);
1553 if (s != null)
1554 return s;
1556 if (children == null)
1557 return null;
1559 foreach (Block child in children) {
1560 if (Explicit != child.Explicit)
1561 continue;
1563 s = child.LookupLabel (name);
1564 if (s != null)
1565 return s;
1568 return null;
1571 LabeledStatement DoLookupLabel (string name)
1573 if (switch_block != null)
1574 return switch_block.LookupLabel (name);
1576 if (labels != null)
1577 if (labels.ContainsKey (name))
1578 return labels [name];
1580 return null;
1583 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1585 Block b = this;
1586 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1587 while (kvi == null) {
1588 b = b.Explicit.Parent;
1589 if (b == null)
1590 return true;
1591 kvi = b.Explicit.GetKnownVariable (name);
1594 if (kvi.Block == b)
1595 return true;
1597 // Is kvi.Block nested inside 'b'
1598 if (b.Explicit != kvi.Block.Explicit) {
1600 // If a variable by the same name it defined in a nested block of this
1601 // block, we violate the invariant meaning in a block.
1603 if (b == this) {
1604 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1605 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1606 return false;
1610 // It's ok if the definition is in a nested subblock of b, but not
1611 // nested inside this block -- a definition in a sibling block
1612 // should not affect us.
1614 return true;
1618 // Block 'b' and kvi.Block are the same textual block.
1619 // However, different variables are extant.
1621 // Check if the variable is in scope in both blocks. We use
1622 // an indirect check that depends on AddVariable doing its
1623 // part in maintaining the invariant-meaning-in-block property.
1625 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1626 return true;
1628 if (this is ToplevelBlock) {
1629 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1630 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1631 return false;
1635 // Even though we detected the error when the name is used, we
1636 // treat it as if the variable declaration was in error.
1638 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1639 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1640 return false;
1643 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1645 LocalInfo vi = GetLocalInfo (name);
1646 if (vi != null) {
1647 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1648 if (Explicit == vi.Block.Explicit) {
1649 Error_AlreadyDeclared (l, name, null);
1650 } else {
1651 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1652 "parent or current" : "parent");
1654 return false;
1657 if (block != null) {
1658 Expression e = block.GetParameterReference (name, Location.Null);
1659 if (e != null) {
1660 ParameterReference pr = e as ParameterReference;
1661 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1662 Error_AlreadyDeclared (loc, name);
1663 else
1664 Error_AlreadyDeclared (loc, name, "parent or current");
1665 return false;
1669 return true;
1672 public LocalInfo AddVariable (Expression type, string name, Location l)
1674 if (!CheckParentConflictName (Toplevel, name, l))
1675 return null;
1677 if (Toplevel.GenericMethod != null) {
1678 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1679 if (tp.Name == name) {
1680 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1681 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1682 return null;
1687 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1688 if (kvi != null) {
1689 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1690 Error_AlreadyDeclared (l, name, "child");
1691 return null;
1694 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1695 AddVariable (vi);
1697 if ((flags & Flags.VariablesInitialized) != 0)
1698 throw new InternalErrorException ("block has already been resolved");
1700 return vi;
1703 protected virtual void AddVariable (LocalInfo li)
1705 Variables.Add (li.Name, li);
1706 Explicit.AddKnownVariable (li.Name, li);
1709 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1711 if (reason == null) {
1712 Error_AlreadyDeclared (loc, var);
1713 return;
1716 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1717 "in this scope because it would give a different meaning " +
1718 "to `{0}', which is already used in a `{1}' scope " +
1719 "to denote something else", var, reason);
1722 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1724 Toplevel.Report.Error (128, loc,
1725 "A local variable named `{0}' is already defined in this scope", name);
1728 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1730 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1731 name, conflict);
1734 public bool AddConstant (Expression type, string name, Expression value, Location l)
1736 if (AddVariable (type, name, l) == null)
1737 return false;
1739 if (constants == null)
1740 constants = new Dictionary<string, Expression> ();
1742 constants.Add (name, value);
1744 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1745 Use ();
1746 return true;
1749 static int next_temp_id = 0;
1751 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1753 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1755 if (temporary_variables == null)
1756 temporary_variables = new List<LocalInfo> ();
1758 int id = ++next_temp_id;
1759 string name = "$s_" + id.ToString ();
1761 LocalInfo li = new LocalInfo (te, name, this, loc);
1762 li.CompilerGenerated = true;
1763 temporary_variables.Add (li);
1764 return li;
1767 public LocalInfo GetLocalInfo (string name)
1769 LocalInfo ret;
1770 for (Block b = this; b != null; b = b.Parent) {
1771 if (b.variables != null) {
1772 if (b.variables.TryGetValue (name, out ret))
1773 return ret;
1777 return null;
1780 public Expression GetVariableType (string name)
1782 LocalInfo vi = GetLocalInfo (name);
1783 return vi == null ? null : vi.Type;
1786 public Expression GetConstantExpression (string name)
1788 Expression ret;
1789 for (Block b = this; b != null; b = b.Parent) {
1790 if (b.constants != null) {
1791 if (b.constants.TryGetValue (name, out ret))
1792 return ret;
1795 return null;
1799 // It should be used by expressions which require to
1800 // register a statement during resolve process.
1802 public void AddScopeStatement (Statement s)
1804 if (scope_initializers == null)
1805 scope_initializers = new List<Statement> ();
1808 // Simple recursive helper, when resolve scope initializer another
1809 // new scope initializer can be added, this ensures it's initialized
1810 // before existing one. For now this can happen with expression trees
1811 // in base ctor initializer only
1813 if (resolving_init_idx.HasValue) {
1814 scope_initializers.Insert (resolving_init_idx.Value, s);
1815 ++resolving_init_idx;
1816 } else {
1817 scope_initializers.Add (s);
1821 public void AddStatement (Statement s)
1823 statements.Add (s);
1824 flags |= Flags.BlockUsed;
1827 public bool Used {
1828 get { return (flags & Flags.BlockUsed) != 0; }
1831 public void Use ()
1833 flags |= Flags.BlockUsed;
1836 public bool HasRet {
1837 get { return (flags & Flags.HasRet) != 0; }
1840 public int AssignableSlots {
1841 get {
1842 // TODO: Re-enable
1843 // if ((flags & Flags.VariablesInitialized) == 0)
1844 // throw new Exception ("Variables have not been initialized yet");
1845 return assignable_slots;
1849 public IList<ToplevelBlock> AnonymousChildren {
1850 get { return anonymous_children; }
1853 public void AddAnonymousChild (ToplevelBlock b)
1855 if (anonymous_children == null)
1856 anonymous_children = new List<ToplevelBlock> ();
1858 anonymous_children.Add (b);
1861 void DoResolveConstants (BlockContext ec)
1863 if (constants == null)
1864 return;
1866 if (variables == null)
1867 throw new InternalErrorException ("cannot happen");
1869 foreach (var de in variables) {
1870 string name = de.Key;
1871 LocalInfo vi = de.Value;
1872 TypeSpec variable_type = vi.VariableType;
1874 if (variable_type == null) {
1875 if (vi.Type is VarExpr)
1876 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1878 continue;
1881 Expression cv;
1882 if (!constants.TryGetValue (name, out cv))
1883 continue;
1885 // Don't let 'const int Foo = Foo;' succeed.
1886 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1887 // which in turn causes the 'must be constant' error to be triggered.
1888 constants.Remove (name);
1890 if (!variable_type.IsConstantCompatible) {
1891 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1892 continue;
1895 ec.CurrentBlock = this;
1896 Expression e;
1897 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1898 e = cv.Resolve (ec);
1900 if (e == null)
1901 continue;
1903 Constant ce = e as Constant;
1904 if (ce == null) {
1905 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1906 continue;
1909 e = ce.ConvertImplicitly (ec, variable_type);
1910 if (e == null) {
1911 if (TypeManager.IsReferenceType (variable_type))
1912 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1913 else
1914 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1915 continue;
1918 constants.Add (name, e);
1919 vi.IsConstant = true;
1923 protected void ResolveMeta (BlockContext ec, int offset)
1925 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1927 // If some parent block was unsafe, we remain unsafe even if this block
1928 // isn't explicitly marked as such.
1929 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
1930 flags |= Flags.VariablesInitialized;
1932 if (variables != null) {
1933 foreach (LocalInfo li in variables.Values) {
1934 if (!li.Resolve (ec))
1935 continue;
1936 li.VariableInfo = new VariableInfo (li, offset);
1937 offset += li.VariableInfo.Length;
1940 assignable_slots = offset;
1942 DoResolveConstants (ec);
1944 if (children == null)
1945 return;
1946 foreach (Block b in children)
1947 b.ResolveMeta (ec, offset);
1952 // Emits the local variable declarations for a block
1954 public virtual void EmitMeta (EmitContext ec)
1956 if (variables != null){
1957 foreach (LocalInfo vi in variables.Values)
1958 vi.ResolveVariable (ec);
1961 if (temporary_variables != null) {
1962 for (int i = 0; i < temporary_variables.Count; i++)
1963 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
1966 if (children != null) {
1967 for (int i = 0; i < children.Count; i++)
1968 ((Block)children[i]).EmitMeta(ec);
1972 void UsageWarning (BlockContext ec)
1974 if (variables == null || ec.Report.WarningLevel < 3)
1975 return;
1977 foreach (var de in variables) {
1978 LocalInfo vi = de.Value;
1980 if (!vi.Used) {
1981 string name = de.Key;
1983 // vi.VariableInfo can be null for 'catch' variables
1984 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
1985 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
1986 else
1987 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
1992 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
1994 Statement body;
1996 // Some statements are wrapped by a Block. Since
1997 // others' internal could be changed, here I treat
1998 // them as possibly wrapped by Block equally.
1999 Block b = s as Block;
2000 if (b != null && b.statements.Count == 1)
2001 s = (Statement) b.statements [0];
2003 if (s is Lock)
2004 body = ((Lock) s).Statement;
2005 else if (s is For)
2006 body = ((For) s).Statement;
2007 else if (s is Foreach)
2008 body = ((Foreach) s).Statement;
2009 else if (s is While)
2010 body = ((While) s).Statement;
2011 else if (s is Fixed)
2012 body = ((Fixed) s).Statement;
2013 else if (s is Using)
2014 body = ((Using) s).EmbeddedStatement;
2015 else if (s is UsingTemporary)
2016 body = ((UsingTemporary) s).Statement;
2017 else
2018 return;
2020 if (body == null || body is EmptyStatement)
2021 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2024 public override bool Resolve (BlockContext ec)
2026 Block prev_block = ec.CurrentBlock;
2027 bool ok = true;
2029 int errors = ec.Report.Errors;
2031 ec.CurrentBlock = this;
2032 ec.StartFlowBranching (this);
2034 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2037 // Compiler generated scope statements
2039 if (scope_initializers != null) {
2040 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2041 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2044 resolving_init_idx = null;
2048 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2049 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2050 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2051 // responsible for handling the situation.
2053 int statement_count = statements.Count;
2054 for (int ix = 0; ix < statement_count; ix++){
2055 Statement s = statements [ix];
2056 // Check possible empty statement (CS0642)
2057 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2058 statements [ix + 1] is ExplicitBlock)
2059 CheckPossibleMistakenEmptyStatement (ec, s);
2062 // Warn if we detect unreachable code.
2064 if (unreachable) {
2065 if (s is EmptyStatement)
2066 continue;
2068 if (!unreachable_shown && !(s is LabeledStatement)) {
2069 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2070 unreachable_shown = true;
2073 Block c_block = s as Block;
2074 if (c_block != null)
2075 c_block.unreachable = c_block.unreachable_shown = true;
2079 // Note that we're not using ResolveUnreachable() for unreachable
2080 // statements here. ResolveUnreachable() creates a temporary
2081 // flow branching and kills it afterwards. This leads to problems
2082 // if you have two unreachable statements where the first one
2083 // assigns a variable and the second one tries to access it.
2086 if (!s.Resolve (ec)) {
2087 ok = false;
2088 if (ec.IsInProbingMode)
2089 break;
2091 statements [ix] = new EmptyStatement (s.loc);
2092 continue;
2095 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2096 statements [ix] = new EmptyStatement (s.loc);
2098 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2099 if (unreachable && s is LabeledStatement)
2100 throw new InternalErrorException ("should not happen");
2103 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2104 ec.CurrentBranching, statement_count);
2106 while (ec.CurrentBranching is FlowBranchingLabeled)
2107 ec.EndFlowBranching ();
2109 bool flow_unreachable = ec.EndFlowBranching ();
2111 ec.CurrentBlock = prev_block;
2113 if (flow_unreachable)
2114 flags |= Flags.HasRet;
2116 // If we're a non-static `struct' constructor which doesn't have an
2117 // initializer, then we must initialize all of the struct's fields.
2118 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2119 ok = false;
2121 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2122 foreach (LabeledStatement label in labels.Values)
2123 if (!label.HasBeenReferenced)
2124 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2127 if (ok && errors == ec.Report.Errors)
2128 UsageWarning (ec);
2130 return ok;
2133 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2135 unreachable_shown = true;
2136 unreachable = true;
2138 if (warn)
2139 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2141 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2142 bool ok = Resolve (ec);
2143 ec.KillFlowBranching ();
2145 return ok;
2148 protected override void DoEmit (EmitContext ec)
2150 for (int ix = 0; ix < statements.Count; ix++){
2151 Statement s = (Statement) statements [ix];
2152 s.Emit (ec);
2156 public override void Emit (EmitContext ec)
2158 if (scope_initializers != null)
2159 EmitScopeInitializers (ec);
2161 ec.Mark (StartLocation);
2162 DoEmit (ec);
2164 if (SymbolWriter.HasSymbolWriter)
2165 EmitSymbolInfo (ec);
2168 protected void EmitScopeInitializers (EmitContext ec)
2170 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2172 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2173 foreach (Statement s in scope_initializers)
2174 s.Emit (ec);
2177 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2180 protected virtual void EmitSymbolInfo (EmitContext ec)
2182 if (variables != null) {
2183 foreach (LocalInfo vi in variables.Values) {
2184 vi.EmitSymbolInfo (ec);
2189 public override string ToString ()
2191 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2194 protected override void CloneTo (CloneContext clonectx, Statement t)
2196 Block target = (Block) t;
2198 clonectx.AddBlockMap (this, target);
2200 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2201 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2202 if (Parent != null)
2203 target.Parent = clonectx.RemapBlockCopy (Parent);
2205 if (variables != null){
2206 target.variables = new Dictionary<string, LocalInfo> ();
2208 foreach (var de in variables){
2209 LocalInfo newlocal = de.Value.Clone (clonectx);
2210 target.variables [de.Key] = newlocal;
2211 clonectx.AddVariableMap (de.Value, newlocal);
2215 target.statements = new List<Statement> (statements.Count);
2216 foreach (Statement s in statements)
2217 target.statements.Add (s.Clone (clonectx));
2219 if (target.children != null){
2220 target.children = new List<Block> (children.Count);
2221 foreach (Block b in children){
2222 target.children.Add (clonectx.LookupBlock (b));
2227 // TODO: labels, switch_block, constants (?), anonymous_children
2232 public class ExplicitBlock : Block
2234 Dictionary<string, IKnownVariable> known_variables;
2235 protected AnonymousMethodStorey am_storey;
2237 public ExplicitBlock (Block parent, Location start, Location end)
2238 : this (parent, (Flags) 0, start, end)
2242 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2243 : base (parent, flags, start, end)
2245 this.Explicit = this;
2248 // <summary>
2249 // Marks a variable with name @name as being used in this or a child block.
2250 // If a variable name has been used in a child block, it's illegal to
2251 // declare a variable with the same name in the current block.
2252 // </summary>
2253 internal void AddKnownVariable (string name, IKnownVariable info)
2255 if (known_variables == null)
2256 known_variables = new Dictionary<string, IKnownVariable> ();
2258 known_variables [name] = info;
2260 if (Parent != null)
2261 Parent.Explicit.AddKnownVariable (name, info);
2264 public AnonymousMethodStorey AnonymousMethodStorey {
2265 get { return am_storey; }
2269 // Creates anonymous method storey in current block
2271 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2274 // When referencing a variable in iterator storey from children anonymous method
2276 if (Toplevel.am_storey is IteratorStorey) {
2277 return Toplevel.am_storey;
2281 // An iterator has only 1 storey block
2283 if (ec.CurrentIterator != null)
2284 return ec.CurrentIterator.Storey;
2286 if (am_storey == null) {
2287 MemberBase mc = ec.MemberContext as MemberBase;
2288 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2291 // Creates anonymous method storey for this block
2293 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2296 return am_storey;
2299 public override void Emit (EmitContext ec)
2301 if (am_storey != null)
2302 am_storey.EmitStoreyInstantiation (ec);
2304 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2305 if (emit_debug_info)
2306 ec.BeginScope ();
2308 base.Emit (ec);
2310 if (emit_debug_info)
2311 ec.EndScope ();
2314 public override void EmitMeta (EmitContext ec)
2317 // Creates anonymous method storey
2319 if (am_storey != null) {
2320 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2322 // Creates parent storey reference when hoisted this is accessible
2324 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2325 ExplicitBlock parent = Toplevel.Parent.Explicit;
2328 // Hoisted this exists in top-level parent storey only
2330 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2331 parent = parent.Parent.Explicit;
2333 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2336 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2338 // TODO MemberCache: Review
2339 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2342 am_storey.CreateType ();
2343 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2344 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2346 am_storey.DefineType ();
2347 am_storey.ResolveTypeParameters ();
2348 am_storey.Define ();
2349 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2351 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2352 if (ref_blocks != null) {
2353 foreach (ExplicitBlock ref_block in ref_blocks) {
2354 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2355 if (b.am_storey != null) {
2356 b.am_storey.AddParentStoreyReference (ec, am_storey);
2358 // Stop propagation inside same top block
2359 if (b.Toplevel == Toplevel)
2360 break;
2362 b = b.Toplevel;
2364 b.HasCapturedVariable = true;
2370 base.EmitMeta (ec);
2373 internal IKnownVariable GetKnownVariable (string name)
2375 if (known_variables == null)
2376 return null;
2378 IKnownVariable kw;
2379 if (!known_variables.TryGetValue (name, out kw))
2380 return null;
2382 return kw;
2385 public bool HasCapturedThis
2387 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2388 get { return (flags & Flags.HasCapturedThis) != 0; }
2391 public bool HasCapturedVariable
2393 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2394 get { return (flags & Flags.HasCapturedVariable) != 0; }
2397 protected override void CloneTo (CloneContext clonectx, Statement t)
2399 ExplicitBlock target = (ExplicitBlock) t;
2400 target.known_variables = null;
2401 base.CloneTo (clonectx, t);
2405 public class ToplevelParameterInfo : IKnownVariable {
2406 public readonly ToplevelBlock Block;
2407 public readonly int Index;
2408 public VariableInfo VariableInfo;
2410 Block IKnownVariable.Block {
2411 get { return Block; }
2413 public Parameter Parameter {
2414 get { return Block.Parameters [Index]; }
2417 public TypeSpec ParameterType {
2418 get { return Block.Parameters.Types [Index]; }
2421 public Location Location {
2422 get { return Parameter.Location; }
2425 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2427 this.Block = block;
2428 this.Index = idx;
2433 // A toplevel block contains extra information, the split is done
2434 // only to separate information that would otherwise bloat the more
2435 // lightweight Block.
2437 // In particular, this was introduced when the support for Anonymous
2438 // Methods was implemented.
2440 public class ToplevelBlock : ExplicitBlock
2443 // Block is converted to an expression
2445 sealed class BlockScopeExpression : Expression
2447 Expression child;
2448 readonly ToplevelBlock block;
2450 public BlockScopeExpression (Expression child, ToplevelBlock block)
2452 this.child = child;
2453 this.block = block;
2456 public override Expression CreateExpressionTree (ResolveContext ec)
2458 throw new NotSupportedException ();
2461 protected override Expression DoResolve (ResolveContext ec)
2463 if (child == null)
2464 return null;
2466 child = child.Resolve (ec);
2467 if (child == null)
2468 return null;
2470 eclass = child.eclass;
2471 type = child.Type;
2472 return this;
2475 public override void Emit (EmitContext ec)
2477 block.EmitMeta (ec);
2478 block.EmitScopeInitializers (ec);
2479 child.Emit (ec);
2483 GenericMethod generic;
2484 protected ParametersCompiled parameters;
2485 ToplevelParameterInfo[] parameter_info;
2486 LocalInfo this_variable;
2487 bool resolved;
2488 bool unreachable;
2489 CompilerContext compiler;
2491 public HoistedVariable HoistedThisVariable;
2493 public bool Resolved {
2494 get {
2495 return resolved;
2500 // The parameters for the block.
2502 public ParametersCompiled Parameters {
2503 get { return parameters; }
2506 public Report Report {
2507 get { return compiler.Report; }
2510 public GenericMethod GenericMethod {
2511 get { return generic; }
2514 public ToplevelBlock Container {
2515 get { return Parent == null ? null : Parent.Toplevel; }
2518 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2519 this (ctx, parent, (Flags) 0, parameters, start)
2523 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2524 this (ctx, parent, parameters, start)
2526 this.generic = generic;
2529 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2530 this (ctx, null, (Flags) 0, parameters, start)
2534 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2535 this (ctx, null, flags, parameters, start)
2539 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2540 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2541 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2542 base (null, flags, start, Location.Null)
2544 this.compiler = ctx;
2545 this.Toplevel = this;
2547 this.parameters = parameters;
2548 this.Parent = parent;
2549 if (parent != null)
2550 parent.AddAnonymousChild (this);
2552 if (!this.parameters.IsEmpty)
2553 ProcessParameters ();
2556 public ToplevelBlock (CompilerContext ctx, Location loc)
2557 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2561 protected override void CloneTo (CloneContext clonectx, Statement t)
2563 ToplevelBlock target = (ToplevelBlock) t;
2564 base.CloneTo (clonectx, t);
2566 if (parameters.Count != 0)
2567 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2568 for (int i = 0; i < parameters.Count; ++i)
2569 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2572 public bool CheckError158 (string name, Location loc)
2574 if (AnonymousChildren != null) {
2575 foreach (ToplevelBlock child in AnonymousChildren) {
2576 if (!child.CheckError158 (name, loc))
2577 return false;
2581 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2582 if (!c.DoCheckError158 (name, loc))
2583 return false;
2586 return true;
2589 void ProcessParameters ()
2591 int n = parameters.Count;
2592 parameter_info = new ToplevelParameterInfo [n];
2593 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2594 for (int i = 0; i < n; ++i) {
2595 parameter_info [i] = new ToplevelParameterInfo (this, i);
2597 Parameter p = parameters [i];
2598 if (p == null)
2599 continue;
2601 string name = p.Name;
2602 if (CheckParentConflictName (top_parent, name, loc))
2603 AddKnownVariable (name, parameter_info [i]);
2606 // mark this block as "used" so that we create local declarations in a sub-block
2607 // FIXME: This appears to uncover a lot of bugs
2608 //this.Use ();
2611 bool DoCheckError158 (string name, Location loc)
2613 LabeledStatement s = LookupLabel (name);
2614 if (s != null) {
2615 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2616 Error_158 (name, loc);
2617 return false;
2620 return true;
2623 public override Expression CreateExpressionTree (ResolveContext ec)
2625 if (statements.Count == 1) {
2626 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2627 if (scope_initializers != null)
2628 expr = new BlockScopeExpression (expr, this);
2630 return expr;
2633 return base.CreateExpressionTree (ec);
2637 // Reformats this block to be top-level iterator block
2639 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2641 IsIterator = true;
2643 // Creates block with original statements
2644 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2646 source.statements = new List<Statement> (1);
2647 source.AddStatement (new Return (iterator, iterator.Location));
2648 source.IsIterator = false;
2650 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2651 source.am_storey = iterator_storey;
2652 return iterator_storey;
2656 // Returns a parameter reference expression for the given name,
2657 // or null if there is no such parameter
2659 public Expression GetParameterReference (string name, Location loc)
2661 for (ToplevelBlock t = this; t != null; t = t.Container) {
2662 Expression expr = t.GetParameterReferenceExpression (name, loc);
2663 if (expr != null)
2664 return expr;
2667 return null;
2670 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2672 int idx = parameters.GetParameterIndexByName (name);
2673 return idx < 0 ?
2674 null : new ParameterReference (parameter_info [idx], loc);
2677 // <summary>
2678 // Returns the "this" instance variable of this block.
2679 // See AddThisVariable() for more information.
2680 // </summary>
2681 public LocalInfo ThisVariable {
2682 get { return this_variable; }
2685 // <summary>
2686 // This is used by non-static `struct' constructors which do not have an
2687 // initializer - in this case, the constructor must initialize all of the
2688 // struct's fields. To do this, we add a "this" variable and use the flow
2689 // analysis code to ensure that it's been fully initialized before control
2690 // leaves the constructor.
2691 // </summary>
2692 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2694 if (this_variable == null) {
2695 this_variable = new LocalInfo (ds, this, l);
2696 this_variable.Used = true;
2697 this_variable.IsThis = true;
2699 Variables.Add ("this", this_variable);
2702 return this_variable;
2705 public bool IsIterator {
2706 get { return (flags & Flags.IsIterator) != 0; }
2707 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2711 // Block has been converted to expression tree
2713 public bool IsExpressionTree {
2714 get { return (flags & Flags.IsExpressionTree) != 0; }
2717 public bool IsThisAssigned (BlockContext ec)
2719 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2722 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2724 if (resolved)
2725 return true;
2727 resolved = true;
2729 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2730 flags |= Flags.IsExpressionTree;
2732 try {
2733 if (!ResolveMeta (rc, ip))
2734 return false;
2736 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2737 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2739 if (!Resolve (rc))
2740 return false;
2742 unreachable = top_level.End ();
2744 } catch (Exception e) {
2745 if (rc.CurrentBlock != null) {
2746 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2747 } else {
2748 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2751 if (Report.DebugFlags > 0)
2752 throw;
2755 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2756 if (rc.CurrentAnonymousMethod == null) {
2757 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2758 return false;
2759 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2760 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2761 rc.CurrentAnonymousMethod.GetSignatureForError ());
2762 return false;
2766 return true;
2769 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2771 int errors = ec.Report.Errors;
2772 int orig_count = parameters.Count;
2774 if (ip != null)
2775 parameters = ip;
2777 // Assert: orig_count != parameter.Count => orig_count == 0
2778 if (orig_count != 0 && orig_count != parameters.Count)
2779 throw new InternalErrorException ("parameter information mismatch");
2781 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2783 for (int i = 0; i < orig_count; ++i) {
2784 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2786 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2787 continue;
2789 VariableInfo vi = new VariableInfo (ip, i, offset);
2790 parameter_info [i].VariableInfo = vi;
2791 offset += vi.Length;
2794 ResolveMeta (ec, offset);
2796 return ec.Report.Errors == errors;
2799 // <summary>
2800 // Check whether all `out' parameters have been assigned.
2801 // </summary>
2802 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2804 if (vector.IsUnreachable)
2805 return;
2807 int n = parameter_info == null ? 0 : parameter_info.Length;
2809 for (int i = 0; i < n; i++) {
2810 VariableInfo var = parameter_info [i].VariableInfo;
2812 if (var == null)
2813 continue;
2815 if (vector.IsAssigned (var, false))
2816 continue;
2818 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2819 var.Name);
2823 public override void Emit (EmitContext ec)
2825 if (Report.Errors > 0)
2826 return;
2828 #if PRODUCTION
2829 try {
2830 #endif
2831 EmitMeta (ec);
2833 if (ec.HasReturnLabel)
2834 ec.ReturnLabel = ec.DefineLabel ();
2836 base.Emit (ec);
2838 ec.Mark (EndLocation);
2840 if (ec.HasReturnLabel)
2841 ec.MarkLabel (ec.ReturnLabel);
2843 if (ec.return_value != null) {
2844 ec.Emit (OpCodes.Ldloc, ec.return_value);
2845 ec.Emit (OpCodes.Ret);
2846 } else {
2848 // If `HasReturnLabel' is set, then we already emitted a
2849 // jump to the end of the method, so we must emit a `ret'
2850 // there.
2852 // Unfortunately, System.Reflection.Emit automatically emits
2853 // a leave to the end of a finally block. This is a problem
2854 // if no code is following the try/finally block since we may
2855 // jump to a point after the end of the method.
2856 // As a workaround, we're always creating a return label in
2857 // this case.
2860 if (ec.HasReturnLabel || !unreachable) {
2861 if (ec.ReturnType != TypeManager.void_type)
2862 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2863 ec.Emit (OpCodes.Ret);
2867 #if PRODUCTION
2868 } catch (Exception e){
2869 Console.WriteLine ("Exception caught by the compiler while emitting:");
2870 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2872 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2873 throw;
2875 #endif
2878 public override void EmitMeta (EmitContext ec)
2880 // Avoid declaring an IL variable for this_variable since it is not accessed
2881 // from the generated IL
2882 if (this_variable != null)
2883 Variables.Remove ("this");
2884 base.EmitMeta (ec);
2887 protected override void EmitSymbolInfo (EmitContext ec)
2889 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2890 if ((ae != null) && (ae.Storey != null))
2891 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2893 base.EmitSymbolInfo (ec);
2897 public class SwitchLabel {
2898 Expression label;
2899 object converted;
2900 Location loc;
2902 Label il_label;
2903 bool il_label_set;
2904 Label il_label_code;
2905 bool il_label_code_set;
2907 public static readonly object NullStringCase = new object ();
2910 // if expr == null, then it is the default case.
2912 public SwitchLabel (Expression expr, Location l)
2914 label = expr;
2915 loc = l;
2918 public Expression Label {
2919 get {
2920 return label;
2924 public Location Location {
2925 get { return loc; }
2928 public object Converted {
2929 get {
2930 return converted;
2934 public Label GetILLabel (EmitContext ec)
2936 if (!il_label_set){
2937 il_label = ec.DefineLabel ();
2938 il_label_set = true;
2940 return il_label;
2943 public Label GetILLabelCode (EmitContext ec)
2945 if (!il_label_code_set){
2946 il_label_code = ec.DefineLabel ();
2947 il_label_code_set = true;
2949 return il_label_code;
2953 // Resolves the expression, reduces it to a literal if possible
2954 // and then converts it to the requested type.
2956 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
2958 Expression e = label.Resolve (ec);
2960 if (e == null)
2961 return false;
2963 Constant c = e as Constant;
2964 if (c == null){
2965 ec.Report.Error (150, loc, "A constant value is expected");
2966 return false;
2969 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2970 converted = NullStringCase;
2971 return true;
2974 if (allow_nullable && c.GetValue () == null) {
2975 converted = NullStringCase;
2976 return true;
2979 c = c.ImplicitConversionRequired (ec, required_type, loc);
2980 if (c == null)
2981 return false;
2983 converted = c.GetValue ();
2984 return true;
2987 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
2989 string label;
2990 if (converted == null)
2991 label = "default";
2992 else if (converted == NullStringCase)
2993 label = "null";
2994 else
2995 label = converted.ToString ();
2997 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2998 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3001 public SwitchLabel Clone (CloneContext clonectx)
3003 return new SwitchLabel (label.Clone (clonectx), loc);
3007 public class SwitchSection {
3008 // An array of SwitchLabels.
3009 public readonly List<SwitchLabel> Labels;
3010 public readonly Block Block;
3012 public SwitchSection (List<SwitchLabel> labels, Block block)
3014 Labels = labels;
3015 Block = block;
3018 public SwitchSection Clone (CloneContext clonectx)
3020 var cloned_labels = new List<SwitchLabel> ();
3022 foreach (SwitchLabel sl in cloned_labels)
3023 cloned_labels.Add (sl.Clone (clonectx));
3025 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3029 public class Switch : Statement {
3030 public List<SwitchSection> Sections;
3031 public Expression Expr;
3033 /// <summary>
3034 /// Maps constants whose type type SwitchType to their SwitchLabels.
3035 /// </summary>
3036 public IDictionary<object, SwitchLabel> Elements;
3038 /// <summary>
3039 /// The governing switch type
3040 /// </summary>
3041 public TypeSpec SwitchType;
3044 // Computed
3046 Label default_target;
3047 Label null_target;
3048 Expression new_expr;
3049 bool is_constant;
3050 bool has_null_case;
3051 SwitchSection constant_section;
3052 SwitchSection default_section;
3054 ExpressionStatement string_dictionary;
3055 FieldExpr switch_cache_field;
3056 static int unique_counter;
3059 // Nullable Types support
3061 Nullable.Unwrap unwrap;
3063 protected bool HaveUnwrap {
3064 get { return unwrap != null; }
3068 // The types allowed to be implicitly cast from
3069 // on the governing type
3071 static TypeSpec [] allowed_types;
3073 public Switch (Expression e, List<SwitchSection> sects, Location l)
3075 Expr = e;
3076 Sections = sects;
3077 loc = l;
3080 public bool GotDefault {
3081 get {
3082 return default_section != null;
3086 public Label DefaultTarget {
3087 get {
3088 return default_target;
3093 // Determines the governing type for a switch. The returned
3094 // expression might be the expression from the switch, or an
3095 // expression that includes any potential conversions to the
3096 // integral types or to string.
3098 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3100 TypeSpec t = expr.Type;
3102 if (t == TypeManager.byte_type ||
3103 t == TypeManager.sbyte_type ||
3104 t == TypeManager.ushort_type ||
3105 t == TypeManager.short_type ||
3106 t == TypeManager.uint32_type ||
3107 t == TypeManager.int32_type ||
3108 t == TypeManager.uint64_type ||
3109 t == TypeManager.int64_type ||
3110 t == TypeManager.char_type ||
3111 t == TypeManager.string_type ||
3112 t == TypeManager.bool_type ||
3113 TypeManager.IsEnumType (t))
3114 return expr;
3116 if (allowed_types == null){
3117 allowed_types = new TypeSpec [] {
3118 TypeManager.sbyte_type,
3119 TypeManager.byte_type,
3120 TypeManager.short_type,
3121 TypeManager.ushort_type,
3122 TypeManager.int32_type,
3123 TypeManager.uint32_type,
3124 TypeManager.int64_type,
3125 TypeManager.uint64_type,
3126 TypeManager.char_type,
3127 TypeManager.string_type
3132 // Try to find a *user* defined implicit conversion.
3134 // If there is no implicit conversion, or if there are multiple
3135 // conversions, we have to report an error
3137 Expression converted = null;
3138 foreach (TypeSpec tt in allowed_types){
3139 Expression e;
3141 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3142 if (e == null)
3143 continue;
3146 // Ignore over-worked ImplicitUserConversions that do
3147 // an implicit conversion in addition to the user conversion.
3149 if (!(e is UserCast))
3150 continue;
3152 if (converted != null){
3153 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3154 return null;
3157 converted = e;
3159 return converted;
3163 // Performs the basic sanity checks on the switch statement
3164 // (looks for duplicate keys and non-constant expressions).
3166 // It also returns a hashtable with the keys that we will later
3167 // use to compute the switch tables
3169 bool CheckSwitch (ResolveContext ec)
3171 bool error = false;
3172 Elements = new Dictionary<object, SwitchLabel> ();
3174 foreach (SwitchSection ss in Sections){
3175 foreach (SwitchLabel sl in ss.Labels){
3176 if (sl.Label == null){
3177 if (default_section != null){
3178 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3179 error = true;
3181 default_section = ss;
3182 continue;
3185 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3186 error = true;
3187 continue;
3190 object key = sl.Converted;
3191 if (key == SwitchLabel.NullStringCase)
3192 has_null_case = true;
3194 try {
3195 Elements.Add (key, sl);
3196 } catch (ArgumentException) {
3197 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3198 error = true;
3202 return !error;
3205 void EmitObjectInteger (EmitContext ec, object k)
3207 if (k is int)
3208 ec.EmitInt ((int) k);
3209 else if (k is Constant) {
3210 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3212 else if (k is uint)
3213 ec.EmitInt (unchecked ((int) (uint) k));
3214 else if (k is long)
3216 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3218 ec.EmitInt ((int) (long) k);
3219 ec.Emit (OpCodes.Conv_I8);
3221 else
3222 ec.EmitLong ((long) k);
3224 else if (k is ulong)
3226 ulong ul = (ulong) k;
3227 if (ul < (1L<<32))
3229 ec.EmitInt (unchecked ((int) ul));
3230 ec.Emit (OpCodes.Conv_U8);
3232 else
3234 ec.EmitLong (unchecked ((long) ul));
3237 else if (k is char)
3238 ec.EmitInt ((int) ((char) k));
3239 else if (k is sbyte)
3240 ec.EmitInt ((int) ((sbyte) k));
3241 else if (k is byte)
3242 ec.EmitInt ((int) ((byte) k));
3243 else if (k is short)
3244 ec.EmitInt ((int) ((short) k));
3245 else if (k is ushort)
3246 ec.EmitInt ((int) ((ushort) k));
3247 else if (k is bool)
3248 ec.EmitInt (((bool) k) ? 1 : 0);
3249 else
3250 throw new Exception ("Unhandled case");
3253 // structure used to hold blocks of keys while calculating table switch
3254 class KeyBlock : IComparable
3256 public KeyBlock (long _first)
3258 first = last = _first;
3260 public long first;
3261 public long last;
3262 public List<object> element_keys;
3263 // how many items are in the bucket
3264 public int Size = 1;
3265 public int Length
3267 get { return (int) (last - first + 1); }
3269 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3271 return kb_last.last - kb_first.first + 1;
3273 public int CompareTo (object obj)
3275 KeyBlock kb = (KeyBlock) obj;
3276 int nLength = Length;
3277 int nLengthOther = kb.Length;
3278 if (nLengthOther == nLength)
3279 return (int) (kb.first - first);
3280 return nLength - nLengthOther;
3284 /// <summary>
3285 /// This method emits code for a lookup-based switch statement (non-string)
3286 /// Basically it groups the cases into blocks that are at least half full,
3287 /// and then spits out individual lookup opcodes for each block.
3288 /// It emits the longest blocks first, and short blocks are just
3289 /// handled with direct compares.
3290 /// </summary>
3291 /// <param name="ec"></param>
3292 /// <param name="val"></param>
3293 /// <returns></returns>
3294 void TableSwitchEmit (EmitContext ec, Expression val)
3296 int element_count = Elements.Count;
3297 object [] element_keys = new object [element_count];
3298 Elements.Keys.CopyTo (element_keys, 0);
3299 Array.Sort (element_keys);
3301 // initialize the block list with one element per key
3302 var key_blocks = new List<KeyBlock> (element_count);
3303 foreach (object key in element_keys)
3304 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3306 KeyBlock current_kb;
3307 // iteratively merge the blocks while they are at least half full
3308 // there's probably a really cool way to do this with a tree...
3309 while (key_blocks.Count > 1)
3311 var key_blocks_new = new List<KeyBlock> ();
3312 current_kb = (KeyBlock) key_blocks [0];
3313 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3315 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3316 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3318 // merge blocks
3319 current_kb.last = kb.last;
3320 current_kb.Size += kb.Size;
3322 else
3324 // start a new block
3325 key_blocks_new.Add (current_kb);
3326 current_kb = kb;
3329 key_blocks_new.Add (current_kb);
3330 if (key_blocks.Count == key_blocks_new.Count)
3331 break;
3332 key_blocks = key_blocks_new;
3335 // initialize the key lists
3336 foreach (KeyBlock kb in key_blocks)
3337 kb.element_keys = new List<object> ();
3339 // fill the key lists
3340 int iBlockCurr = 0;
3341 if (key_blocks.Count > 0) {
3342 current_kb = (KeyBlock) key_blocks [0];
3343 foreach (object key in element_keys)
3345 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3346 System.Convert.ToInt64 (key) > current_kb.last;
3347 if (next_block)
3348 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3349 current_kb.element_keys.Add (key);
3353 // sort the blocks so we can tackle the largest ones first
3354 key_blocks.Sort ();
3356 // okay now we can start...
3357 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3358 Label lbl_default = default_target;
3360 Type type_keys = null;
3361 if (element_keys.Length > 0)
3362 type_keys = element_keys [0].GetType (); // used for conversions
3364 TypeSpec compare_type;
3366 if (TypeManager.IsEnumType (SwitchType))
3367 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3368 else
3369 compare_type = SwitchType;
3371 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3373 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3374 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3375 if (kb.Length <= 2)
3377 foreach (object key in kb.element_keys) {
3378 SwitchLabel sl = (SwitchLabel) Elements [key];
3379 if (key is int && (int) key == 0) {
3380 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3381 } else {
3382 val.Emit (ec);
3383 EmitObjectInteger (ec, key);
3384 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3388 else
3390 // TODO: if all the keys in the block are the same and there are
3391 // no gaps/defaults then just use a range-check.
3392 if (compare_type == TypeManager.int64_type ||
3393 compare_type == TypeManager.uint64_type)
3395 // TODO: optimize constant/I4 cases
3397 // check block range (could be > 2^31)
3398 val.Emit (ec);
3399 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3400 ec.Emit (OpCodes.Blt, lbl_default);
3401 val.Emit (ec);
3402 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3403 ec.Emit (OpCodes.Bgt, lbl_default);
3405 // normalize range
3406 val.Emit (ec);
3407 if (kb.first != 0)
3409 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3410 ec.Emit (OpCodes.Sub);
3412 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3414 else
3416 // normalize range
3417 val.Emit (ec);
3418 int first = (int) kb.first;
3419 if (first > 0)
3421 ec.EmitInt (first);
3422 ec.Emit (OpCodes.Sub);
3424 else if (first < 0)
3426 ec.EmitInt (-first);
3427 ec.Emit (OpCodes.Add);
3431 // first, build the list of labels for the switch
3432 int iKey = 0;
3433 int cJumps = kb.Length;
3434 Label [] switch_labels = new Label [cJumps];
3435 for (int iJump = 0; iJump < cJumps; iJump++)
3437 object key = kb.element_keys [iKey];
3438 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3440 SwitchLabel sl = (SwitchLabel) Elements [key];
3441 switch_labels [iJump] = sl.GetILLabel (ec);
3442 iKey++;
3444 else
3445 switch_labels [iJump] = lbl_default;
3447 // emit the switch opcode
3448 ec.Emit (OpCodes.Switch, switch_labels);
3451 // mark the default for this block
3452 if (iBlock != 0)
3453 ec.MarkLabel (lbl_default);
3456 // TODO: find the default case and emit it here,
3457 // to prevent having to do the following jump.
3458 // make sure to mark other labels in the default section
3460 // the last default just goes to the end
3461 if (element_keys.Length > 0)
3462 ec.Emit (OpCodes.Br, lbl_default);
3464 // now emit the code for the sections
3465 bool found_default = false;
3467 foreach (SwitchSection ss in Sections) {
3468 foreach (SwitchLabel sl in ss.Labels) {
3469 if (sl.Converted == SwitchLabel.NullStringCase) {
3470 ec.MarkLabel (null_target);
3471 } else if (sl.Label == null) {
3472 ec.MarkLabel (lbl_default);
3473 found_default = true;
3474 if (!has_null_case)
3475 ec.MarkLabel (null_target);
3477 ec.MarkLabel (sl.GetILLabel (ec));
3478 ec.MarkLabel (sl.GetILLabelCode (ec));
3480 ss.Block.Emit (ec);
3483 if (!found_default) {
3484 ec.MarkLabel (lbl_default);
3485 if (!has_null_case) {
3486 ec.MarkLabel (null_target);
3490 ec.MarkLabel (lbl_end);
3493 SwitchSection FindSection (SwitchLabel label)
3495 foreach (SwitchSection ss in Sections){
3496 foreach (SwitchLabel sl in ss.Labels){
3497 if (label == sl)
3498 return ss;
3502 return null;
3505 public static void Reset ()
3507 unique_counter = 0;
3508 allowed_types = null;
3511 public override bool Resolve (BlockContext ec)
3513 Expr = Expr.Resolve (ec);
3514 if (Expr == null)
3515 return false;
3517 new_expr = SwitchGoverningType (ec, Expr);
3519 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3520 unwrap = Nullable.Unwrap.Create (Expr, false);
3521 if (unwrap == null)
3522 return false;
3524 new_expr = SwitchGoverningType (ec, unwrap);
3527 if (new_expr == null){
3528 ec.Report.Error (151, loc,
3529 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3530 TypeManager.CSharpName (Expr.Type));
3531 return false;
3534 // Validate switch.
3535 SwitchType = new_expr.Type;
3537 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3538 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3539 return false;
3542 if (!CheckSwitch (ec))
3543 return false;
3545 if (HaveUnwrap)
3546 Elements.Remove (SwitchLabel.NullStringCase);
3548 Switch old_switch = ec.Switch;
3549 ec.Switch = this;
3550 ec.Switch.SwitchType = SwitchType;
3552 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3553 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3555 var constant = new_expr as Constant;
3556 if (constant != null) {
3557 is_constant = true;
3558 object key = constant.GetValue ();
3559 SwitchLabel label;
3560 if (Elements.TryGetValue (key, out label))
3561 constant_section = FindSection (label);
3563 if (constant_section == null)
3564 constant_section = default_section;
3567 bool first = true;
3568 bool ok = true;
3569 foreach (SwitchSection ss in Sections){
3570 if (!first)
3571 ec.CurrentBranching.CreateSibling (
3572 null, FlowBranching.SiblingType.SwitchSection);
3573 else
3574 first = false;
3576 if (is_constant && (ss != constant_section)) {
3577 // If we're a constant switch, we're only emitting
3578 // one single section - mark all the others as
3579 // unreachable.
3580 ec.CurrentBranching.CurrentUsageVector.Goto ();
3581 if (!ss.Block.ResolveUnreachable (ec, true)) {
3582 ok = false;
3584 } else {
3585 if (!ss.Block.Resolve (ec))
3586 ok = false;
3590 if (default_section == null)
3591 ec.CurrentBranching.CreateSibling (
3592 null, FlowBranching.SiblingType.SwitchSection);
3594 ec.EndFlowBranching ();
3595 ec.Switch = old_switch;
3597 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3599 if (!ok)
3600 return false;
3602 if (SwitchType == TypeManager.string_type && !is_constant) {
3603 // TODO: Optimize single case, and single+default case
3604 ResolveStringSwitchMap (ec);
3607 return true;
3610 void ResolveStringSwitchMap (ResolveContext ec)
3612 FullNamedExpression string_dictionary_type;
3613 if (TypeManager.generic_ienumerable_type != null) {
3614 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3615 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3617 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3618 new TypeArguments (
3619 new TypeExpression (TypeManager.string_type, loc),
3620 new TypeExpression (TypeManager.int32_type, loc)), loc);
3621 } else {
3622 MemberAccess system_collections_generic = new MemberAccess (
3623 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3625 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3628 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3629 Field field = new Field (ctype, string_dictionary_type,
3630 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3631 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3632 if (!field.Define ())
3633 return;
3634 ctype.AddField (field);
3636 var init = new List<Expression> ();
3637 int counter = 0;
3638 Elements.Clear ();
3639 string value = null;
3640 foreach (SwitchSection section in Sections) {
3641 int last_count = init.Count;
3642 foreach (SwitchLabel sl in section.Labels) {
3643 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3644 continue;
3646 value = (string) sl.Converted;
3647 var init_args = new List<Expression> (2);
3648 init_args.Add (new StringLiteral (value, sl.Location));
3649 init_args.Add (new IntConstant (counter, loc));
3650 init.Add (new CollectionElementInitializer (init_args, loc));
3654 // Don't add empty sections
3656 if (last_count == init.Count)
3657 continue;
3659 Elements.Add (counter, section.Labels [0]);
3660 ++counter;
3663 Arguments args = new Arguments (1);
3664 args.Add (new Argument (new IntConstant (init.Count, loc)));
3665 Expression initializer = new NewInitialize (string_dictionary_type, args,
3666 new CollectionOrObjectInitializers (init, loc), loc);
3668 switch_cache_field = new FieldExpr (field, loc);
3669 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3672 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3674 Label l_initialized = ec.DefineLabel ();
3677 // Skip initialization when value is null
3679 value.EmitBranchable (ec, null_target, false);
3682 // Check if string dictionary is initialized and initialize
3684 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3685 string_dictionary.EmitStatement (ec);
3686 ec.MarkLabel (l_initialized);
3688 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3690 ResolveContext rc = new ResolveContext (ec.MemberContext);
3692 if (TypeManager.generic_ienumerable_type != null) {
3693 Arguments get_value_args = new Arguments (2);
3694 get_value_args.Add (new Argument (value));
3695 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3696 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3697 if (get_item == null)
3698 return;
3701 // A value was not found, go to default case
3703 get_item.EmitBranchable (ec, default_target, false);
3704 } else {
3705 Arguments get_value_args = new Arguments (1);
3706 get_value_args.Add (new Argument (value));
3708 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3709 if (get_item == null)
3710 return;
3712 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3713 get_item_object.EmitAssign (ec, get_item, true, false);
3714 ec.Emit (OpCodes.Brfalse, default_target);
3716 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3717 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3719 get_item_int.EmitStatement (ec);
3720 get_item_object.Release (ec);
3723 TableSwitchEmit (ec, string_switch_variable);
3724 string_switch_variable.Release (ec);
3727 protected override void DoEmit (EmitContext ec)
3729 default_target = ec.DefineLabel ();
3730 null_target = ec.DefineLabel ();
3732 // Store variable for comparission purposes
3733 // TODO: Don't duplicate non-captured VariableReference
3734 LocalTemporary value;
3735 if (HaveUnwrap) {
3736 value = new LocalTemporary (SwitchType);
3737 unwrap.EmitCheck (ec);
3738 ec.Emit (OpCodes.Brfalse, null_target);
3739 new_expr.Emit (ec);
3740 value.Store (ec);
3741 } else if (!is_constant) {
3742 value = new LocalTemporary (SwitchType);
3743 new_expr.Emit (ec);
3744 value.Store (ec);
3745 } else
3746 value = null;
3749 // Setup the codegen context
3751 Label old_end = ec.LoopEnd;
3752 Switch old_switch = ec.Switch;
3754 ec.LoopEnd = ec.DefineLabel ();
3755 ec.Switch = this;
3757 // Emit Code.
3758 if (is_constant) {
3759 if (constant_section != null)
3760 constant_section.Block.Emit (ec);
3761 } else if (string_dictionary != null) {
3762 DoEmitStringSwitch (value, ec);
3763 } else {
3764 TableSwitchEmit (ec, value);
3767 if (value != null)
3768 value.Release (ec);
3770 // Restore context state.
3771 ec.MarkLabel (ec.LoopEnd);
3774 // Restore the previous context
3776 ec.LoopEnd = old_end;
3777 ec.Switch = old_switch;
3780 protected override void CloneTo (CloneContext clonectx, Statement t)
3782 Switch target = (Switch) t;
3784 target.Expr = Expr.Clone (clonectx);
3785 target.Sections = new List<SwitchSection> ();
3786 foreach (SwitchSection ss in Sections){
3787 target.Sections.Add (ss.Clone (clonectx));
3792 // A place where execution can restart in an iterator
3793 public abstract class ResumableStatement : Statement
3795 bool prepared;
3796 protected Label resume_point;
3798 public Label PrepareForEmit (EmitContext ec)
3800 if (!prepared) {
3801 prepared = true;
3802 resume_point = ec.DefineLabel ();
3804 return resume_point;
3807 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3809 return end;
3811 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3816 // Base class for statements that are implemented in terms of try...finally
3817 public abstract class ExceptionStatement : ResumableStatement
3819 bool code_follows;
3820 Iterator iter;
3821 List<ResumableStatement> resume_points;
3822 int first_resume_pc;
3824 protected abstract void EmitPreTryBody (EmitContext ec);
3825 protected abstract void EmitTryBody (EmitContext ec);
3826 protected abstract void EmitFinallyBody (EmitContext ec);
3828 protected sealed override void DoEmit (EmitContext ec)
3830 EmitPreTryBody (ec);
3832 if (resume_points != null) {
3833 ec.EmitInt ((int) Iterator.State.Running);
3834 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3837 ec.BeginExceptionBlock ();
3839 if (resume_points != null) {
3840 ec.MarkLabel (resume_point);
3842 // For normal control flow, we want to fall-through the Switch
3843 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3844 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3845 ec.EmitInt (first_resume_pc);
3846 ec.Emit (OpCodes.Sub);
3848 Label [] labels = new Label [resume_points.Count];
3849 for (int i = 0; i < resume_points.Count; ++i)
3850 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3851 ec.Emit (OpCodes.Switch, labels);
3854 EmitTryBody (ec);
3856 ec.BeginFinallyBlock ();
3858 Label start_finally = ec.DefineLabel ();
3859 if (resume_points != null) {
3860 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3861 ec.Emit (OpCodes.Brfalse_S, start_finally);
3862 ec.Emit (OpCodes.Endfinally);
3865 ec.MarkLabel (start_finally);
3866 EmitFinallyBody (ec);
3868 ec.EndExceptionBlock ();
3871 public void SomeCodeFollows ()
3873 code_follows = true;
3876 public override bool Resolve (BlockContext ec)
3878 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3879 // So, ensure there's some IL code after this statement.
3880 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3881 ec.NeedReturnLabel ();
3883 iter = ec.CurrentIterator;
3884 return true;
3887 public void AddResumePoint (ResumableStatement stmt, int pc)
3889 if (resume_points == null) {
3890 resume_points = new List<ResumableStatement> ();
3891 first_resume_pc = pc;
3894 if (pc != first_resume_pc + resume_points.Count)
3895 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3897 resume_points.Add (stmt);
3900 Label dispose_try_block;
3901 bool prepared_for_dispose, emitted_dispose;
3902 public override Label PrepareForDispose (EmitContext ec, Label end)
3904 if (!prepared_for_dispose) {
3905 prepared_for_dispose = true;
3906 dispose_try_block = ec.DefineLabel ();
3908 return dispose_try_block;
3911 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3913 if (emitted_dispose)
3914 return;
3916 emitted_dispose = true;
3918 Label end_of_try = ec.DefineLabel ();
3920 // Ensure that the only way we can get into this code is through a dispatcher
3921 if (have_dispatcher)
3922 ec.Emit (OpCodes.Br, end);
3924 ec.BeginExceptionBlock ();
3926 ec.MarkLabel (dispose_try_block);
3928 Label [] labels = null;
3929 for (int i = 0; i < resume_points.Count; ++i) {
3930 ResumableStatement s = (ResumableStatement) resume_points [i];
3931 Label ret = s.PrepareForDispose (ec, end_of_try);
3932 if (ret.Equals (end_of_try) && labels == null)
3933 continue;
3934 if (labels == null) {
3935 labels = new Label [resume_points.Count];
3936 for (int j = 0; j < i; ++j)
3937 labels [j] = end_of_try;
3939 labels [i] = ret;
3942 if (labels != null) {
3943 int j;
3944 for (j = 1; j < labels.Length; ++j)
3945 if (!labels [0].Equals (labels [j]))
3946 break;
3947 bool emit_dispatcher = j < labels.Length;
3949 if (emit_dispatcher) {
3950 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3951 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3952 ec.EmitInt (first_resume_pc);
3953 ec.Emit (OpCodes.Sub);
3954 ec.Emit (OpCodes.Switch, labels);
3955 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3958 foreach (ResumableStatement s in resume_points)
3959 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3962 ec.MarkLabel (end_of_try);
3964 ec.BeginFinallyBlock ();
3966 EmitFinallyBody (ec);
3968 ec.EndExceptionBlock ();
3972 public class Lock : ExceptionStatement {
3973 Expression expr;
3974 public Statement Statement;
3975 TemporaryVariable temp;
3977 public Lock (Expression expr, Statement stmt, Location l)
3979 this.expr = expr;
3980 Statement = stmt;
3981 loc = l;
3984 public override bool Resolve (BlockContext ec)
3986 expr = expr.Resolve (ec);
3987 if (expr == null)
3988 return false;
3990 if (!TypeManager.IsReferenceType (expr.Type)){
3991 ec.Report.Error (185, loc,
3992 "`{0}' is not a reference type as required by the lock statement",
3993 TypeManager.CSharpName (expr.Type));
3994 return false;
3997 ec.StartFlowBranching (this);
3998 bool ok = Statement.Resolve (ec);
3999 ec.EndFlowBranching ();
4001 ok &= base.Resolve (ec);
4003 // Avoid creating libraries that reference the internal
4004 // mcs NullType:
4005 TypeSpec t = expr.Type;
4006 if (t == TypeManager.null_type)
4007 t = TypeManager.object_type;
4009 temp = new TemporaryVariable (t, loc);
4010 temp.Resolve (ec);
4012 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4013 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4014 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4015 monitor_type, "Enter", loc, TypeManager.object_type);
4016 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4017 monitor_type, "Exit", loc, TypeManager.object_type);
4020 return ok;
4023 protected override void EmitPreTryBody (EmitContext ec)
4025 temp.EmitAssign (ec, expr);
4026 temp.Emit (ec);
4027 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4030 protected override void EmitTryBody (EmitContext ec)
4032 Statement.Emit (ec);
4035 protected override void EmitFinallyBody (EmitContext ec)
4037 temp.Emit (ec);
4038 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4041 protected override void CloneTo (CloneContext clonectx, Statement t)
4043 Lock target = (Lock) t;
4045 target.expr = expr.Clone (clonectx);
4046 target.Statement = Statement.Clone (clonectx);
4050 public class Unchecked : Statement {
4051 public Block Block;
4053 public Unchecked (Block b)
4055 Block = b;
4056 b.Unchecked = true;
4059 public override bool Resolve (BlockContext ec)
4061 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4062 return Block.Resolve (ec);
4065 protected override void DoEmit (EmitContext ec)
4067 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4068 Block.Emit (ec);
4071 protected override void CloneTo (CloneContext clonectx, Statement t)
4073 Unchecked target = (Unchecked) t;
4075 target.Block = clonectx.LookupBlock (Block);
4079 public class Checked : Statement {
4080 public Block Block;
4082 public Checked (Block b)
4084 Block = b;
4085 b.Unchecked = false;
4088 public override bool Resolve (BlockContext ec)
4090 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4091 return Block.Resolve (ec);
4094 protected override void DoEmit (EmitContext ec)
4096 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4097 Block.Emit (ec);
4100 protected override void CloneTo (CloneContext clonectx, Statement t)
4102 Checked target = (Checked) t;
4104 target.Block = clonectx.LookupBlock (Block);
4108 public class Unsafe : Statement {
4109 public Block Block;
4111 public Unsafe (Block b)
4113 Block = b;
4114 Block.Unsafe = true;
4115 loc = b.StartLocation;
4118 public override bool Resolve (BlockContext ec)
4120 if (ec.CurrentIterator != null)
4121 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4123 using (ec.Set (ResolveContext.Options.UnsafeScope))
4124 return Block.Resolve (ec);
4127 protected override void DoEmit (EmitContext ec)
4129 Block.Emit (ec);
4132 protected override void CloneTo (CloneContext clonectx, Statement t)
4134 Unsafe target = (Unsafe) t;
4136 target.Block = clonectx.LookupBlock (Block);
4141 // Fixed statement
4143 public class Fixed : Statement {
4144 Expression type;
4145 List<KeyValuePair<LocalInfo, Expression>> declarators;
4146 Statement statement;
4147 TypeSpec expr_type;
4148 Emitter[] data;
4149 bool has_ret;
4151 abstract class Emitter
4153 protected LocalInfo vi;
4154 protected Expression converted;
4156 protected Emitter (Expression expr, LocalInfo li)
4158 converted = expr;
4159 vi = li;
4162 public abstract void Emit (EmitContext ec);
4163 public abstract void EmitExit (EmitContext ec);
4166 class ExpressionEmitter : Emitter {
4167 public ExpressionEmitter (Expression converted, LocalInfo li) :
4168 base (converted, li)
4172 public override void Emit (EmitContext ec) {
4174 // Store pointer in pinned location
4176 converted.Emit (ec);
4177 vi.EmitAssign (ec);
4180 public override void EmitExit (EmitContext ec)
4182 ec.Emit (OpCodes.Ldc_I4_0);
4183 ec.Emit (OpCodes.Conv_U);
4184 vi.EmitAssign (ec);
4188 class StringEmitter : Emitter
4190 LocalInfo pinned_string;
4192 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4193 base (expr, li)
4195 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4196 pinned_string.Pinned = true;
4199 public StringEmitter Resolve (ResolveContext rc)
4201 pinned_string.Resolve (rc);
4203 if (TypeManager.int_get_offset_to_string_data == null) {
4204 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4205 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4208 return this;
4211 public override void Emit (EmitContext ec)
4213 pinned_string.ResolveVariable (ec);
4215 converted.Emit (ec);
4216 pinned_string.EmitAssign (ec);
4218 // TODO: Should use Binary::Add
4219 pinned_string.Emit (ec);
4220 ec.Emit (OpCodes.Conv_I);
4222 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4223 //pe.InstanceExpression = pinned_string;
4224 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4226 ec.Emit (OpCodes.Add);
4227 vi.EmitAssign (ec);
4230 public override void EmitExit (EmitContext ec)
4232 ec.Emit (OpCodes.Ldnull);
4233 pinned_string.EmitAssign (ec);
4237 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4239 this.type = type;
4240 declarators = decls;
4241 statement = stmt;
4242 loc = l;
4245 public Statement Statement {
4246 get { return statement; }
4249 public override bool Resolve (BlockContext ec)
4251 if (!ec.IsUnsafe){
4252 Expression.UnsafeError (ec, loc);
4253 return false;
4256 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4257 if (texpr == null) {
4258 if (type is VarExpr)
4259 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4261 return false;
4264 expr_type = texpr.Type;
4266 data = new Emitter [declarators.Count];
4268 if (!expr_type.IsPointer){
4269 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4270 return false;
4273 int i = 0;
4274 foreach (var p in declarators){
4275 LocalInfo vi = p.Key;
4276 Expression e = p.Value;
4278 vi.VariableInfo.SetAssigned (ec);
4279 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4282 // The rules for the possible declarators are pretty wise,
4283 // but the production on the grammar is more concise.
4285 // So we have to enforce these rules here.
4287 // We do not resolve before doing the case 1 test,
4288 // because the grammar is explicit in that the token &
4289 // is present, so we need to test for this particular case.
4292 if (e is Cast){
4293 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4294 return false;
4297 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4298 e = e.Resolve (ec);
4301 if (e == null)
4302 return false;
4305 // Case 2: Array
4307 if (e.Type.IsArray){
4308 TypeSpec array_type = TypeManager.GetElementType (e.Type);
4311 // Provided that array_type is unmanaged,
4313 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4314 return false;
4317 // and T* is implicitly convertible to the
4318 // pointer type given in the fixed statement.
4320 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4322 Expression converted = Convert.ImplicitConversionRequired (
4323 ec, array_ptr, vi.VariableType, loc);
4324 if (converted == null)
4325 return false;
4328 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4330 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4331 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4332 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4333 new NullPointer (loc),
4334 converted);
4336 converted = converted.Resolve (ec);
4338 data [i] = new ExpressionEmitter (converted, vi);
4339 i++;
4341 continue;
4345 // Case 3: string
4347 if (e.Type == TypeManager.string_type){
4348 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4349 i++;
4350 continue;
4353 // Case 4: fixed buffer
4354 if (e is FixedBufferPtr) {
4355 data [i++] = new ExpressionEmitter (e, vi);
4356 continue;
4360 // Case 1: & object.
4362 Unary u = e as Unary;
4363 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4364 IVariableReference vr = u.Expr as IVariableReference;
4365 if (vr == null || !vr.IsFixed) {
4366 data [i] = new ExpressionEmitter (e, vi);
4370 if (data [i++] == null)
4371 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4373 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4376 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4377 bool ok = statement.Resolve (ec);
4378 bool flow_unreachable = ec.EndFlowBranching ();
4379 has_ret = flow_unreachable;
4381 return ok;
4384 protected override void DoEmit (EmitContext ec)
4386 for (int i = 0; i < data.Length; i++) {
4387 data [i].Emit (ec);
4390 statement.Emit (ec);
4392 if (has_ret)
4393 return;
4396 // Clear the pinned variable
4398 for (int i = 0; i < data.Length; i++) {
4399 data [i].EmitExit (ec);
4403 protected override void CloneTo (CloneContext clonectx, Statement t)
4405 Fixed target = (Fixed) t;
4407 target.type = type.Clone (clonectx);
4408 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4409 foreach (var p in declarators) {
4410 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4411 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4414 target.statement = statement.Clone (clonectx);
4418 public class Catch : Statement {
4419 public readonly string Name;
4420 public Block Block;
4421 public Block VarBlock;
4423 Expression type_expr;
4424 TypeSpec type;
4426 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4428 type_expr = type;
4429 Name = name;
4430 Block = block;
4431 VarBlock = var_block;
4432 loc = l;
4435 public TypeSpec CatchType {
4436 get {
4437 return type;
4441 public bool IsGeneral {
4442 get {
4443 return type_expr == null;
4447 protected override void DoEmit (EmitContext ec)
4449 if (CatchType != null)
4450 ec.BeginCatchBlock (CatchType);
4451 else
4452 ec.BeginCatchBlock (TypeManager.object_type);
4454 if (VarBlock != null)
4455 VarBlock.Emit (ec);
4457 if (Name != null) {
4458 // TODO: Move to resolve
4459 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4460 lvr.Resolve (new ResolveContext (ec.MemberContext));
4462 // Only to make verifier happy
4463 if (TypeManager.IsGenericParameter (lvr.Type))
4464 ec.Emit (OpCodes.Unbox_Any, lvr.Type);
4466 Expression source;
4467 if (lvr.IsHoisted) {
4468 LocalTemporary lt = new LocalTemporary (lvr.Type);
4469 lt.Store (ec);
4470 source = lt;
4471 } else {
4472 // Variable is at the top of the stack
4473 source = EmptyExpression.Null;
4476 lvr.EmitAssign (ec, source, false, false);
4477 } else
4478 ec.Emit (OpCodes.Pop);
4480 Block.Emit (ec);
4483 public override bool Resolve (BlockContext ec)
4485 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4486 if (type_expr != null) {
4487 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4488 if (te == null)
4489 return false;
4491 type = te.Type;
4493 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4494 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4495 return false;
4497 } else
4498 type = null;
4500 if (!Block.Resolve (ec))
4501 return false;
4503 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4504 // emit the "unused variable" warnings.
4505 if (VarBlock != null)
4506 return VarBlock.Resolve (ec);
4508 return true;
4512 protected override void CloneTo (CloneContext clonectx, Statement t)
4514 Catch target = (Catch) t;
4516 if (type_expr != null)
4517 target.type_expr = type_expr.Clone (clonectx);
4518 if (VarBlock != null)
4519 target.VarBlock = clonectx.LookupBlock (VarBlock);
4520 target.Block = clonectx.LookupBlock (Block);
4524 public class TryFinally : ExceptionStatement {
4525 Statement stmt;
4526 Block fini;
4528 public TryFinally (Statement stmt, Block fini, Location l)
4530 this.stmt = stmt;
4531 this.fini = fini;
4532 loc = l;
4535 public override bool Resolve (BlockContext ec)
4537 bool ok = true;
4539 ec.StartFlowBranching (this);
4541 if (!stmt.Resolve (ec))
4542 ok = false;
4544 if (ok)
4545 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4546 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4547 if (!fini.Resolve (ec))
4548 ok = false;
4551 ec.EndFlowBranching ();
4553 ok &= base.Resolve (ec);
4555 return ok;
4558 protected override void EmitPreTryBody (EmitContext ec)
4562 protected override void EmitTryBody (EmitContext ec)
4564 stmt.Emit (ec);
4567 protected override void EmitFinallyBody (EmitContext ec)
4569 fini.Emit (ec);
4572 protected override void CloneTo (CloneContext clonectx, Statement t)
4574 TryFinally target = (TryFinally) t;
4576 target.stmt = (Statement) stmt.Clone (clonectx);
4577 if (fini != null)
4578 target.fini = clonectx.LookupBlock (fini);
4582 public class TryCatch : Statement {
4583 public Block Block;
4584 public List<Catch> Specific;
4585 public Catch General;
4586 bool inside_try_finally, code_follows;
4588 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4590 this.Block = block;
4591 this.Specific = catch_clauses;
4592 this.inside_try_finally = inside_try_finally;
4594 Catch c = catch_clauses [0];
4595 if (c.IsGeneral) {
4596 this.General = c;
4597 catch_clauses.RemoveAt (0);
4600 loc = l;
4603 public override bool Resolve (BlockContext ec)
4605 bool ok = true;
4607 ec.StartFlowBranching (this);
4609 if (!Block.Resolve (ec))
4610 ok = false;
4612 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4613 int last_index = 0;
4614 foreach (Catch c in Specific){
4615 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4617 if (c.Name != null) {
4618 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4619 if (vi == null)
4620 throw new Exception ();
4622 vi.VariableInfo = null;
4625 if (!c.Resolve (ec)) {
4626 ok = false;
4627 continue;
4630 TypeSpec resolved_type = c.CatchType;
4631 for (int ii = 0; ii < last_index; ++ii) {
4632 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4633 ec.Report.Error (160, c.loc,
4634 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4635 TypeManager.CSharpName (prev_catches [ii]));
4636 ok = false;
4640 prev_catches [last_index++] = resolved_type;
4643 if (General != null) {
4644 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4645 foreach (Catch c in Specific){
4646 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4647 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'");
4652 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4654 if (!General.Resolve (ec))
4655 ok = false;
4658 ec.EndFlowBranching ();
4660 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4661 // So, ensure there's some IL code after this statement
4662 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4663 ec.NeedReturnLabel ();
4665 return ok;
4668 public void SomeCodeFollows ()
4670 code_follows = true;
4673 protected override void DoEmit (EmitContext ec)
4675 if (!inside_try_finally)
4676 ec.BeginExceptionBlock ();
4678 Block.Emit (ec);
4680 foreach (Catch c in Specific)
4681 c.Emit (ec);
4683 if (General != null)
4684 General.Emit (ec);
4686 if (!inside_try_finally)
4687 ec.EndExceptionBlock ();
4690 protected override void CloneTo (CloneContext clonectx, Statement t)
4692 TryCatch target = (TryCatch) t;
4694 target.Block = clonectx.LookupBlock (Block);
4695 if (General != null)
4696 target.General = (Catch) General.Clone (clonectx);
4697 if (Specific != null){
4698 target.Specific = new List<Catch> ();
4699 foreach (Catch c in Specific)
4700 target.Specific.Add ((Catch) c.Clone (clonectx));
4705 // FIXME: Why is it almost exact copy of Using ??
4706 public class UsingTemporary : ExceptionStatement {
4707 TemporaryVariable local_copy;
4708 public Statement Statement;
4709 Expression expr;
4710 TypeSpec expr_type;
4712 public UsingTemporary (Expression expr, Statement stmt, Location l)
4714 this.expr = expr;
4715 Statement = stmt;
4716 loc = l;
4719 public override bool Resolve (BlockContext ec)
4721 expr = expr.Resolve (ec);
4722 if (expr == null)
4723 return false;
4725 expr_type = expr.Type;
4727 if (!expr_type.ImplementsInterface (TypeManager.idisposable_type) &&
4728 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4729 if (expr_type != InternalType.Dynamic) {
4730 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4731 return false;
4734 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4735 expr_type = expr.Type;
4738 local_copy = new TemporaryVariable (expr_type, loc);
4739 local_copy.Resolve (ec);
4741 ec.StartFlowBranching (this);
4743 bool ok = Statement.Resolve (ec);
4745 ec.EndFlowBranching ();
4747 ok &= base.Resolve (ec);
4749 if (TypeManager.void_dispose_void == null) {
4750 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4751 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4754 return ok;
4757 protected override void EmitPreTryBody (EmitContext ec)
4759 local_copy.EmitAssign (ec, expr);
4762 protected override void EmitTryBody (EmitContext ec)
4764 Statement.Emit (ec);
4767 protected override void EmitFinallyBody (EmitContext ec)
4769 if (!TypeManager.IsStruct (expr_type)) {
4770 Label skip = ec.DefineLabel ();
4771 local_copy.Emit (ec);
4772 ec.Emit (OpCodes.Brfalse, skip);
4773 local_copy.Emit (ec);
4774 ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4775 ec.MarkLabel (skip);
4776 return;
4779 MethodSpec ms = MemberCache.FindMember (expr_type,
4780 MemberFilter.Method ("Dispose", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.void_type),
4781 BindingRestriction.InstanceOnly) as MethodSpec;
4783 if (ms == null) {
4784 local_copy.Emit (ec);
4785 ec.Emit (OpCodes.Box, expr_type);
4786 ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4787 return;
4790 local_copy.AddressOf (ec, AddressOp.Load);
4791 ec.Emit (OpCodes.Call, ms);
4794 protected override void CloneTo (CloneContext clonectx, Statement t)
4796 UsingTemporary target = (UsingTemporary) t;
4798 target.expr = expr.Clone (clonectx);
4799 target.Statement = Statement.Clone (clonectx);
4803 public class Using : ExceptionStatement {
4804 Statement stmt;
4805 public Statement EmbeddedStatement {
4806 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4809 Expression var;
4810 Expression init;
4812 ExpressionStatement assign;
4814 public Using (Expression var, Expression init, Statement stmt, Location l)
4816 this.var = var;
4817 this.init = init;
4818 this.stmt = stmt;
4819 loc = l;
4822 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4824 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4825 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4826 TypeManager.CSharpName (expr.Type));
4829 protected override void EmitPreTryBody (EmitContext ec)
4831 assign.EmitStatement (ec);
4834 protected override void EmitTryBody (EmitContext ec)
4836 stmt.Emit (ec);
4839 protected override void EmitFinallyBody (EmitContext ec)
4841 Label skip = ec.DefineLabel ();
4843 bool emit_null_check = !TypeManager.IsValueType (var.Type);
4844 if (emit_null_check) {
4845 var.Emit (ec);
4846 ec.Emit (OpCodes.Brfalse, skip);
4849 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
4851 if (emit_null_check)
4852 ec.MarkLabel (skip);
4855 public override bool Resolve (BlockContext ec)
4857 if (!ResolveVariable (ec))
4858 return false;
4860 ec.StartFlowBranching (this);
4862 bool ok = stmt.Resolve (ec);
4864 ec.EndFlowBranching ();
4866 ok &= base.Resolve (ec);
4868 if (TypeManager.void_dispose_void == null) {
4869 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4870 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4873 return ok;
4876 bool ResolveVariable (BlockContext ec)
4878 assign = new SimpleAssign (var, init, loc);
4879 assign = assign.ResolveStatement (ec);
4880 if (assign == null)
4881 return false;
4883 if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4884 return true;
4887 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
4888 if (e == null) {
4889 if (assign.Type == InternalType.Dynamic) {
4890 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
4891 var = new TemporaryVariable (e.Type, loc);
4892 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
4893 return true;
4896 Error_IsNotConvertibleToIDisposable (ec, var);
4897 return false;
4900 throw new NotImplementedException ("covariance?");
4903 protected override void CloneTo (CloneContext clonectx, Statement t)
4905 Using target = (Using) t;
4907 target.var = var.Clone (clonectx);
4908 target.init = init.Clone (clonectx);
4909 target.stmt = stmt.Clone (clonectx);
4913 /// <summary>
4914 /// Implementation of the foreach C# statement
4915 /// </summary>
4916 public class Foreach : Statement {
4918 sealed class ArrayForeach : Statement
4920 class ArrayCounter : TemporaryVariable
4922 StatementExpression increment;
4924 public ArrayCounter (Location loc)
4925 : base (TypeManager.int32_type, loc)
4929 public void ResolveIncrement (BlockContext ec)
4931 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
4932 increment.Resolve (ec);
4935 public void EmitIncrement (EmitContext ec)
4937 increment.Emit (ec);
4941 readonly Foreach for_each;
4942 readonly Statement statement;
4944 Expression conv;
4945 TemporaryVariable[] lengths;
4946 Expression [] length_exprs;
4947 ArrayCounter[] counter;
4949 TemporaryVariable copy;
4950 Expression access;
4952 public ArrayForeach (Foreach @foreach, int rank)
4954 for_each = @foreach;
4955 statement = for_each.statement;
4956 loc = @foreach.loc;
4958 counter = new ArrayCounter [rank];
4959 length_exprs = new Expression [rank];
4962 // Only use temporary length variables when dealing with
4963 // multi-dimensional arrays
4965 if (rank > 1)
4966 lengths = new TemporaryVariable [rank];
4969 protected override void CloneTo (CloneContext clonectx, Statement target)
4971 throw new NotImplementedException ();
4974 public override bool Resolve (BlockContext ec)
4976 copy = new TemporaryVariable (for_each.expr.Type, loc);
4977 copy.Resolve (ec);
4979 int rank = length_exprs.Length;
4980 Arguments list = new Arguments (rank);
4981 for (int i = 0; i < rank; i++) {
4982 counter [i] = new ArrayCounter (loc);
4983 counter [i].ResolveIncrement (ec);
4985 if (rank == 1) {
4986 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
4987 } else {
4988 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4989 lengths [i].Resolve (ec);
4991 Arguments args = new Arguments (1);
4992 args.Add (new Argument (new IntConstant (i, loc)));
4993 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
4996 list.Add (new Argument (counter [i]));
4999 access = new ElementAccess (copy, list).Resolve (ec);
5000 if (access == null)
5001 return false;
5003 Expression var_type = for_each.type;
5004 VarExpr ve = var_type as VarExpr;
5005 if (ve != null) {
5006 // Infer implicitly typed local variable from foreach array type
5007 var_type = new TypeExpression (access.Type, ve.Location);
5010 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5011 if (var_type == null)
5012 return false;
5014 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5015 if (conv == null)
5016 return false;
5018 bool ok = true;
5020 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5021 ec.CurrentBranching.CreateSibling ();
5023 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5024 if (for_each.variable == null)
5025 ok = false;
5027 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5028 if (!statement.Resolve (ec))
5029 ok = false;
5030 ec.EndFlowBranching ();
5032 // There's no direct control flow from the end of the embedded statement to the end of the loop
5033 ec.CurrentBranching.CurrentUsageVector.Goto ();
5035 ec.EndFlowBranching ();
5037 return ok;
5040 protected override void DoEmit (EmitContext ec)
5042 copy.EmitAssign (ec, for_each.expr);
5044 int rank = length_exprs.Length;
5045 Label[] test = new Label [rank];
5046 Label[] loop = new Label [rank];
5048 for (int i = 0; i < rank; i++) {
5049 test [i] = ec.DefineLabel ();
5050 loop [i] = ec.DefineLabel ();
5052 if (lengths != null)
5053 lengths [i].EmitAssign (ec, length_exprs [i]);
5056 IntConstant zero = new IntConstant (0, loc);
5057 for (int i = 0; i < rank; i++) {
5058 counter [i].EmitAssign (ec, zero);
5060 ec.Emit (OpCodes.Br, test [i]);
5061 ec.MarkLabel (loop [i]);
5064 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5066 statement.Emit (ec);
5068 ec.MarkLabel (ec.LoopBegin);
5070 for (int i = rank - 1; i >= 0; i--){
5071 counter [i].EmitIncrement (ec);
5073 ec.MarkLabel (test [i]);
5074 counter [i].Emit (ec);
5076 if (lengths != null)
5077 lengths [i].Emit (ec);
5078 else
5079 length_exprs [i].Emit (ec);
5081 ec.Emit (OpCodes.Blt, loop [i]);
5084 ec.MarkLabel (ec.LoopEnd);
5088 sealed class CollectionForeach : Statement
5090 class CollectionForeachStatement : Statement
5092 TypeSpec type;
5093 Expression variable, current, conv;
5094 Statement statement;
5095 Assign assign;
5097 public CollectionForeachStatement (TypeSpec type, Expression variable,
5098 Expression current, Statement statement,
5099 Location loc)
5101 this.type = type;
5102 this.variable = variable;
5103 this.current = current;
5104 this.statement = statement;
5105 this.loc = loc;
5108 protected override void CloneTo (CloneContext clonectx, Statement target)
5110 throw new NotImplementedException ();
5113 public override bool Resolve (BlockContext ec)
5115 current = current.Resolve (ec);
5116 if (current == null)
5117 return false;
5119 conv = Convert.ExplicitConversion (ec, current, type, loc);
5120 if (conv == null)
5121 return false;
5123 assign = new SimpleAssign (variable, conv, loc);
5124 if (assign.Resolve (ec) == null)
5125 return false;
5127 if (!statement.Resolve (ec))
5128 return false;
5130 return true;
5133 protected override void DoEmit (EmitContext ec)
5135 assign.EmitStatement (ec);
5136 statement.Emit (ec);
5140 Expression variable, expr;
5141 Statement statement;
5143 TemporaryVariable enumerator;
5144 Expression init;
5145 Statement loop;
5146 Statement wrapper;
5148 MethodGroupExpr get_enumerator;
5149 PropertyExpr get_current;
5150 MethodSpec move_next;
5151 Expression var_type;
5152 TypeSpec enumerator_type;
5153 bool enumerator_found;
5155 public CollectionForeach (Expression var_type, Expression var,
5156 Expression expr, Statement stmt, Location l)
5158 this.var_type = var_type;
5159 this.variable = var;
5160 this.expr = expr;
5161 statement = stmt;
5162 loc = l;
5165 protected override void CloneTo (CloneContext clonectx, Statement target)
5167 throw new NotImplementedException ();
5170 bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi)
5172 TypeSpec return_type = mi.ReturnType;
5175 // Ok, we can access it, now make sure that we can do something
5176 // with this `GetEnumerator'
5179 if (return_type == TypeManager.ienumerator_type ||
5180 return_type.ImplementsInterface (TypeManager.ienumerator_type)) {
5182 // If it is not an interface, lets try to find the methods ourselves.
5183 // For example, if we have:
5184 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5185 // We can avoid the iface call. This is a runtime perf boost.
5186 // even bigger if we have a ValueType, because we avoid the cost
5187 // of boxing.
5189 // We have to make sure that both methods exist for us to take
5190 // this path. If one of the methods does not exist, we will just
5191 // use the interface. Sadly, this complex if statement is the only
5192 // way I could do this without a goto
5195 if (TypeManager.bool_movenext_void == null) {
5196 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5197 TypeManager.ienumerator_type, "MoveNext", loc, TypeSpec.EmptyTypes);
5200 if (TypeManager.ienumerator_getcurrent == null) {
5201 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5202 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5206 // Prefer a generic enumerator over a non-generic one.
5208 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5209 enumerator_type = return_type;
5210 if (!FetchGetCurrent (ec, return_type))
5211 get_current = new PropertyExpr (TypeManager.ienumerator_getcurrent, loc);
5212 if (!FetchMoveNext (return_type))
5213 move_next = TypeManager.bool_movenext_void;
5214 return true;
5217 if (return_type.IsInterface ||
5218 !FetchMoveNext (return_type) ||
5219 !FetchGetCurrent (ec, return_type)) {
5220 enumerator_type = return_type;
5221 move_next = TypeManager.bool_movenext_void;
5222 get_current = new PropertyExpr (TypeManager.ienumerator_getcurrent, loc);
5223 return true;
5225 } else {
5227 // Ok, so they dont return an IEnumerable, we will have to
5228 // find if they support the GetEnumerator pattern.
5231 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5232 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",
5233 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5234 return false;
5238 enumerator_type = return_type;
5240 return true;
5244 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5246 bool FetchMoveNext (TypeSpec t)
5248 move_next = MemberCache.FindMember (t,
5249 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5250 BindingRestriction.InstanceOnly) as MethodSpec;
5252 return move_next != null && (move_next.Modifiers & Modifiers.PUBLIC) != 0;
5256 // Retrieves a `public T get_Current ()' method from the Type `t'
5258 bool FetchGetCurrent (ResolveContext ec, TypeSpec t)
5260 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5261 ec.CurrentType, t, "Current", 0, MemberKind.Property,
5262 BindingRestriction.AccessibleOnly, loc) as PropertyExpr;
5263 if (pe == null)
5264 return false;
5266 get_current = pe;
5267 return true;
5270 void Error_Enumerator (BlockContext ec)
5272 if (enumerator_found) {
5273 return;
5276 ec.Report.Error (1579, loc,
5277 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5278 TypeManager.CSharpName (expr.Type));
5281 bool TryType (ResolveContext ec, TypeSpec t)
5283 var mg = Expression.MemberLookup (ec.Compiler, ec.CurrentType, null, t, "GetEnumerator", 0,
5284 MemberKind.Method, BindingRestriction.NoOverrides | BindingRestriction.InstanceOnly, loc) as MethodGroupExpr;
5286 if (mg == null)
5287 return false;
5289 MethodSpec result = null;
5290 MethodSpec tmp_move_next = null;
5291 PropertyExpr tmp_get_cur = null;
5292 TypeSpec tmp_enumerator_type = enumerator_type;
5293 foreach (MethodSpec mi in mg.Methods) {
5294 if (!mi.Parameters.IsEmpty)
5295 continue;
5297 // Check whether GetEnumerator is public
5298 if ((mi.Modifiers & Modifiers.AccessibilityMask) != Modifiers.PUBLIC)
5299 continue;
5301 enumerator_found = true;
5303 if (!GetEnumeratorFilter (ec, mi))
5304 continue;
5306 if (result != null) {
5307 if (TypeManager.IsGenericType (result.ReturnType)) {
5308 if (!TypeManager.IsGenericType (mi.ReturnType))
5309 continue;
5311 ec.Report.SymbolRelatedToPreviousError (t);
5312 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5313 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5314 TypeManager.CSharpName (t), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5315 return false;
5318 // Always prefer generics enumerators
5319 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5320 if (mi.DeclaringType.ImplementsInterface (result.DeclaringType) ||
5321 result.DeclaringType.ImplementsInterface (mi.DeclaringType))
5322 continue;
5324 ec.Report.SymbolRelatedToPreviousError (result);
5325 ec.Report.SymbolRelatedToPreviousError (mi);
5326 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5327 TypeManager.CSharpName (t), "enumerable", result.GetSignatureForError (), mi.GetSignatureForError ());
5328 return false;
5331 result = mi;
5332 tmp_move_next = move_next;
5333 tmp_get_cur = get_current;
5334 tmp_enumerator_type = enumerator_type;
5335 if (mi.DeclaringType == t)
5336 break;
5339 if (result != null) {
5340 move_next = tmp_move_next;
5341 get_current = tmp_get_cur;
5342 enumerator_type = tmp_enumerator_type;
5343 get_enumerator = new MethodGroupExpr (result, enumerator_type, loc);
5345 if (t != expr.Type) {
5346 expr = Convert.ExplicitConversion (
5347 ec, expr, t, loc);
5348 if (expr == null)
5349 throw new InternalErrorException ();
5352 get_enumerator.InstanceExpression = expr;
5353 get_enumerator.IsBase = t != expr.Type;
5355 return true;
5358 return false;
5361 bool ProbeCollectionType (ResolveContext ec, TypeSpec t)
5363 int errors = ec.Report.Errors;
5364 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type;){
5365 if (TryType (ec, tt))
5366 return true;
5367 tt = tt.BaseType;
5370 if (ec.Report.Errors > errors)
5371 return false;
5374 // Now try to find the method in the interfaces
5376 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type; ) {
5377 if (tt.Interfaces != null) {
5378 foreach (TypeSpec i in tt.Interfaces) {
5379 if (TryType (ec, i))
5380 return true;
5383 tt = tt.BaseType;
5386 return false;
5389 public override bool Resolve (BlockContext ec)
5391 enumerator_type = TypeManager.ienumerator_type;
5393 bool is_dynamic = expr.Type == InternalType.Dynamic;
5394 if (is_dynamic)
5395 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5397 if (!ProbeCollectionType (ec, expr.Type)) {
5398 Error_Enumerator (ec);
5399 return false;
5402 VarExpr ve = var_type as VarExpr;
5403 if (ve != null) {
5404 // Infer implicitly typed local variable from foreach enumerable type
5405 var_type = new TypeExpression (
5406 is_dynamic ? InternalType.Dynamic : get_current.Type,
5407 var_type.Location);
5410 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5411 if (var_type == null)
5412 return false;
5414 enumerator = new TemporaryVariable (enumerator_type, loc);
5415 enumerator.Resolve (ec);
5417 init = new Invocation (get_enumerator, null);
5418 init = init.Resolve (ec);
5419 if (init == null)
5420 return false;
5422 Expression move_next_expr;
5424 var mi = new List<MemberSpec> (1) { move_next };
5425 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5426 mg.InstanceExpression = enumerator;
5428 move_next_expr = new Invocation (mg, null);
5431 get_current.InstanceExpression = enumerator;
5433 Statement block = new CollectionForeachStatement (
5434 var_type.Type, variable, get_current, statement, loc);
5436 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5439 bool implements_idisposable = enumerator_type.ImplementsInterface (TypeManager.idisposable_type);
5440 if (implements_idisposable || !enumerator_type.IsSealed) {
5441 wrapper = new DisposableWrapper (this, implements_idisposable);
5442 } else {
5443 wrapper = new NonDisposableWrapper (this);
5446 return wrapper.Resolve (ec);
5449 protected override void DoEmit (EmitContext ec)
5451 wrapper.Emit (ec);
5454 class NonDisposableWrapper : Statement {
5455 CollectionForeach parent;
5457 internal NonDisposableWrapper (CollectionForeach parent)
5459 this.parent = parent;
5462 protected override void CloneTo (CloneContext clonectx, Statement target)
5464 throw new NotSupportedException ();
5467 public override bool Resolve (BlockContext ec)
5469 return parent.ResolveLoop (ec);
5472 protected override void DoEmit (EmitContext ec)
5474 parent.EmitLoopInit (ec);
5475 parent.EmitLoopBody (ec);
5479 sealed class DisposableWrapper : ExceptionStatement
5481 CollectionForeach parent;
5482 bool implements_idisposable;
5484 internal DisposableWrapper (CollectionForeach parent, bool implements)
5486 this.parent = parent;
5487 this.implements_idisposable = implements;
5490 protected override void CloneTo (CloneContext clonectx, Statement target)
5492 throw new NotSupportedException ();
5495 public override bool Resolve (BlockContext ec)
5497 bool ok = true;
5499 ec.StartFlowBranching (this);
5501 if (!parent.ResolveLoop (ec))
5502 ok = false;
5504 ec.EndFlowBranching ();
5506 ok &= base.Resolve (ec);
5508 if (TypeManager.void_dispose_void == null) {
5509 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5510 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5512 return ok;
5515 protected override void EmitPreTryBody (EmitContext ec)
5517 parent.EmitLoopInit (ec);
5520 protected override void EmitTryBody (EmitContext ec)
5522 parent.EmitLoopBody (ec);
5525 protected override void EmitFinallyBody (EmitContext ec)
5527 Expression instance = parent.enumerator;
5528 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5530 parent.enumerator.Emit (ec);
5532 Label call_dispose = ec.DefineLabel ();
5534 if (!implements_idisposable) {
5535 ec.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5536 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5537 temp.Store (ec);
5538 temp.Emit (ec);
5539 instance = temp;
5542 ec.Emit (OpCodes.Brtrue_S, call_dispose);
5544 // using 'endfinally' to empty the evaluation stack
5545 ec.Emit (OpCodes.Endfinally);
5546 ec.MarkLabel (call_dispose);
5549 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5553 bool ResolveLoop (BlockContext ec)
5555 return loop.Resolve (ec);
5558 void EmitLoopInit (EmitContext ec)
5560 enumerator.EmitAssign (ec, init);
5563 void EmitLoopBody (EmitContext ec)
5565 loop.Emit (ec);
5569 Expression type;
5570 Expression variable;
5571 Expression expr;
5572 Statement statement;
5574 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5575 Statement stmt, Location l)
5577 this.type = type;
5578 this.variable = var;
5579 this.expr = expr;
5580 statement = stmt;
5581 loc = l;
5584 public Statement Statement {
5585 get { return statement; }
5588 public override bool Resolve (BlockContext ec)
5590 expr = expr.Resolve (ec);
5591 if (expr == null)
5592 return false;
5594 if (expr.IsNull) {
5595 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5596 return false;
5599 if (expr.Type == TypeManager.string_type) {
5600 statement = new ArrayForeach (this, 1);
5601 } else if (expr.Type is ArrayContainer) {
5602 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5603 } else {
5604 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5605 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5606 expr.ExprClassName);
5607 return false;
5610 statement = new CollectionForeach (type, variable, expr, statement, loc);
5613 return statement.Resolve (ec);
5616 protected override void DoEmit (EmitContext ec)
5618 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5619 ec.LoopBegin = ec.DefineLabel ();
5620 ec.LoopEnd = ec.DefineLabel ();
5622 statement.Emit (ec);
5624 ec.LoopBegin = old_begin;
5625 ec.LoopEnd = old_end;
5628 protected override void CloneTo (CloneContext clonectx, Statement t)
5630 Foreach target = (Foreach) t;
5632 target.type = type.Clone (clonectx);
5633 target.variable = variable.Clone (clonectx);
5634 target.expr = expr.Clone (clonectx);
5635 target.statement = statement.Clone (clonectx);