2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / mcs / statement.cs
blob30414dba290fecca8ec642e1eba630448cb9d619
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 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 var tblock = block.CheckParameterNameConflict (name);
1659 if (tblock != null) {
1660 if (block == tblock && block is Linq.QueryBlock)
1661 Error_AlreadyDeclared (loc, name);
1662 else
1663 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 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1678 if (kvi != null) {
1679 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1680 Error_AlreadyDeclared (l, name, "child");
1681 return null;
1684 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1685 AddVariable (vi);
1687 if ((flags & Flags.VariablesInitialized) != 0)
1688 throw new InternalErrorException ("block has already been resolved");
1690 return vi;
1693 protected virtual void AddVariable (LocalInfo li)
1695 Variables.Add (li.Name, li);
1696 Explicit.AddKnownVariable (li.Name, li);
1699 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1701 if (reason == null) {
1702 Error_AlreadyDeclared (loc, var);
1703 return;
1706 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1707 "in this scope because it would give a different meaning " +
1708 "to `{0}', which is already used in a `{1}' scope " +
1709 "to denote something else", var, reason);
1712 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1714 Toplevel.Report.Error (128, loc,
1715 "A local variable named `{0}' is already defined in this scope", name);
1718 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1720 Toplevel.Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1721 name, conflict);
1724 public bool AddConstant (Expression type, string name, Expression value, Location l)
1726 if (AddVariable (type, name, l) == null)
1727 return false;
1729 if (constants == null)
1730 constants = new Dictionary<string, Expression> ();
1732 constants.Add (name, value);
1734 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1735 Use ();
1736 return true;
1739 static int next_temp_id = 0;
1741 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1743 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1745 if (temporary_variables == null)
1746 temporary_variables = new List<LocalInfo> ();
1748 int id = ++next_temp_id;
1749 string name = "$s_" + id.ToString ();
1751 LocalInfo li = new LocalInfo (te, name, this, loc);
1752 li.CompilerGenerated = true;
1753 temporary_variables.Add (li);
1754 return li;
1757 public LocalInfo GetLocalInfo (string name)
1759 LocalInfo ret;
1760 for (Block b = this; b != null; b = b.Parent) {
1761 if (b.variables != null && b.variables.TryGetValue (name, out ret)) {
1762 return ret;
1766 return null;
1769 public Expression GetVariableType (string name)
1771 LocalInfo vi = GetLocalInfo (name);
1772 return vi == null ? null : vi.Type;
1775 public Expression GetConstantExpression (string name)
1777 Expression ret;
1778 for (Block b = this; b != null; b = b.Parent) {
1779 if (b.constants != null) {
1780 if (b.constants.TryGetValue (name, out ret))
1781 return ret;
1784 return null;
1788 // It should be used by expressions which require to
1789 // register a statement during resolve process.
1791 public void AddScopeStatement (Statement s)
1793 if (scope_initializers == null)
1794 scope_initializers = new List<Statement> ();
1797 // Simple recursive helper, when resolve scope initializer another
1798 // new scope initializer can be added, this ensures it's initialized
1799 // before existing one. For now this can happen with expression trees
1800 // in base ctor initializer only
1802 if (resolving_init_idx.HasValue) {
1803 scope_initializers.Insert (resolving_init_idx.Value, s);
1804 ++resolving_init_idx;
1805 } else {
1806 scope_initializers.Add (s);
1810 public void AddStatement (Statement s)
1812 statements.Add (s);
1813 flags |= Flags.BlockUsed;
1816 public bool Used {
1817 get { return (flags & Flags.BlockUsed) != 0; }
1820 public void Use ()
1822 flags |= Flags.BlockUsed;
1825 public bool HasRet {
1826 get { return (flags & Flags.HasRet) != 0; }
1829 public int AssignableSlots {
1830 get {
1831 // TODO: Re-enable
1832 // if ((flags & Flags.VariablesInitialized) == 0)
1833 // throw new Exception ("Variables have not been initialized yet");
1834 return assignable_slots;
1838 public IList<ToplevelBlock> AnonymousChildren {
1839 get { return anonymous_children; }
1842 public void AddAnonymousChild (ToplevelBlock b)
1844 if (anonymous_children == null)
1845 anonymous_children = new List<ToplevelBlock> ();
1847 anonymous_children.Add (b);
1850 void DoResolveConstants (BlockContext ec)
1852 if (constants == null)
1853 return;
1855 if (variables == null)
1856 throw new InternalErrorException ("cannot happen");
1858 foreach (var de in variables) {
1859 string name = de.Key;
1860 LocalInfo vi = de.Value;
1861 TypeSpec variable_type = vi.VariableType;
1863 if (variable_type == null) {
1864 if (vi.Type is VarExpr)
1865 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1867 continue;
1870 Expression cv;
1871 if (!constants.TryGetValue (name, out cv))
1872 continue;
1874 // Don't let 'const int Foo = Foo;' succeed.
1875 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1876 // which in turn causes the 'must be constant' error to be triggered.
1877 constants.Remove (name);
1879 if (!variable_type.IsConstantCompatible) {
1880 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1881 continue;
1884 ec.CurrentBlock = this;
1885 Expression e;
1886 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1887 e = cv.Resolve (ec);
1889 if (e == null)
1890 continue;
1892 Constant ce = e as Constant;
1893 if (ce == null) {
1894 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1895 continue;
1898 e = ce.ConvertImplicitly (ec, variable_type);
1899 if (e == null) {
1900 if (TypeManager.IsReferenceType (variable_type))
1901 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1902 else
1903 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1904 continue;
1907 constants.Add (name, e);
1908 vi.IsConstant = true;
1912 protected void ResolveMeta (BlockContext ec, int offset)
1914 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1916 // If some parent block was unsafe, we remain unsafe even if this block
1917 // isn't explicitly marked as such.
1918 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
1919 flags |= Flags.VariablesInitialized;
1921 if (variables != null) {
1922 foreach (LocalInfo li in variables.Values) {
1923 if (!li.Resolve (ec))
1924 continue;
1925 li.VariableInfo = new VariableInfo (li, offset);
1926 offset += li.VariableInfo.Length;
1929 assignable_slots = offset;
1931 DoResolveConstants (ec);
1933 if (children == null)
1934 return;
1935 foreach (Block b in children)
1936 b.ResolveMeta (ec, offset);
1941 // Emits the local variable declarations for a block
1943 public virtual void EmitMeta (EmitContext ec)
1945 if (variables != null){
1946 foreach (LocalInfo vi in variables.Values)
1947 vi.ResolveVariable (ec);
1950 if (temporary_variables != null) {
1951 for (int i = 0; i < temporary_variables.Count; i++)
1952 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
1955 if (children != null) {
1956 for (int i = 0; i < children.Count; i++)
1957 ((Block)children[i]).EmitMeta(ec);
1961 void UsageWarning (BlockContext ec)
1963 if (variables == null || ec.Report.WarningLevel < 3)
1964 return;
1966 foreach (var de in variables) {
1967 LocalInfo vi = de.Value;
1969 if (!vi.Used) {
1970 string name = de.Key;
1972 // vi.VariableInfo can be null for 'catch' variables
1973 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
1974 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
1975 else
1976 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
1981 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
1983 Statement body;
1985 // Some statements are wrapped by a Block. Since
1986 // others' internal could be changed, here I treat
1987 // them as possibly wrapped by Block equally.
1988 Block b = s as Block;
1989 if (b != null && b.statements.Count == 1)
1990 s = (Statement) b.statements [0];
1992 if (s is Lock)
1993 body = ((Lock) s).Statement;
1994 else if (s is For)
1995 body = ((For) s).Statement;
1996 else if (s is Foreach)
1997 body = ((Foreach) s).Statement;
1998 else if (s is While)
1999 body = ((While) s).Statement;
2000 else if (s is Fixed)
2001 body = ((Fixed) s).Statement;
2002 else if (s is Using)
2003 body = ((Using) s).EmbeddedStatement;
2004 else if (s is UsingTemporary)
2005 body = ((UsingTemporary) s).Statement;
2006 else
2007 return;
2009 if (body == null || body is EmptyStatement)
2010 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2013 public override bool Resolve (BlockContext ec)
2015 Block prev_block = ec.CurrentBlock;
2016 bool ok = true;
2018 int errors = ec.Report.Errors;
2020 ec.CurrentBlock = this;
2021 ec.StartFlowBranching (this);
2023 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2026 // Compiler generated scope statements
2028 if (scope_initializers != null) {
2029 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2030 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2033 resolving_init_idx = null;
2037 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2038 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2039 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2040 // responsible for handling the situation.
2042 int statement_count = statements.Count;
2043 for (int ix = 0; ix < statement_count; ix++){
2044 Statement s = statements [ix];
2045 // Check possible empty statement (CS0642)
2046 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2047 statements [ix + 1] is ExplicitBlock)
2048 CheckPossibleMistakenEmptyStatement (ec, s);
2051 // Warn if we detect unreachable code.
2053 if (unreachable) {
2054 if (s is EmptyStatement)
2055 continue;
2057 if (!unreachable_shown && !(s is LabeledStatement)) {
2058 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2059 unreachable_shown = true;
2062 Block c_block = s as Block;
2063 if (c_block != null)
2064 c_block.unreachable = c_block.unreachable_shown = true;
2068 // Note that we're not using ResolveUnreachable() for unreachable
2069 // statements here. ResolveUnreachable() creates a temporary
2070 // flow branching and kills it afterwards. This leads to problems
2071 // if you have two unreachable statements where the first one
2072 // assigns a variable and the second one tries to access it.
2075 if (!s.Resolve (ec)) {
2076 ok = false;
2077 if (ec.IsInProbingMode)
2078 break;
2080 statements [ix] = new EmptyStatement (s.loc);
2081 continue;
2084 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2085 statements [ix] = new EmptyStatement (s.loc);
2087 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2088 if (unreachable && s is LabeledStatement)
2089 throw new InternalErrorException ("should not happen");
2092 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2093 ec.CurrentBranching, statement_count);
2095 while (ec.CurrentBranching is FlowBranchingLabeled)
2096 ec.EndFlowBranching ();
2098 bool flow_unreachable = ec.EndFlowBranching ();
2100 ec.CurrentBlock = prev_block;
2102 if (flow_unreachable)
2103 flags |= Flags.HasRet;
2105 // If we're a non-static `struct' constructor which doesn't have an
2106 // initializer, then we must initialize all of the struct's fields.
2107 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2108 ok = false;
2110 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2111 foreach (LabeledStatement label in labels.Values)
2112 if (!label.HasBeenReferenced)
2113 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2116 if (ok && errors == ec.Report.Errors)
2117 UsageWarning (ec);
2119 return ok;
2122 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2124 unreachable_shown = true;
2125 unreachable = true;
2127 if (warn)
2128 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2130 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2131 bool ok = Resolve (ec);
2132 ec.KillFlowBranching ();
2134 return ok;
2137 protected override void DoEmit (EmitContext ec)
2139 for (int ix = 0; ix < statements.Count; ix++){
2140 Statement s = (Statement) statements [ix];
2141 s.Emit (ec);
2145 public override void Emit (EmitContext ec)
2147 if (scope_initializers != null)
2148 EmitScopeInitializers (ec);
2150 ec.Mark (StartLocation);
2151 DoEmit (ec);
2153 if (SymbolWriter.HasSymbolWriter)
2154 EmitSymbolInfo (ec);
2157 protected void EmitScopeInitializers (EmitContext ec)
2159 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2161 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2162 foreach (Statement s in scope_initializers)
2163 s.Emit (ec);
2166 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2169 protected virtual void EmitSymbolInfo (EmitContext ec)
2171 if (variables != null) {
2172 foreach (LocalInfo vi in variables.Values) {
2173 vi.EmitSymbolInfo (ec);
2178 public override string ToString ()
2180 return String.Format ("{0} ({1}:{2})", GetType (), this_id, StartLocation);
2183 protected override void CloneTo (CloneContext clonectx, Statement t)
2185 Block target = (Block) t;
2187 clonectx.AddBlockMap (this, target);
2189 target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2190 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2191 if (Parent != null)
2192 target.Parent = clonectx.RemapBlockCopy (Parent);
2194 if (variables != null){
2195 target.variables = new Dictionary<string, LocalInfo> ();
2197 foreach (var de in variables){
2198 LocalInfo newlocal = de.Value.Clone (clonectx);
2199 target.variables [de.Key] = newlocal;
2200 clonectx.AddVariableMap (de.Value, newlocal);
2204 target.statements = new List<Statement> (statements.Count);
2205 foreach (Statement s in statements)
2206 target.statements.Add (s.Clone (clonectx));
2208 if (target.children != null){
2209 target.children = new List<Block> (children.Count);
2210 foreach (Block b in children){
2211 target.children.Add (clonectx.LookupBlock (b));
2216 // TODO: labels, switch_block, constants (?), anonymous_children
2221 public class ExplicitBlock : Block
2223 Dictionary<string, IKnownVariable> known_variables;
2224 protected AnonymousMethodStorey am_storey;
2226 public ExplicitBlock (Block parent, Location start, Location end)
2227 : this (parent, (Flags) 0, start, end)
2231 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2232 : base (parent, flags, start, end)
2234 this.Explicit = this;
2237 // <summary>
2238 // Marks a variable with name @name as being used in this or a child block.
2239 // If a variable name has been used in a child block, it's illegal to
2240 // declare a variable with the same name in the current block.
2241 // </summary>
2242 internal void AddKnownVariable (string name, IKnownVariable info)
2244 if (known_variables == null)
2245 known_variables = new Dictionary<string, IKnownVariable> ();
2247 known_variables [name] = info;
2249 if (Parent != null)
2250 Parent.Explicit.AddKnownVariable (name, info);
2253 public AnonymousMethodStorey AnonymousMethodStorey {
2254 get { return am_storey; }
2258 // Creates anonymous method storey in current block
2260 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2263 // When referencing a variable in iterator storey from children anonymous method
2265 if (Toplevel.am_storey is IteratorStorey) {
2266 return Toplevel.am_storey;
2270 // An iterator has only 1 storey block
2272 if (ec.CurrentIterator != null)
2273 return ec.CurrentIterator.Storey;
2275 if (am_storey == null) {
2276 MemberBase mc = ec.MemberContext as MemberBase;
2277 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2280 // Creates anonymous method storey for this block
2282 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2285 return am_storey;
2288 public override void Emit (EmitContext ec)
2290 if (am_storey != null)
2291 am_storey.EmitStoreyInstantiation (ec);
2293 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2294 if (emit_debug_info)
2295 ec.BeginScope ();
2297 base.Emit (ec);
2299 if (emit_debug_info)
2300 ec.EndScope ();
2303 public override void EmitMeta (EmitContext ec)
2306 // Creates anonymous method storey
2308 if (am_storey != null) {
2309 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2311 // Creates parent storey reference when hoisted this is accessible
2313 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2314 ExplicitBlock parent = Toplevel.Parent.Explicit;
2317 // Hoisted this exists in top-level parent storey only
2319 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2320 parent = parent.Parent.Explicit;
2322 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2325 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2327 // TODO MemberCache: Review
2328 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2331 am_storey.CreateType ();
2332 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2333 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2335 am_storey.DefineType ();
2336 am_storey.ResolveTypeParameters ();
2337 am_storey.Define ();
2338 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2340 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2341 if (ref_blocks != null) {
2342 foreach (ExplicitBlock ref_block in ref_blocks) {
2343 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2344 if (b.am_storey != null) {
2345 b.am_storey.AddParentStoreyReference (ec, am_storey);
2347 // Stop propagation inside same top block
2348 if (b.Toplevel == Toplevel)
2349 break;
2351 b = b.Toplevel;
2353 b.HasCapturedVariable = true;
2359 base.EmitMeta (ec);
2362 public IKnownVariable GetKnownVariable (string name)
2364 if (known_variables == null)
2365 return null;
2367 IKnownVariable kw;
2368 known_variables.TryGetValue (name, out kw);
2369 return kw;
2372 public bool HasCapturedThis
2374 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2375 get { return (flags & Flags.HasCapturedThis) != 0; }
2378 public bool HasCapturedVariable
2380 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2381 get { return (flags & Flags.HasCapturedVariable) != 0; }
2384 protected override void CloneTo (CloneContext clonectx, Statement t)
2386 ExplicitBlock target = (ExplicitBlock) t;
2387 target.known_variables = null;
2388 base.CloneTo (clonectx, t);
2392 public class ToplevelParameterInfo : IKnownVariable {
2393 public readonly ToplevelBlock Block;
2394 public readonly int Index;
2395 public VariableInfo VariableInfo;
2397 Block IKnownVariable.Block {
2398 get { return Block; }
2400 public Parameter Parameter {
2401 get { return Block.Parameters [Index]; }
2404 public TypeSpec ParameterType {
2405 get { return Block.Parameters.Types [Index]; }
2408 public Location Location {
2409 get { return Parameter.Location; }
2412 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2414 this.Block = block;
2415 this.Index = idx;
2420 // A toplevel block contains extra information, the split is done
2421 // only to separate information that would otherwise bloat the more
2422 // lightweight Block.
2424 // In particular, this was introduced when the support for Anonymous
2425 // Methods was implemented.
2427 public class ToplevelBlock : ExplicitBlock
2430 // Block is converted to an expression
2432 sealed class BlockScopeExpression : Expression
2434 Expression child;
2435 readonly ToplevelBlock block;
2437 public BlockScopeExpression (Expression child, ToplevelBlock block)
2439 this.child = child;
2440 this.block = block;
2443 public override Expression CreateExpressionTree (ResolveContext ec)
2445 throw new NotSupportedException ();
2448 protected override Expression DoResolve (ResolveContext ec)
2450 if (child == null)
2451 return null;
2453 child = child.Resolve (ec);
2454 if (child == null)
2455 return null;
2457 eclass = child.eclass;
2458 type = child.Type;
2459 return this;
2462 public override void Emit (EmitContext ec)
2464 block.EmitMeta (ec);
2465 block.EmitScopeInitializers (ec);
2466 child.Emit (ec);
2470 protected ParametersCompiled parameters;
2471 protected ToplevelParameterInfo[] parameter_info;
2472 LocalInfo this_variable;
2473 bool resolved;
2474 bool unreachable;
2475 CompilerContext compiler;
2477 public HoistedVariable HoistedThisVariable;
2479 public bool Resolved {
2480 get {
2481 return resolved;
2486 // The parameters for the block.
2488 public ParametersCompiled Parameters {
2489 get { return parameters; }
2492 public Report Report {
2493 get { return compiler.Report; }
2496 public ToplevelBlock Container {
2497 get { return Parent == null ? null : Parent.Toplevel; }
2500 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2501 this (ctx, parent, (Flags) 0, parameters, start)
2505 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2506 this (ctx, null, (Flags) 0, parameters, start)
2510 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2511 this (ctx, null, flags, parameters, start)
2515 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2516 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2517 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2518 base (null, flags, start, Location.Null)
2520 this.compiler = ctx;
2521 this.Toplevel = this;
2523 this.parameters = parameters;
2524 this.Parent = parent;
2525 if (parent != null)
2526 parent.AddAnonymousChild (this);
2528 if (!this.parameters.IsEmpty)
2529 ProcessParameters ();
2532 public ToplevelBlock (CompilerContext ctx, Location loc)
2533 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2537 protected override void CloneTo (CloneContext clonectx, Statement t)
2539 ToplevelBlock target = (ToplevelBlock) t;
2540 base.CloneTo (clonectx, t);
2542 if (parameters.Count != 0) {
2543 target.parameter_info = new ToplevelParameterInfo[parameters.Count];
2544 for (int i = 0; i < parameters.Count; ++i)
2545 target.parameter_info[i] = new ToplevelParameterInfo (target, i);
2549 public bool CheckError158 (string name, Location loc)
2551 if (AnonymousChildren != null) {
2552 foreach (ToplevelBlock child in AnonymousChildren) {
2553 if (!child.CheckError158 (name, loc))
2554 return false;
2558 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2559 if (!c.DoCheckError158 (name, loc))
2560 return false;
2563 return true;
2566 void ProcessParameters ()
2568 int n = parameters.Count;
2569 parameter_info = new ToplevelParameterInfo [n];
2570 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2571 for (int i = 0; i < n; ++i) {
2572 parameter_info [i] = new ToplevelParameterInfo (this, i);
2574 Parameter p = parameters [i];
2575 if (p == null)
2576 continue;
2578 string name = p.Name;
2579 if (CheckParentConflictName (top_parent, name, loc))
2580 AddKnownVariable (name, parameter_info [i]);
2583 // mark this block as "used" so that we create local declarations in a sub-block
2584 // FIXME: This appears to uncover a lot of bugs
2585 //this.Use ();
2588 bool DoCheckError158 (string name, Location loc)
2590 LabeledStatement s = LookupLabel (name);
2591 if (s != null) {
2592 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2593 Error_158 (name, loc);
2594 return false;
2597 return true;
2600 public override Expression CreateExpressionTree (ResolveContext ec)
2602 if (statements.Count == 1) {
2603 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2604 if (scope_initializers != null)
2605 expr = new BlockScopeExpression (expr, this);
2607 return expr;
2610 return base.CreateExpressionTree (ec);
2614 // Reformats this block to be top-level iterator block
2616 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2618 IsIterator = true;
2620 // Creates block with original statements
2621 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2623 source.statements = new List<Statement> (1);
2624 source.AddStatement (new Return (iterator, iterator.Location));
2625 source.IsIterator = false;
2627 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2628 source.am_storey = iterator_storey;
2629 return iterator_storey;
2633 // Returns a parameter reference expression for the given name,
2634 // or null if there is no such parameter
2636 public Expression GetParameterReference (string name, Location loc)
2638 for (ToplevelBlock t = this; t != null; t = t.Container) {
2639 if (t.parameters.IsEmpty)
2640 continue;
2642 Expression expr = t.GetParameterReferenceExpression (name, loc);
2643 if (expr != null)
2644 return expr;
2647 return null;
2650 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2652 int idx = parameters.GetParameterIndexByName (name);
2653 return idx < 0 ?
2654 null : new ParameterReference (parameter_info [idx], loc);
2657 public ToplevelBlock CheckParameterNameConflict (string name)
2659 for (ToplevelBlock t = this; t != null; t = t.Container) {
2660 if (t.HasParameterWithName (name))
2661 return t;
2664 return null;
2667 protected virtual bool HasParameterWithName (string name)
2669 return parameters.GetParameterIndexByName (name) >= 0;
2672 // <summary>
2673 // Returns the "this" instance variable of this block.
2674 // See AddThisVariable() for more information.
2675 // </summary>
2676 public LocalInfo ThisVariable {
2677 get { return this_variable; }
2680 // <summary>
2681 // This is used by non-static `struct' constructors which do not have an
2682 // initializer - in this case, the constructor must initialize all of the
2683 // struct's fields. To do this, we add a "this" variable and use the flow
2684 // analysis code to ensure that it's been fully initialized before control
2685 // leaves the constructor.
2686 // </summary>
2687 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2689 if (this_variable == null) {
2690 this_variable = new LocalInfo (ds, this, l);
2691 this_variable.Used = true;
2692 this_variable.IsThis = true;
2694 Variables.Add ("this", this_variable);
2697 return this_variable;
2700 public bool IsIterator {
2701 get { return (flags & Flags.IsIterator) != 0; }
2702 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2706 // Block has been converted to expression tree
2708 public bool IsExpressionTree {
2709 get { return (flags & Flags.IsExpressionTree) != 0; }
2712 public bool IsThisAssigned (BlockContext ec)
2714 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2717 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2719 if (resolved)
2720 return true;
2722 resolved = true;
2724 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2725 flags |= Flags.IsExpressionTree;
2727 try {
2728 if (!ResolveMeta (rc, ip))
2729 return false;
2731 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2732 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2734 if (!Resolve (rc))
2735 return false;
2737 unreachable = top_level.End ();
2739 } catch (Exception e) {
2740 if (e is CompletionResult)
2741 throw;
2743 if (rc.CurrentBlock != null) {
2744 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2745 } else {
2746 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2749 if (Report.DebugFlags > 0)
2750 throw;
2753 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2754 if (rc.CurrentAnonymousMethod == null) {
2755 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2756 return false;
2757 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2758 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2759 rc.CurrentAnonymousMethod.GetSignatureForError ());
2760 return false;
2764 return true;
2767 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2769 int errors = ec.Report.Errors;
2770 int orig_count = parameters.Count;
2772 if (ip != null)
2773 parameters = ip;
2775 // Assert: orig_count != parameter.Count => orig_count == 0
2776 if (orig_count != 0 && orig_count != parameters.Count)
2777 throw new InternalErrorException ("parameter information mismatch");
2779 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2781 for (int i = 0; i < orig_count; ++i) {
2782 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2784 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2785 continue;
2787 VariableInfo vi = new VariableInfo (ip, i, offset);
2788 parameter_info [i].VariableInfo = vi;
2789 offset += vi.Length;
2792 ResolveMeta (ec, offset);
2794 return ec.Report.Errors == errors;
2797 // <summary>
2798 // Check whether all `out' parameters have been assigned.
2799 // </summary>
2800 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2802 if (vector.IsUnreachable)
2803 return;
2805 int n = parameter_info == null ? 0 : parameter_info.Length;
2807 for (int i = 0; i < n; i++) {
2808 VariableInfo var = parameter_info [i].VariableInfo;
2810 if (var == null)
2811 continue;
2813 if (vector.IsAssigned (var, false))
2814 continue;
2816 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2817 var.Name);
2821 public override void Emit (EmitContext ec)
2823 if (Report.Errors > 0)
2824 return;
2826 #if PRODUCTION
2827 try {
2828 #endif
2829 EmitMeta (ec);
2831 if (ec.HasReturnLabel)
2832 ec.ReturnLabel = ec.DefineLabel ();
2834 base.Emit (ec);
2836 ec.Mark (EndLocation);
2838 if (ec.HasReturnLabel)
2839 ec.MarkLabel (ec.ReturnLabel);
2841 if (ec.return_value != null) {
2842 ec.Emit (OpCodes.Ldloc, ec.return_value);
2843 ec.Emit (OpCodes.Ret);
2844 } else {
2846 // If `HasReturnLabel' is set, then we already emitted a
2847 // jump to the end of the method, so we must emit a `ret'
2848 // there.
2850 // Unfortunately, System.Reflection.Emit automatically emits
2851 // a leave to the end of a finally block. This is a problem
2852 // if no code is following the try/finally block since we may
2853 // jump to a point after the end of the method.
2854 // As a workaround, we're always creating a return label in
2855 // this case.
2858 if (ec.HasReturnLabel || !unreachable) {
2859 if (ec.ReturnType != TypeManager.void_type)
2860 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2861 ec.Emit (OpCodes.Ret);
2865 #if PRODUCTION
2866 } catch (Exception e){
2867 Console.WriteLine ("Exception caught by the compiler while emitting:");
2868 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2870 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2871 throw;
2873 #endif
2876 public override void EmitMeta (EmitContext ec)
2878 // Avoid declaring an IL variable for this_variable since it is not accessed
2879 // from the generated IL
2880 if (this_variable != null)
2881 Variables.Remove ("this");
2882 base.EmitMeta (ec);
2885 protected override void EmitSymbolInfo (EmitContext ec)
2887 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2888 if ((ae != null) && (ae.Storey != null))
2889 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2891 base.EmitSymbolInfo (ec);
2895 public class SwitchLabel {
2896 Expression label;
2897 object converted;
2898 Location loc;
2900 Label il_label;
2901 bool il_label_set;
2902 Label il_label_code;
2903 bool il_label_code_set;
2905 public static readonly object NullStringCase = new object ();
2908 // if expr == null, then it is the default case.
2910 public SwitchLabel (Expression expr, Location l)
2912 label = expr;
2913 loc = l;
2916 public Expression Label {
2917 get {
2918 return label;
2922 public Location Location {
2923 get { return loc; }
2926 public object Converted {
2927 get {
2928 return converted;
2932 public Label GetILLabel (EmitContext ec)
2934 if (!il_label_set){
2935 il_label = ec.DefineLabel ();
2936 il_label_set = true;
2938 return il_label;
2941 public Label GetILLabelCode (EmitContext ec)
2943 if (!il_label_code_set){
2944 il_label_code = ec.DefineLabel ();
2945 il_label_code_set = true;
2947 return il_label_code;
2951 // Resolves the expression, reduces it to a literal if possible
2952 // and then converts it to the requested type.
2954 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
2956 Expression e = label.Resolve (ec);
2958 if (e == null)
2959 return false;
2961 Constant c = e as Constant;
2962 if (c == null){
2963 ec.Report.Error (150, loc, "A constant value is expected");
2964 return false;
2967 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2968 converted = NullStringCase;
2969 return true;
2972 if (allow_nullable && c.GetValue () == null) {
2973 converted = NullStringCase;
2974 return true;
2977 c = c.ImplicitConversionRequired (ec, required_type, loc);
2978 if (c == null)
2979 return false;
2981 converted = c.GetValue ();
2982 return true;
2985 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
2987 string label;
2988 if (converted == null)
2989 label = "default";
2990 else if (converted == NullStringCase)
2991 label = "null";
2992 else
2993 label = converted.ToString ();
2995 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2996 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2999 public SwitchLabel Clone (CloneContext clonectx)
3001 return new SwitchLabel (label.Clone (clonectx), loc);
3005 public class SwitchSection {
3006 // An array of SwitchLabels.
3007 public readonly List<SwitchLabel> Labels;
3008 public readonly Block Block;
3010 public SwitchSection (List<SwitchLabel> labels, Block block)
3012 Labels = labels;
3013 Block = block;
3016 public SwitchSection Clone (CloneContext clonectx)
3018 var cloned_labels = new List<SwitchLabel> ();
3020 foreach (SwitchLabel sl in cloned_labels)
3021 cloned_labels.Add (sl.Clone (clonectx));
3023 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3027 public class Switch : Statement {
3028 public List<SwitchSection> Sections;
3029 public Expression Expr;
3031 /// <summary>
3032 /// Maps constants whose type type SwitchType to their SwitchLabels.
3033 /// </summary>
3034 public IDictionary<object, SwitchLabel> Elements;
3036 /// <summary>
3037 /// The governing switch type
3038 /// </summary>
3039 public TypeSpec SwitchType;
3042 // Computed
3044 Label default_target;
3045 Label null_target;
3046 Expression new_expr;
3047 bool is_constant;
3048 bool has_null_case;
3049 SwitchSection constant_section;
3050 SwitchSection default_section;
3052 ExpressionStatement string_dictionary;
3053 FieldExpr switch_cache_field;
3054 static int unique_counter;
3057 // Nullable Types support
3059 Nullable.Unwrap unwrap;
3061 protected bool HaveUnwrap {
3062 get { return unwrap != null; }
3066 // The types allowed to be implicitly cast from
3067 // on the governing type
3069 static TypeSpec [] allowed_types;
3071 public Switch (Expression e, List<SwitchSection> sects, Location l)
3073 Expr = e;
3074 Sections = sects;
3075 loc = l;
3078 public bool GotDefault {
3079 get {
3080 return default_section != null;
3084 public Label DefaultTarget {
3085 get {
3086 return default_target;
3091 // Determines the governing type for a switch. The returned
3092 // expression might be the expression from the switch, or an
3093 // expression that includes any potential conversions to the
3094 // integral types or to string.
3096 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3098 TypeSpec t = expr.Type;
3100 if (t == TypeManager.byte_type ||
3101 t == TypeManager.sbyte_type ||
3102 t == TypeManager.ushort_type ||
3103 t == TypeManager.short_type ||
3104 t == TypeManager.uint32_type ||
3105 t == TypeManager.int32_type ||
3106 t == TypeManager.uint64_type ||
3107 t == TypeManager.int64_type ||
3108 t == TypeManager.char_type ||
3109 t == TypeManager.string_type ||
3110 t == TypeManager.bool_type ||
3111 TypeManager.IsEnumType (t))
3112 return expr;
3114 if (allowed_types == null){
3115 allowed_types = new TypeSpec [] {
3116 TypeManager.sbyte_type,
3117 TypeManager.byte_type,
3118 TypeManager.short_type,
3119 TypeManager.ushort_type,
3120 TypeManager.int32_type,
3121 TypeManager.uint32_type,
3122 TypeManager.int64_type,
3123 TypeManager.uint64_type,
3124 TypeManager.char_type,
3125 TypeManager.string_type
3130 // Try to find a *user* defined implicit conversion.
3132 // If there is no implicit conversion, or if there are multiple
3133 // conversions, we have to report an error
3135 Expression converted = null;
3136 foreach (TypeSpec tt in allowed_types){
3137 Expression e;
3139 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3140 if (e == null)
3141 continue;
3144 // Ignore over-worked ImplicitUserConversions that do
3145 // an implicit conversion in addition to the user conversion.
3147 if (!(e is UserCast))
3148 continue;
3150 if (converted != null){
3151 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3152 return null;
3155 converted = e;
3157 return converted;
3161 // Performs the basic sanity checks on the switch statement
3162 // (looks for duplicate keys and non-constant expressions).
3164 // It also returns a hashtable with the keys that we will later
3165 // use to compute the switch tables
3167 bool CheckSwitch (ResolveContext ec)
3169 bool error = false;
3170 Elements = new Dictionary<object, SwitchLabel> ();
3172 foreach (SwitchSection ss in Sections){
3173 foreach (SwitchLabel sl in ss.Labels){
3174 if (sl.Label == null){
3175 if (default_section != null){
3176 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3177 error = true;
3179 default_section = ss;
3180 continue;
3183 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3184 error = true;
3185 continue;
3188 object key = sl.Converted;
3189 if (key == SwitchLabel.NullStringCase)
3190 has_null_case = true;
3192 try {
3193 Elements.Add (key, sl);
3194 } catch (ArgumentException) {
3195 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3196 error = true;
3200 return !error;
3203 void EmitObjectInteger (EmitContext ec, object k)
3205 if (k is int)
3206 ec.EmitInt ((int) k);
3207 else if (k is Constant) {
3208 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3210 else if (k is uint)
3211 ec.EmitInt (unchecked ((int) (uint) k));
3212 else if (k is long)
3214 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3216 ec.EmitInt ((int) (long) k);
3217 ec.Emit (OpCodes.Conv_I8);
3219 else
3220 ec.EmitLong ((long) k);
3222 else if (k is ulong)
3224 ulong ul = (ulong) k;
3225 if (ul < (1L<<32))
3227 ec.EmitInt (unchecked ((int) ul));
3228 ec.Emit (OpCodes.Conv_U8);
3230 else
3232 ec.EmitLong (unchecked ((long) ul));
3235 else if (k is char)
3236 ec.EmitInt ((int) ((char) k));
3237 else if (k is sbyte)
3238 ec.EmitInt ((int) ((sbyte) k));
3239 else if (k is byte)
3240 ec.EmitInt ((int) ((byte) k));
3241 else if (k is short)
3242 ec.EmitInt ((int) ((short) k));
3243 else if (k is ushort)
3244 ec.EmitInt ((int) ((ushort) k));
3245 else if (k is bool)
3246 ec.EmitInt (((bool) k) ? 1 : 0);
3247 else
3248 throw new Exception ("Unhandled case");
3251 // structure used to hold blocks of keys while calculating table switch
3252 class KeyBlock : IComparable
3254 public KeyBlock (long _first)
3256 first = last = _first;
3258 public long first;
3259 public long last;
3260 public List<object> element_keys;
3261 // how many items are in the bucket
3262 public int Size = 1;
3263 public int Length
3265 get { return (int) (last - first + 1); }
3267 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3269 return kb_last.last - kb_first.first + 1;
3271 public int CompareTo (object obj)
3273 KeyBlock kb = (KeyBlock) obj;
3274 int nLength = Length;
3275 int nLengthOther = kb.Length;
3276 if (nLengthOther == nLength)
3277 return (int) (kb.first - first);
3278 return nLength - nLengthOther;
3282 /// <summary>
3283 /// This method emits code for a lookup-based switch statement (non-string)
3284 /// Basically it groups the cases into blocks that are at least half full,
3285 /// and then spits out individual lookup opcodes for each block.
3286 /// It emits the longest blocks first, and short blocks are just
3287 /// handled with direct compares.
3288 /// </summary>
3289 /// <param name="ec"></param>
3290 /// <param name="val"></param>
3291 /// <returns></returns>
3292 void TableSwitchEmit (EmitContext ec, Expression val)
3294 int element_count = Elements.Count;
3295 object [] element_keys = new object [element_count];
3296 Elements.Keys.CopyTo (element_keys, 0);
3297 Array.Sort (element_keys);
3299 // initialize the block list with one element per key
3300 var key_blocks = new List<KeyBlock> (element_count);
3301 foreach (object key in element_keys)
3302 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3304 KeyBlock current_kb;
3305 // iteratively merge the blocks while they are at least half full
3306 // there's probably a really cool way to do this with a tree...
3307 while (key_blocks.Count > 1)
3309 var key_blocks_new = new List<KeyBlock> ();
3310 current_kb = (KeyBlock) key_blocks [0];
3311 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3313 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3314 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3316 // merge blocks
3317 current_kb.last = kb.last;
3318 current_kb.Size += kb.Size;
3320 else
3322 // start a new block
3323 key_blocks_new.Add (current_kb);
3324 current_kb = kb;
3327 key_blocks_new.Add (current_kb);
3328 if (key_blocks.Count == key_blocks_new.Count)
3329 break;
3330 key_blocks = key_blocks_new;
3333 // initialize the key lists
3334 foreach (KeyBlock kb in key_blocks)
3335 kb.element_keys = new List<object> ();
3337 // fill the key lists
3338 int iBlockCurr = 0;
3339 if (key_blocks.Count > 0) {
3340 current_kb = (KeyBlock) key_blocks [0];
3341 foreach (object key in element_keys)
3343 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3344 System.Convert.ToInt64 (key) > current_kb.last;
3345 if (next_block)
3346 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3347 current_kb.element_keys.Add (key);
3351 // sort the blocks so we can tackle the largest ones first
3352 key_blocks.Sort ();
3354 // okay now we can start...
3355 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3356 Label lbl_default = default_target;
3358 Type type_keys = null;
3359 if (element_keys.Length > 0)
3360 type_keys = element_keys [0].GetType (); // used for conversions
3362 TypeSpec compare_type;
3364 if (TypeManager.IsEnumType (SwitchType))
3365 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3366 else
3367 compare_type = SwitchType;
3369 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3371 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3372 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3373 if (kb.Length <= 2)
3375 foreach (object key in kb.element_keys) {
3376 SwitchLabel sl = (SwitchLabel) Elements [key];
3377 if (key is int && (int) key == 0) {
3378 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3379 } else {
3380 val.Emit (ec);
3381 EmitObjectInteger (ec, key);
3382 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3386 else
3388 // TODO: if all the keys in the block are the same and there are
3389 // no gaps/defaults then just use a range-check.
3390 if (compare_type == TypeManager.int64_type ||
3391 compare_type == TypeManager.uint64_type)
3393 // TODO: optimize constant/I4 cases
3395 // check block range (could be > 2^31)
3396 val.Emit (ec);
3397 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3398 ec.Emit (OpCodes.Blt, lbl_default);
3399 val.Emit (ec);
3400 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3401 ec.Emit (OpCodes.Bgt, lbl_default);
3403 // normalize range
3404 val.Emit (ec);
3405 if (kb.first != 0)
3407 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3408 ec.Emit (OpCodes.Sub);
3410 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3412 else
3414 // normalize range
3415 val.Emit (ec);
3416 int first = (int) kb.first;
3417 if (first > 0)
3419 ec.EmitInt (first);
3420 ec.Emit (OpCodes.Sub);
3422 else if (first < 0)
3424 ec.EmitInt (-first);
3425 ec.Emit (OpCodes.Add);
3429 // first, build the list of labels for the switch
3430 int iKey = 0;
3431 int cJumps = kb.Length;
3432 Label [] switch_labels = new Label [cJumps];
3433 for (int iJump = 0; iJump < cJumps; iJump++)
3435 object key = kb.element_keys [iKey];
3436 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3438 SwitchLabel sl = (SwitchLabel) Elements [key];
3439 switch_labels [iJump] = sl.GetILLabel (ec);
3440 iKey++;
3442 else
3443 switch_labels [iJump] = lbl_default;
3445 // emit the switch opcode
3446 ec.Emit (OpCodes.Switch, switch_labels);
3449 // mark the default for this block
3450 if (iBlock != 0)
3451 ec.MarkLabel (lbl_default);
3454 // TODO: find the default case and emit it here,
3455 // to prevent having to do the following jump.
3456 // make sure to mark other labels in the default section
3458 // the last default just goes to the end
3459 if (element_keys.Length > 0)
3460 ec.Emit (OpCodes.Br, lbl_default);
3462 // now emit the code for the sections
3463 bool found_default = false;
3465 foreach (SwitchSection ss in Sections) {
3466 foreach (SwitchLabel sl in ss.Labels) {
3467 if (sl.Converted == SwitchLabel.NullStringCase) {
3468 ec.MarkLabel (null_target);
3469 } else if (sl.Label == null) {
3470 ec.MarkLabel (lbl_default);
3471 found_default = true;
3472 if (!has_null_case)
3473 ec.MarkLabel (null_target);
3475 ec.MarkLabel (sl.GetILLabel (ec));
3476 ec.MarkLabel (sl.GetILLabelCode (ec));
3478 ss.Block.Emit (ec);
3481 if (!found_default) {
3482 ec.MarkLabel (lbl_default);
3483 if (!has_null_case) {
3484 ec.MarkLabel (null_target);
3488 ec.MarkLabel (lbl_end);
3491 SwitchSection FindSection (SwitchLabel label)
3493 foreach (SwitchSection ss in Sections){
3494 foreach (SwitchLabel sl in ss.Labels){
3495 if (label == sl)
3496 return ss;
3500 return null;
3503 public static void Reset ()
3505 unique_counter = 0;
3506 allowed_types = null;
3509 public override bool Resolve (BlockContext ec)
3511 Expr = Expr.Resolve (ec);
3512 if (Expr == null)
3513 return false;
3515 new_expr = SwitchGoverningType (ec, Expr);
3517 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3518 unwrap = Nullable.Unwrap.Create (Expr, false);
3519 if (unwrap == null)
3520 return false;
3522 new_expr = SwitchGoverningType (ec, unwrap);
3525 if (new_expr == null){
3526 ec.Report.Error (151, loc,
3527 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3528 TypeManager.CSharpName (Expr.Type));
3529 return false;
3532 // Validate switch.
3533 SwitchType = new_expr.Type;
3535 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3536 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3537 return false;
3540 if (!CheckSwitch (ec))
3541 return false;
3543 if (HaveUnwrap)
3544 Elements.Remove (SwitchLabel.NullStringCase);
3546 Switch old_switch = ec.Switch;
3547 ec.Switch = this;
3548 ec.Switch.SwitchType = SwitchType;
3550 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3551 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3553 var constant = new_expr as Constant;
3554 if (constant != null) {
3555 is_constant = true;
3556 object key = constant.GetValue ();
3557 SwitchLabel label;
3558 if (Elements.TryGetValue (key, out label))
3559 constant_section = FindSection (label);
3561 if (constant_section == null)
3562 constant_section = default_section;
3565 bool first = true;
3566 bool ok = true;
3567 foreach (SwitchSection ss in Sections){
3568 if (!first)
3569 ec.CurrentBranching.CreateSibling (
3570 null, FlowBranching.SiblingType.SwitchSection);
3571 else
3572 first = false;
3574 if (is_constant && (ss != constant_section)) {
3575 // If we're a constant switch, we're only emitting
3576 // one single section - mark all the others as
3577 // unreachable.
3578 ec.CurrentBranching.CurrentUsageVector.Goto ();
3579 if (!ss.Block.ResolveUnreachable (ec, true)) {
3580 ok = false;
3582 } else {
3583 if (!ss.Block.Resolve (ec))
3584 ok = false;
3588 if (default_section == null)
3589 ec.CurrentBranching.CreateSibling (
3590 null, FlowBranching.SiblingType.SwitchSection);
3592 ec.EndFlowBranching ();
3593 ec.Switch = old_switch;
3595 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3597 if (!ok)
3598 return false;
3600 if (SwitchType == TypeManager.string_type && !is_constant) {
3601 // TODO: Optimize single case, and single+default case
3602 ResolveStringSwitchMap (ec);
3605 return true;
3608 void ResolveStringSwitchMap (ResolveContext ec)
3610 FullNamedExpression string_dictionary_type;
3611 if (TypeManager.generic_ienumerable_type != null) {
3612 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3613 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3615 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3616 new TypeArguments (
3617 new TypeExpression (TypeManager.string_type, loc),
3618 new TypeExpression (TypeManager.int32_type, loc)), loc);
3619 } else {
3620 MemberAccess system_collections_generic = new MemberAccess (
3621 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3623 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3626 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3627 Field field = new Field (ctype, string_dictionary_type,
3628 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3629 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3630 if (!field.Define ())
3631 return;
3632 ctype.AddField (field);
3634 var init = new List<Expression> ();
3635 int counter = 0;
3636 Elements.Clear ();
3637 string value = null;
3638 foreach (SwitchSection section in Sections) {
3639 int last_count = init.Count;
3640 foreach (SwitchLabel sl in section.Labels) {
3641 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3642 continue;
3644 value = (string) sl.Converted;
3645 var init_args = new List<Expression> (2);
3646 init_args.Add (new StringLiteral (value, sl.Location));
3647 init_args.Add (new IntConstant (counter, loc));
3648 init.Add (new CollectionElementInitializer (init_args, loc));
3652 // Don't add empty sections
3654 if (last_count == init.Count)
3655 continue;
3657 Elements.Add (counter, section.Labels [0]);
3658 ++counter;
3661 Arguments args = new Arguments (1);
3662 args.Add (new Argument (new IntConstant (init.Count, loc)));
3663 Expression initializer = new NewInitialize (string_dictionary_type, args,
3664 new CollectionOrObjectInitializers (init, loc), loc);
3666 switch_cache_field = new FieldExpr (field, loc);
3667 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3670 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3672 Label l_initialized = ec.DefineLabel ();
3675 // Skip initialization when value is null
3677 value.EmitBranchable (ec, null_target, false);
3680 // Check if string dictionary is initialized and initialize
3682 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3683 string_dictionary.EmitStatement (ec);
3684 ec.MarkLabel (l_initialized);
3686 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3688 ResolveContext rc = new ResolveContext (ec.MemberContext);
3690 if (TypeManager.generic_ienumerable_type != null) {
3691 Arguments get_value_args = new Arguments (2);
3692 get_value_args.Add (new Argument (value));
3693 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3694 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3695 if (get_item == null)
3696 return;
3699 // A value was not found, go to default case
3701 get_item.EmitBranchable (ec, default_target, false);
3702 } else {
3703 Arguments get_value_args = new Arguments (1);
3704 get_value_args.Add (new Argument (value));
3706 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args, loc), loc).Resolve (rc);
3707 if (get_item == null)
3708 return;
3710 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3711 get_item_object.EmitAssign (ec, get_item, true, false);
3712 ec.Emit (OpCodes.Brfalse, default_target);
3714 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3715 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3717 get_item_int.EmitStatement (ec);
3718 get_item_object.Release (ec);
3721 TableSwitchEmit (ec, string_switch_variable);
3722 string_switch_variable.Release (ec);
3725 protected override void DoEmit (EmitContext ec)
3727 default_target = ec.DefineLabel ();
3728 null_target = ec.DefineLabel ();
3730 // Store variable for comparission purposes
3731 // TODO: Don't duplicate non-captured VariableReference
3732 LocalTemporary value;
3733 if (HaveUnwrap) {
3734 value = new LocalTemporary (SwitchType);
3735 unwrap.EmitCheck (ec);
3736 ec.Emit (OpCodes.Brfalse, null_target);
3737 new_expr.Emit (ec);
3738 value.Store (ec);
3739 } else if (!is_constant) {
3740 value = new LocalTemporary (SwitchType);
3741 new_expr.Emit (ec);
3742 value.Store (ec);
3743 } else
3744 value = null;
3747 // Setup the codegen context
3749 Label old_end = ec.LoopEnd;
3750 Switch old_switch = ec.Switch;
3752 ec.LoopEnd = ec.DefineLabel ();
3753 ec.Switch = this;
3755 // Emit Code.
3756 if (is_constant) {
3757 if (constant_section != null)
3758 constant_section.Block.Emit (ec);
3759 } else if (string_dictionary != null) {
3760 DoEmitStringSwitch (value, ec);
3761 } else {
3762 TableSwitchEmit (ec, value);
3765 if (value != null)
3766 value.Release (ec);
3768 // Restore context state.
3769 ec.MarkLabel (ec.LoopEnd);
3772 // Restore the previous context
3774 ec.LoopEnd = old_end;
3775 ec.Switch = old_switch;
3778 protected override void CloneTo (CloneContext clonectx, Statement t)
3780 Switch target = (Switch) t;
3782 target.Expr = Expr.Clone (clonectx);
3783 target.Sections = new List<SwitchSection> ();
3784 foreach (SwitchSection ss in Sections){
3785 target.Sections.Add (ss.Clone (clonectx));
3790 // A place where execution can restart in an iterator
3791 public abstract class ResumableStatement : Statement
3793 bool prepared;
3794 protected Label resume_point;
3796 public Label PrepareForEmit (EmitContext ec)
3798 if (!prepared) {
3799 prepared = true;
3800 resume_point = ec.DefineLabel ();
3802 return resume_point;
3805 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3807 return end;
3809 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3814 // Base class for statements that are implemented in terms of try...finally
3815 public abstract class ExceptionStatement : ResumableStatement
3817 bool code_follows;
3818 Iterator iter;
3819 List<ResumableStatement> resume_points;
3820 int first_resume_pc;
3822 protected abstract void EmitPreTryBody (EmitContext ec);
3823 protected abstract void EmitTryBody (EmitContext ec);
3824 protected abstract void EmitFinallyBody (EmitContext ec);
3826 protected sealed override void DoEmit (EmitContext ec)
3828 EmitPreTryBody (ec);
3830 if (resume_points != null) {
3831 ec.EmitInt ((int) Iterator.State.Running);
3832 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3835 ec.BeginExceptionBlock ();
3837 if (resume_points != null) {
3838 ec.MarkLabel (resume_point);
3840 // For normal control flow, we want to fall-through the Switch
3841 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3842 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3843 ec.EmitInt (first_resume_pc);
3844 ec.Emit (OpCodes.Sub);
3846 Label [] labels = new Label [resume_points.Count];
3847 for (int i = 0; i < resume_points.Count; ++i)
3848 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3849 ec.Emit (OpCodes.Switch, labels);
3852 EmitTryBody (ec);
3854 ec.BeginFinallyBlock ();
3856 Label start_finally = ec.DefineLabel ();
3857 if (resume_points != null) {
3858 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3859 ec.Emit (OpCodes.Brfalse_S, start_finally);
3860 ec.Emit (OpCodes.Endfinally);
3863 ec.MarkLabel (start_finally);
3864 EmitFinallyBody (ec);
3866 ec.EndExceptionBlock ();
3869 public void SomeCodeFollows ()
3871 code_follows = true;
3874 public override bool Resolve (BlockContext ec)
3876 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3877 // So, ensure there's some IL code after this statement.
3878 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3879 ec.NeedReturnLabel ();
3881 iter = ec.CurrentIterator;
3882 return true;
3885 public void AddResumePoint (ResumableStatement stmt, int pc)
3887 if (resume_points == null) {
3888 resume_points = new List<ResumableStatement> ();
3889 first_resume_pc = pc;
3892 if (pc != first_resume_pc + resume_points.Count)
3893 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3895 resume_points.Add (stmt);
3898 Label dispose_try_block;
3899 bool prepared_for_dispose, emitted_dispose;
3900 public override Label PrepareForDispose (EmitContext ec, Label end)
3902 if (!prepared_for_dispose) {
3903 prepared_for_dispose = true;
3904 dispose_try_block = ec.DefineLabel ();
3906 return dispose_try_block;
3909 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3911 if (emitted_dispose)
3912 return;
3914 emitted_dispose = true;
3916 Label end_of_try = ec.DefineLabel ();
3918 // Ensure that the only way we can get into this code is through a dispatcher
3919 if (have_dispatcher)
3920 ec.Emit (OpCodes.Br, end);
3922 ec.BeginExceptionBlock ();
3924 ec.MarkLabel (dispose_try_block);
3926 Label [] labels = null;
3927 for (int i = 0; i < resume_points.Count; ++i) {
3928 ResumableStatement s = (ResumableStatement) resume_points [i];
3929 Label ret = s.PrepareForDispose (ec, end_of_try);
3930 if (ret.Equals (end_of_try) && labels == null)
3931 continue;
3932 if (labels == null) {
3933 labels = new Label [resume_points.Count];
3934 for (int j = 0; j < i; ++j)
3935 labels [j] = end_of_try;
3937 labels [i] = ret;
3940 if (labels != null) {
3941 int j;
3942 for (j = 1; j < labels.Length; ++j)
3943 if (!labels [0].Equals (labels [j]))
3944 break;
3945 bool emit_dispatcher = j < labels.Length;
3947 if (emit_dispatcher) {
3948 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3949 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3950 ec.EmitInt (first_resume_pc);
3951 ec.Emit (OpCodes.Sub);
3952 ec.Emit (OpCodes.Switch, labels);
3953 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3956 foreach (ResumableStatement s in resume_points)
3957 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3960 ec.MarkLabel (end_of_try);
3962 ec.BeginFinallyBlock ();
3964 EmitFinallyBody (ec);
3966 ec.EndExceptionBlock ();
3970 public class Lock : ExceptionStatement {
3971 Expression expr;
3972 public Statement Statement;
3973 TemporaryVariable temp;
3975 public Lock (Expression expr, Statement stmt, Location l)
3977 this.expr = expr;
3978 Statement = stmt;
3979 loc = l;
3982 public override bool Resolve (BlockContext ec)
3984 expr = expr.Resolve (ec);
3985 if (expr == null)
3986 return false;
3988 if (!TypeManager.IsReferenceType (expr.Type)){
3989 ec.Report.Error (185, loc,
3990 "`{0}' is not a reference type as required by the lock statement",
3991 TypeManager.CSharpName (expr.Type));
3992 return false;
3995 ec.StartFlowBranching (this);
3996 bool ok = Statement.Resolve (ec);
3997 ec.EndFlowBranching ();
3999 ok &= base.Resolve (ec);
4001 // Avoid creating libraries that reference the internal
4002 // mcs NullType:
4003 TypeSpec t = expr.Type;
4004 if (t == TypeManager.null_type)
4005 t = TypeManager.object_type;
4007 temp = new TemporaryVariable (t, loc);
4008 temp.Resolve (ec);
4010 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4011 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4012 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4013 monitor_type, "Enter", loc, TypeManager.object_type);
4014 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4015 monitor_type, "Exit", loc, TypeManager.object_type);
4018 return ok;
4021 protected override void EmitPreTryBody (EmitContext ec)
4023 temp.EmitAssign (ec, expr);
4024 temp.Emit (ec);
4025 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4028 protected override void EmitTryBody (EmitContext ec)
4030 Statement.Emit (ec);
4033 protected override void EmitFinallyBody (EmitContext ec)
4035 temp.Emit (ec);
4036 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4039 protected override void CloneTo (CloneContext clonectx, Statement t)
4041 Lock target = (Lock) t;
4043 target.expr = expr.Clone (clonectx);
4044 target.Statement = Statement.Clone (clonectx);
4048 public class Unchecked : Statement {
4049 public Block Block;
4051 public Unchecked (Block b, Location loc)
4053 Block = b;
4054 b.Unchecked = true;
4055 this.loc = loc;
4058 public override bool Resolve (BlockContext ec)
4060 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4061 return Block.Resolve (ec);
4064 protected override void DoEmit (EmitContext ec)
4066 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4067 Block.Emit (ec);
4070 protected override void CloneTo (CloneContext clonectx, Statement t)
4072 Unchecked target = (Unchecked) t;
4074 target.Block = clonectx.LookupBlock (Block);
4078 public class Checked : Statement {
4079 public Block Block;
4081 public Checked (Block b, Location loc)
4083 Block = b;
4084 b.Unchecked = false;
4085 this.loc = loc;
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, Location loc)
4113 Block = b;
4114 Block.Unsafe = true;
4115 this.loc = loc;
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
4708 TemporaryVariable local_copy;
4709 public Statement Statement;
4710 Expression expr;
4711 Statement dispose_call;
4713 public UsingTemporary (Expression expr, Statement stmt, Location l)
4715 this.expr = expr;
4716 Statement = stmt;
4717 loc = l;
4720 public override bool Resolve (BlockContext ec)
4722 expr = expr.Resolve (ec);
4723 if (expr == null)
4724 return false;
4726 var expr_type = expr.Type;
4728 if (!expr_type.ImplementsInterface (TypeManager.idisposable_type) &&
4729 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4730 if (expr_type != InternalType.Dynamic) {
4731 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4732 return false;
4735 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4736 expr_type = expr.Type;
4739 local_copy = new TemporaryVariable (expr_type, loc);
4740 local_copy.Resolve (ec);
4742 if (TypeManager.void_dispose_void == null) {
4743 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4744 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4747 var dispose_mg = new MethodGroupExpr (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc) {
4748 InstanceExpression = TypeManager.IsNullableType (expr_type) ?
4749 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
4750 local_copy
4753 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
4755 // Add conditional call when disposing possible null variable
4756 if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
4757 dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
4759 dispose_call.Resolve (ec);
4761 ec.StartFlowBranching (this);
4763 bool ok = Statement.Resolve (ec);
4765 ec.EndFlowBranching ();
4767 ok &= base.Resolve (ec);
4769 return ok;
4772 protected override void EmitPreTryBody (EmitContext ec)
4774 local_copy.EmitAssign (ec, expr);
4777 protected override void EmitTryBody (EmitContext ec)
4779 Statement.Emit (ec);
4782 protected override void EmitFinallyBody (EmitContext ec)
4784 dispose_call.Emit (ec);
4787 protected override void CloneTo (CloneContext clonectx, Statement t)
4789 UsingTemporary target = (UsingTemporary) t;
4791 target.expr = expr.Clone (clonectx);
4792 target.Statement = Statement.Clone (clonectx);
4796 public class Using : ExceptionStatement {
4797 Statement stmt;
4798 public Statement EmbeddedStatement {
4799 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4802 Expression var;
4803 Expression init;
4805 ExpressionStatement assign;
4807 public Using (Expression var, Expression init, Statement stmt, Location l)
4809 this.var = var;
4810 this.init = init;
4811 this.stmt = stmt;
4812 loc = l;
4815 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4817 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4818 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4819 TypeManager.CSharpName (expr.Type));
4822 protected override void EmitPreTryBody (EmitContext ec)
4824 assign.EmitStatement (ec);
4827 protected override void EmitTryBody (EmitContext ec)
4829 stmt.Emit (ec);
4832 protected override void EmitFinallyBody (EmitContext ec)
4834 Label skip = ec.DefineLabel ();
4836 bool emit_null_check = !TypeManager.IsValueType (var.Type);
4837 if (emit_null_check) {
4838 var.Emit (ec);
4839 ec.Emit (OpCodes.Brfalse, skip);
4842 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
4844 if (emit_null_check)
4845 ec.MarkLabel (skip);
4848 public override bool Resolve (BlockContext ec)
4850 if (!ResolveVariable (ec))
4851 return false;
4853 ec.StartFlowBranching (this);
4855 bool ok = stmt.Resolve (ec);
4857 ec.EndFlowBranching ();
4859 ok &= base.Resolve (ec);
4861 if (TypeManager.void_dispose_void == null) {
4862 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4863 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4866 return ok;
4869 bool ResolveVariable (BlockContext ec)
4871 assign = new SimpleAssign (var, init, loc);
4872 assign = assign.ResolveStatement (ec);
4873 if (assign == null)
4874 return false;
4876 if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4877 return true;
4880 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
4881 if (e == null) {
4882 if (assign.Type == InternalType.Dynamic) {
4883 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
4884 var = new TemporaryVariable (e.Type, loc);
4885 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
4886 return true;
4889 Error_IsNotConvertibleToIDisposable (ec, var);
4890 return false;
4893 throw new NotImplementedException ("covariance?");
4896 protected override void CloneTo (CloneContext clonectx, Statement t)
4898 Using target = (Using) t;
4900 target.var = var.Clone (clonectx);
4901 target.init = init.Clone (clonectx);
4902 target.stmt = stmt.Clone (clonectx);
4906 /// <summary>
4907 /// Implementation of the foreach C# statement
4908 /// </summary>
4909 public class Foreach : Statement {
4911 sealed class ArrayForeach : Statement
4913 class ArrayCounter : TemporaryVariable
4915 StatementExpression increment;
4917 public ArrayCounter (Location loc)
4918 : base (TypeManager.int32_type, loc)
4922 public void ResolveIncrement (BlockContext ec)
4924 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
4925 increment.Resolve (ec);
4928 public void EmitIncrement (EmitContext ec)
4930 increment.Emit (ec);
4934 readonly Foreach for_each;
4935 readonly Statement statement;
4937 Expression conv;
4938 TemporaryVariable[] lengths;
4939 Expression [] length_exprs;
4940 ArrayCounter[] counter;
4942 TemporaryVariable copy;
4943 Expression access;
4945 public ArrayForeach (Foreach @foreach, int rank)
4947 for_each = @foreach;
4948 statement = for_each.statement;
4949 loc = @foreach.loc;
4951 counter = new ArrayCounter [rank];
4952 length_exprs = new Expression [rank];
4955 // Only use temporary length variables when dealing with
4956 // multi-dimensional arrays
4958 if (rank > 1)
4959 lengths = new TemporaryVariable [rank];
4962 protected override void CloneTo (CloneContext clonectx, Statement target)
4964 throw new NotImplementedException ();
4967 public override bool Resolve (BlockContext ec)
4969 copy = new TemporaryVariable (for_each.expr.Type, loc);
4970 copy.Resolve (ec);
4972 int rank = length_exprs.Length;
4973 Arguments list = new Arguments (rank);
4974 for (int i = 0; i < rank; i++) {
4975 counter [i] = new ArrayCounter (loc);
4976 counter [i].ResolveIncrement (ec);
4978 if (rank == 1) {
4979 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
4980 } else {
4981 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4982 lengths [i].Resolve (ec);
4984 Arguments args = new Arguments (1);
4985 args.Add (new Argument (new IntConstant (i, loc)));
4986 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
4989 list.Add (new Argument (counter [i]));
4992 access = new ElementAccess (copy, list, loc).Resolve (ec);
4993 if (access == null)
4994 return false;
4996 Expression var_type = for_each.type;
4997 VarExpr ve = var_type as VarExpr;
4998 if (ve != null) {
4999 // Infer implicitly typed local variable from foreach array type
5000 var_type = new TypeExpression (access.Type, ve.Location);
5003 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5004 if (var_type == null)
5005 return false;
5007 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5008 if (conv == null)
5009 return false;
5011 bool ok = true;
5013 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5014 ec.CurrentBranching.CreateSibling ();
5016 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5017 if (for_each.variable == null)
5018 ok = false;
5020 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5021 if (!statement.Resolve (ec))
5022 ok = false;
5023 ec.EndFlowBranching ();
5025 // There's no direct control flow from the end of the embedded statement to the end of the loop
5026 ec.CurrentBranching.CurrentUsageVector.Goto ();
5028 ec.EndFlowBranching ();
5030 return ok;
5033 protected override void DoEmit (EmitContext ec)
5035 copy.EmitAssign (ec, for_each.expr);
5037 int rank = length_exprs.Length;
5038 Label[] test = new Label [rank];
5039 Label[] loop = new Label [rank];
5041 for (int i = 0; i < rank; i++) {
5042 test [i] = ec.DefineLabel ();
5043 loop [i] = ec.DefineLabel ();
5045 if (lengths != null)
5046 lengths [i].EmitAssign (ec, length_exprs [i]);
5049 IntConstant zero = new IntConstant (0, loc);
5050 for (int i = 0; i < rank; i++) {
5051 counter [i].EmitAssign (ec, zero);
5053 ec.Emit (OpCodes.Br, test [i]);
5054 ec.MarkLabel (loop [i]);
5057 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5059 statement.Emit (ec);
5061 ec.MarkLabel (ec.LoopBegin);
5063 for (int i = rank - 1; i >= 0; i--){
5064 counter [i].EmitIncrement (ec);
5066 ec.MarkLabel (test [i]);
5067 counter [i].Emit (ec);
5069 if (lengths != null)
5070 lengths [i].Emit (ec);
5071 else
5072 length_exprs [i].Emit (ec);
5074 ec.Emit (OpCodes.Blt, loop [i]);
5077 ec.MarkLabel (ec.LoopEnd);
5081 sealed class CollectionForeach : Statement
5083 class CollectionForeachStatement : Statement
5085 TypeSpec type;
5086 Expression variable, current, conv;
5087 Statement statement;
5088 Assign assign;
5090 public CollectionForeachStatement (TypeSpec type, Expression variable,
5091 Expression current, Statement statement,
5092 Location loc)
5094 this.type = type;
5095 this.variable = variable;
5096 this.current = current;
5097 this.statement = statement;
5098 this.loc = loc;
5101 protected override void CloneTo (CloneContext clonectx, Statement target)
5103 throw new NotImplementedException ();
5106 public override bool Resolve (BlockContext ec)
5108 current = current.Resolve (ec);
5109 if (current == null)
5110 return false;
5112 conv = Convert.ExplicitConversion (ec, current, type, loc);
5113 if (conv == null)
5114 return false;
5116 assign = new SimpleAssign (variable, conv, loc);
5117 if (assign.Resolve (ec) == null)
5118 return false;
5120 if (!statement.Resolve (ec))
5121 return false;
5123 return true;
5126 protected override void DoEmit (EmitContext ec)
5128 assign.EmitStatement (ec);
5129 statement.Emit (ec);
5133 Expression variable, expr;
5134 Statement statement;
5136 TemporaryVariable enumerator;
5137 Expression init;
5138 Statement loop;
5139 Statement wrapper;
5141 MethodGroupExpr get_enumerator;
5142 PropertyExpr get_current;
5143 MethodSpec move_next;
5144 Expression var_type;
5145 TypeSpec enumerator_type;
5146 bool enumerator_found;
5148 public CollectionForeach (Expression var_type, Expression var,
5149 Expression expr, Statement stmt, Location l)
5151 this.var_type = var_type;
5152 this.variable = var;
5153 this.expr = expr;
5154 statement = stmt;
5155 loc = l;
5158 protected override void CloneTo (CloneContext clonectx, Statement target)
5160 throw new NotImplementedException ();
5163 bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi)
5165 TypeSpec return_type = mi.ReturnType;
5168 // Ok, we can access it, now make sure that we can do something
5169 // with this `GetEnumerator'
5172 if (return_type == TypeManager.ienumerator_type ||
5173 return_type.ImplementsInterface (TypeManager.ienumerator_type)) {
5175 // If it is not an interface, lets try to find the methods ourselves.
5176 // For example, if we have:
5177 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5178 // We can avoid the iface call. This is a runtime perf boost.
5179 // even bigger if we have a ValueType, because we avoid the cost
5180 // of boxing.
5182 // We have to make sure that both methods exist for us to take
5183 // this path. If one of the methods does not exist, we will just
5184 // use the interface. Sadly, this complex if statement is the only
5185 // way I could do this without a goto
5188 if (TypeManager.bool_movenext_void == null) {
5189 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5190 TypeManager.ienumerator_type, "MoveNext", loc, TypeSpec.EmptyTypes);
5193 if (TypeManager.ienumerator_getcurrent == null) {
5194 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5195 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5199 // Prefer a generic enumerator over a non-generic one.
5201 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5202 enumerator_type = return_type;
5203 if (!FetchGetCurrent (ec, return_type))
5204 get_current = new PropertyExpr (TypeManager.ienumerator_getcurrent, loc);
5205 if (!FetchMoveNext (return_type))
5206 move_next = TypeManager.bool_movenext_void;
5207 return true;
5210 if (return_type.IsInterface ||
5211 !FetchMoveNext (return_type) ||
5212 !FetchGetCurrent (ec, return_type)) {
5213 enumerator_type = return_type;
5214 move_next = TypeManager.bool_movenext_void;
5215 get_current = new PropertyExpr (TypeManager.ienumerator_getcurrent, loc);
5216 return true;
5218 } else {
5220 // Ok, so they dont return an IEnumerable, we will have to
5221 // find if they support the GetEnumerator pattern.
5224 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5225 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",
5226 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5227 return false;
5231 enumerator_type = return_type;
5233 return true;
5237 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5239 bool FetchMoveNext (TypeSpec t)
5241 move_next = MemberCache.FindMember (t,
5242 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5243 BindingRestriction.InstanceOnly) as MethodSpec;
5245 return move_next != null && (move_next.Modifiers & Modifiers.PUBLIC) != 0;
5249 // Retrieves a `public T get_Current ()' method from the Type `t'
5251 bool FetchGetCurrent (ResolveContext ec, TypeSpec t)
5253 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5254 ec.CurrentType, t, "Current", 0, MemberKind.Property,
5255 BindingRestriction.AccessibleOnly, loc) as PropertyExpr;
5256 if (pe == null)
5257 return false;
5259 get_current = pe;
5260 return true;
5263 void Error_Enumerator (BlockContext ec)
5265 if (enumerator_found) {
5266 return;
5269 ec.Report.Error (1579, loc,
5270 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5271 TypeManager.CSharpName (expr.Type));
5274 bool TryType (ResolveContext ec, TypeSpec t)
5276 var mg = Expression.MemberLookup (ec.Compiler, ec.CurrentType, null, t, "GetEnumerator", 0,
5277 MemberKind.Method, BindingRestriction.NoOverrides | BindingRestriction.InstanceOnly, loc) as MethodGroupExpr;
5279 if (mg == null)
5280 return false;
5282 MethodSpec result = null;
5283 MethodSpec tmp_move_next = null;
5284 PropertyExpr tmp_get_cur = null;
5285 TypeSpec tmp_enumerator_type = enumerator_type;
5286 foreach (MethodSpec mi in mg.Methods) {
5287 if (!mi.Parameters.IsEmpty)
5288 continue;
5290 // Check whether GetEnumerator is public
5291 if ((mi.Modifiers & Modifiers.AccessibilityMask) != Modifiers.PUBLIC)
5292 continue;
5294 enumerator_found = true;
5296 if (!GetEnumeratorFilter (ec, mi))
5297 continue;
5299 if (result != null) {
5300 if (TypeManager.IsGenericType (result.ReturnType)) {
5301 if (!TypeManager.IsGenericType (mi.ReturnType))
5302 continue;
5304 ec.Report.SymbolRelatedToPreviousError (t);
5305 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5306 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5307 TypeManager.CSharpName (t), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5308 return false;
5311 // Always prefer generics enumerators
5312 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5313 if (mi.DeclaringType.ImplementsInterface (result.DeclaringType) ||
5314 result.DeclaringType.ImplementsInterface (mi.DeclaringType))
5315 continue;
5317 ec.Report.SymbolRelatedToPreviousError (result);
5318 ec.Report.SymbolRelatedToPreviousError (mi);
5319 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5320 TypeManager.CSharpName (t), "enumerable", result.GetSignatureForError (), mi.GetSignatureForError ());
5321 return false;
5324 result = mi;
5325 tmp_move_next = move_next;
5326 tmp_get_cur = get_current;
5327 tmp_enumerator_type = enumerator_type;
5328 if (mi.DeclaringType == t)
5329 break;
5332 if (result != null) {
5333 move_next = tmp_move_next;
5334 get_current = tmp_get_cur;
5335 enumerator_type = tmp_enumerator_type;
5336 get_enumerator = new MethodGroupExpr (result, enumerator_type, loc);
5338 if (t != expr.Type) {
5339 expr = Convert.ExplicitConversion (
5340 ec, expr, t, loc);
5341 if (expr == null)
5342 throw new InternalErrorException ();
5345 get_enumerator.InstanceExpression = expr;
5346 // get_enumerator.IsBase = t != expr.Type;
5348 return true;
5351 return false;
5354 bool ProbeCollectionType (ResolveContext ec, TypeSpec t)
5356 int errors = ec.Report.Errors;
5357 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type;){
5358 if (TryType (ec, tt))
5359 return true;
5360 tt = tt.BaseType;
5363 if (ec.Report.Errors > errors)
5364 return false;
5367 // Now try to find the method in the interfaces
5369 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type; ) {
5370 if (tt.Interfaces != null) {
5371 foreach (TypeSpec i in tt.Interfaces) {
5372 if (TryType (ec, i))
5373 return true;
5376 tt = tt.BaseType;
5379 return false;
5382 public override bool Resolve (BlockContext ec)
5384 enumerator_type = TypeManager.ienumerator_type;
5386 bool is_dynamic = expr.Type == InternalType.Dynamic;
5387 if (is_dynamic)
5388 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5390 if (!ProbeCollectionType (ec, expr.Type)) {
5391 Error_Enumerator (ec);
5392 return false;
5395 VarExpr ve = var_type as VarExpr;
5396 if (ve != null) {
5397 // Infer implicitly typed local variable from foreach enumerable type
5398 var_type = new TypeExpression (
5399 is_dynamic ? InternalType.Dynamic : get_current.Type,
5400 var_type.Location);
5403 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5404 if (var_type == null)
5405 return false;
5407 enumerator = new TemporaryVariable (enumerator_type, loc);
5408 enumerator.Resolve (ec);
5410 init = new Invocation (get_enumerator, null);
5411 init = init.Resolve (ec);
5412 if (init == null)
5413 return false;
5415 Expression move_next_expr;
5417 var mi = new List<MemberSpec> (1) { move_next };
5418 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5419 mg.InstanceExpression = enumerator;
5421 move_next_expr = new Invocation (mg, null);
5424 get_current.InstanceExpression = enumerator;
5426 Statement block = new CollectionForeachStatement (
5427 var_type.Type, variable, get_current, statement, loc);
5429 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5432 bool implements_idisposable = enumerator_type.ImplementsInterface (TypeManager.idisposable_type);
5433 if (implements_idisposable || !enumerator_type.IsSealed) {
5434 wrapper = new DisposableWrapper (this, implements_idisposable);
5435 } else {
5436 wrapper = new NonDisposableWrapper (this);
5439 return wrapper.Resolve (ec);
5442 protected override void DoEmit (EmitContext ec)
5444 wrapper.Emit (ec);
5447 class NonDisposableWrapper : Statement {
5448 CollectionForeach parent;
5450 internal NonDisposableWrapper (CollectionForeach parent)
5452 this.parent = parent;
5455 protected override void CloneTo (CloneContext clonectx, Statement target)
5457 throw new NotSupportedException ();
5460 public override bool Resolve (BlockContext ec)
5462 return parent.ResolveLoop (ec);
5465 protected override void DoEmit (EmitContext ec)
5467 parent.EmitLoopInit (ec);
5468 parent.EmitLoopBody (ec);
5472 sealed class DisposableWrapper : ExceptionStatement
5474 CollectionForeach parent;
5475 bool implements_idisposable;
5477 internal DisposableWrapper (CollectionForeach parent, bool implements)
5479 this.parent = parent;
5480 this.implements_idisposable = implements;
5483 protected override void CloneTo (CloneContext clonectx, Statement target)
5485 throw new NotSupportedException ();
5488 public override bool Resolve (BlockContext ec)
5490 bool ok = true;
5492 ec.StartFlowBranching (this);
5494 if (!parent.ResolveLoop (ec))
5495 ok = false;
5497 ec.EndFlowBranching ();
5499 ok &= base.Resolve (ec);
5501 if (TypeManager.void_dispose_void == null) {
5502 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5503 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5505 return ok;
5508 protected override void EmitPreTryBody (EmitContext ec)
5510 parent.EmitLoopInit (ec);
5513 protected override void EmitTryBody (EmitContext ec)
5515 parent.EmitLoopBody (ec);
5518 protected override void EmitFinallyBody (EmitContext ec)
5520 Expression instance = parent.enumerator;
5521 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5523 parent.enumerator.Emit (ec);
5525 Label call_dispose = ec.DefineLabel ();
5527 if (!implements_idisposable) {
5528 ec.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5529 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5530 temp.Store (ec);
5531 temp.Emit (ec);
5532 instance = temp;
5535 ec.Emit (OpCodes.Brtrue_S, call_dispose);
5537 // using 'endfinally' to empty the evaluation stack
5538 ec.Emit (OpCodes.Endfinally);
5539 ec.MarkLabel (call_dispose);
5542 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5546 bool ResolveLoop (BlockContext ec)
5548 return loop.Resolve (ec);
5551 void EmitLoopInit (EmitContext ec)
5553 enumerator.EmitAssign (ec, init);
5556 void EmitLoopBody (EmitContext ec)
5558 loop.Emit (ec);
5562 Expression type;
5563 Expression variable;
5564 Expression expr;
5565 Statement statement;
5567 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5568 Statement stmt, Location l)
5570 this.type = type;
5571 this.variable = var;
5572 this.expr = expr;
5573 statement = stmt;
5574 loc = l;
5577 public Statement Statement {
5578 get { return statement; }
5581 public override bool Resolve (BlockContext ec)
5583 expr = expr.Resolve (ec);
5584 if (expr == null)
5585 return false;
5587 if (expr.IsNull) {
5588 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5589 return false;
5592 if (expr.Type == TypeManager.string_type) {
5593 statement = new ArrayForeach (this, 1);
5594 } else if (expr.Type is ArrayContainer) {
5595 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5596 } else {
5597 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5598 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5599 expr.ExprClassName);
5600 return false;
5603 statement = new CollectionForeach (type, variable, expr, statement, loc);
5606 return statement.Resolve (ec);
5609 protected override void DoEmit (EmitContext ec)
5611 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5612 ec.LoopBegin = ec.DefineLabel ();
5613 ec.LoopEnd = ec.DefineLabel ();
5615 statement.Emit (ec);
5617 ec.LoopBegin = old_begin;
5618 ec.LoopEnd = old_end;
5621 protected override void CloneTo (CloneContext clonectx, Statement t)
5623 Foreach target = (Foreach) t;
5625 target.type = type.Clone (clonectx);
5626 target.variable = variable.Clone (clonectx);
5627 target.expr = expr.Clone (clonectx);
5628 target.statement = statement.Clone (clonectx);