2010-03-05 Rodrigo Kumpera <rkumpera@novell.com>
[mcs.git] / mcs / statement.cs
bloba19150d5115daa1b535b356516bb22daccab22f0
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);
96 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
99 public sealed class EmptyStatement : Statement {
101 private EmptyStatement () {}
103 public static readonly EmptyStatement Value = new EmptyStatement ();
105 public override bool Resolve (BlockContext ec)
107 return true;
110 public override bool ResolveUnreachable (BlockContext ec, bool warn)
112 return true;
115 protected override void DoEmit (EmitContext ec)
119 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
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 void MutateHoistedGenericType (AnonymousMethodStorey storey)
154 expr.MutateHoistedGenericType (storey);
155 TrueStatement.MutateHoistedGenericType (storey);
156 if (FalseStatement != null)
157 FalseStatement.MutateHoistedGenericType (storey);
160 public override bool Resolve (BlockContext ec)
162 bool ok = true;
164 Report.Debug (1, "START IF BLOCK", loc);
166 expr = expr.Resolve (ec);
167 if (expr == null) {
168 ok = false;
169 } else {
171 // Dead code elimination
173 if (expr is Constant) {
174 bool take = !((Constant) expr).IsDefaultValue;
176 if (take) {
177 if (!TrueStatement.Resolve (ec))
178 return false;
180 if ((FalseStatement != null) &&
181 !FalseStatement.ResolveUnreachable (ec, true))
182 return false;
183 FalseStatement = null;
184 } else {
185 if (!TrueStatement.ResolveUnreachable (ec, true))
186 return false;
187 TrueStatement = null;
189 if ((FalseStatement != null) &&
190 !FalseStatement.Resolve (ec))
191 return false;
194 return true;
198 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
200 ok &= TrueStatement.Resolve (ec);
202 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
204 ec.CurrentBranching.CreateSibling ();
206 if (FalseStatement != null)
207 ok &= FalseStatement.Resolve (ec);
209 ec.EndFlowBranching ();
211 Report.Debug (1, "END IF BLOCK", loc);
213 return ok;
216 protected override void DoEmit (EmitContext ec)
218 ILGenerator ig = ec.ig;
219 Label false_target = ig.DefineLabel ();
220 Label end;
223 // If we're a boolean constant, Resolve() already
224 // eliminated dead code for us.
226 Constant c = expr as Constant;
227 if (c != null){
228 c.EmitSideEffect (ec);
230 if (!c.IsDefaultValue)
231 TrueStatement.Emit (ec);
232 else if (FalseStatement != null)
233 FalseStatement.Emit (ec);
235 return;
238 expr.EmitBranchable (ec, false_target, false);
240 TrueStatement.Emit (ec);
242 if (FalseStatement != null){
243 bool branch_emitted = false;
245 end = ig.DefineLabel ();
246 if (!is_true_ret){
247 ig.Emit (OpCodes.Br, end);
248 branch_emitted = true;
251 ig.MarkLabel (false_target);
252 FalseStatement.Emit (ec);
254 if (branch_emitted)
255 ig.MarkLabel (end);
256 } else {
257 ig.MarkLabel (false_target);
261 protected override void CloneTo (CloneContext clonectx, Statement t)
263 If target = (If) t;
265 target.expr = expr.Clone (clonectx);
266 target.TrueStatement = TrueStatement.Clone (clonectx);
267 if (FalseStatement != null)
268 target.FalseStatement = FalseStatement.Clone (clonectx);
272 public class Do : Statement {
273 public Expression expr;
274 public Statement EmbeddedStatement;
276 public Do (Statement statement, BooleanExpression bool_expr, Location l)
278 expr = bool_expr;
279 EmbeddedStatement = statement;
280 loc = l;
283 public override bool Resolve (BlockContext ec)
285 bool ok = true;
287 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
289 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
291 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
292 if (!EmbeddedStatement.Resolve (ec))
293 ok = false;
294 ec.EndFlowBranching ();
296 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
297 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
299 expr = expr.Resolve (ec);
300 if (expr == null)
301 ok = false;
302 else if (expr is Constant){
303 bool infinite = !((Constant) expr).IsDefaultValue;
304 if (infinite)
305 ec.CurrentBranching.CurrentUsageVector.Goto ();
308 ec.EndFlowBranching ();
310 return ok;
313 protected override void DoEmit (EmitContext ec)
315 ILGenerator ig = ec.ig;
316 Label loop = ig.DefineLabel ();
317 Label old_begin = ec.LoopBegin;
318 Label old_end = ec.LoopEnd;
320 ec.LoopBegin = ig.DefineLabel ();
321 ec.LoopEnd = ig.DefineLabel ();
323 ig.MarkLabel (loop);
324 EmbeddedStatement.Emit (ec);
325 ig.MarkLabel (ec.LoopBegin);
328 // Dead code elimination
330 if (expr is Constant){
331 bool res = !((Constant) expr).IsDefaultValue;
333 expr.EmitSideEffect (ec);
334 if (res)
335 ec.ig.Emit (OpCodes.Br, loop);
336 } else
337 expr.EmitBranchable (ec, loop, true);
339 ig.MarkLabel (ec.LoopEnd);
341 ec.LoopBegin = old_begin;
342 ec.LoopEnd = old_end;
345 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
347 expr.MutateHoistedGenericType (storey);
348 EmbeddedStatement.MutateHoistedGenericType (storey);
351 protected override void CloneTo (CloneContext clonectx, Statement t)
353 Do target = (Do) t;
355 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
356 target.expr = expr.Clone (clonectx);
360 public class While : Statement {
361 public Expression expr;
362 public Statement Statement;
363 bool infinite, empty;
365 public While (BooleanExpression bool_expr, Statement statement, Location l)
367 this.expr = bool_expr;
368 Statement = statement;
369 loc = l;
372 public override bool Resolve (BlockContext ec)
374 bool ok = true;
376 expr = expr.Resolve (ec);
377 if (expr == null)
378 ok = false;
381 // Inform whether we are infinite or not
383 if (expr is Constant){
384 bool value = !((Constant) expr).IsDefaultValue;
386 if (value == false){
387 if (!Statement.ResolveUnreachable (ec, true))
388 return false;
389 empty = true;
390 return true;
391 } else
392 infinite = true;
395 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
396 if (!infinite)
397 ec.CurrentBranching.CreateSibling ();
399 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
400 if (!Statement.Resolve (ec))
401 ok = false;
402 ec.EndFlowBranching ();
404 // There's no direct control flow from the end of the embedded statement to the end of the loop
405 ec.CurrentBranching.CurrentUsageVector.Goto ();
407 ec.EndFlowBranching ();
409 return ok;
412 protected override void DoEmit (EmitContext ec)
414 if (empty) {
415 expr.EmitSideEffect (ec);
416 return;
419 ILGenerator ig = ec.ig;
420 Label old_begin = ec.LoopBegin;
421 Label old_end = ec.LoopEnd;
423 ec.LoopBegin = ig.DefineLabel ();
424 ec.LoopEnd = ig.DefineLabel ();
427 // Inform whether we are infinite or not
429 if (expr is Constant){
430 // expr is 'true', since the 'empty' case above handles the 'false' case
431 ig.MarkLabel (ec.LoopBegin);
432 expr.EmitSideEffect (ec);
433 Statement.Emit (ec);
434 ig.Emit (OpCodes.Br, ec.LoopBegin);
437 // Inform that we are infinite (ie, `we return'), only
438 // if we do not `break' inside the code.
440 ig.MarkLabel (ec.LoopEnd);
441 } else {
442 Label while_loop = ig.DefineLabel ();
444 ig.Emit (OpCodes.Br, ec.LoopBegin);
445 ig.MarkLabel (while_loop);
447 Statement.Emit (ec);
449 ig.MarkLabel (ec.LoopBegin);
450 ec.Mark (loc);
452 expr.EmitBranchable (ec, while_loop, true);
454 ig.MarkLabel (ec.LoopEnd);
457 ec.LoopBegin = old_begin;
458 ec.LoopEnd = old_end;
461 public override void Emit (EmitContext ec)
463 DoEmit (ec);
466 protected override void CloneTo (CloneContext clonectx, Statement t)
468 While target = (While) t;
470 target.expr = expr.Clone (clonectx);
471 target.Statement = Statement.Clone (clonectx);
474 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
476 expr.MutateHoistedGenericType (storey);
477 Statement.MutateHoistedGenericType (storey);
481 public class For : Statement {
482 Expression Test;
483 Statement InitStatement;
484 Statement Increment;
485 public Statement Statement;
486 bool infinite, empty;
488 public For (Statement init_statement,
489 BooleanExpression test,
490 Statement increment,
491 Statement statement,
492 Location l)
494 InitStatement = init_statement;
495 Test = test;
496 Increment = increment;
497 Statement = statement;
498 loc = l;
501 public override bool Resolve (BlockContext ec)
503 bool ok = true;
505 if (InitStatement != null){
506 if (!InitStatement.Resolve (ec))
507 ok = false;
510 if (Test != null){
511 Test = Test.Resolve (ec);
512 if (Test == null)
513 ok = false;
514 else if (Test is Constant){
515 bool value = !((Constant) Test).IsDefaultValue;
517 if (value == false){
518 if (!Statement.ResolveUnreachable (ec, true))
519 return false;
520 if ((Increment != null) &&
521 !Increment.ResolveUnreachable (ec, false))
522 return false;
523 empty = true;
524 return true;
525 } else
526 infinite = true;
528 } else
529 infinite = true;
531 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
532 if (!infinite)
533 ec.CurrentBranching.CreateSibling ();
535 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
537 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
538 if (!Statement.Resolve (ec))
539 ok = false;
540 ec.EndFlowBranching ();
542 if (Increment != null){
543 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
544 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
545 ok = false;
546 } else {
547 if (!Increment.Resolve (ec))
548 ok = false;
552 // There's no direct control flow from the end of the embedded statement to the end of the loop
553 ec.CurrentBranching.CurrentUsageVector.Goto ();
555 ec.EndFlowBranching ();
557 return ok;
560 protected override void DoEmit (EmitContext ec)
562 if (InitStatement != null && InitStatement != EmptyStatement.Value)
563 InitStatement.Emit (ec);
565 if (empty) {
566 Test.EmitSideEffect (ec);
567 return;
570 ILGenerator ig = ec.ig;
571 Label old_begin = ec.LoopBegin;
572 Label old_end = ec.LoopEnd;
573 Label loop = ig.DefineLabel ();
574 Label test = ig.DefineLabel ();
576 ec.LoopBegin = ig.DefineLabel ();
577 ec.LoopEnd = ig.DefineLabel ();
579 ig.Emit (OpCodes.Br, test);
580 ig.MarkLabel (loop);
581 Statement.Emit (ec);
583 ig.MarkLabel (ec.LoopBegin);
584 if (Increment != EmptyStatement.Value)
585 Increment.Emit (ec);
587 ig.MarkLabel (test);
589 // If test is null, there is no test, and we are just
590 // an infinite loop
592 if (Test != null){
594 // The Resolve code already catches the case for
595 // Test == Constant (false) so we know that
596 // this is true
598 if (Test is Constant) {
599 Test.EmitSideEffect (ec);
600 ig.Emit (OpCodes.Br, loop);
601 } else {
602 Test.EmitBranchable (ec, loop, true);
605 } else
606 ig.Emit (OpCodes.Br, loop);
607 ig.MarkLabel (ec.LoopEnd);
609 ec.LoopBegin = old_begin;
610 ec.LoopEnd = old_end;
613 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
615 if (InitStatement != null)
616 InitStatement.MutateHoistedGenericType (storey);
617 if (Test != null)
618 Test.MutateHoistedGenericType (storey);
619 if (Increment != null)
620 Increment.MutateHoistedGenericType (storey);
622 Statement.MutateHoistedGenericType (storey);
625 protected override void CloneTo (CloneContext clonectx, Statement t)
627 For target = (For) t;
629 if (InitStatement != null)
630 target.InitStatement = InitStatement.Clone (clonectx);
631 if (Test != null)
632 target.Test = Test.Clone (clonectx);
633 if (Increment != null)
634 target.Increment = Increment.Clone (clonectx);
635 target.Statement = Statement.Clone (clonectx);
639 public class StatementExpression : Statement {
640 ExpressionStatement expr;
642 public StatementExpression (ExpressionStatement expr)
644 this.expr = expr;
645 loc = expr.Location;
648 public override bool Resolve (BlockContext ec)
650 expr = expr.ResolveStatement (ec);
651 return expr != null;
654 protected override void DoEmit (EmitContext ec)
656 expr.EmitStatement (ec);
659 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
661 expr.MutateHoistedGenericType (storey);
664 public override string ToString ()
666 return "StatementExpression (" + expr + ")";
669 protected override void CloneTo (CloneContext clonectx, Statement t)
671 StatementExpression target = (StatementExpression) t;
673 target.expr = (ExpressionStatement) expr.Clone (clonectx);
677 // A 'return' or a 'yield break'
678 public abstract class ExitStatement : Statement
680 protected bool unwind_protect;
681 protected abstract bool DoResolve (BlockContext ec);
683 public virtual void Error_FinallyClause (Report Report)
685 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
688 public sealed override bool Resolve (BlockContext ec)
690 if (!DoResolve (ec))
691 return false;
693 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
694 if (unwind_protect)
695 ec.NeedReturnLabel ();
696 ec.CurrentBranching.CurrentUsageVector.Goto ();
697 return true;
701 /// <summary>
702 /// Implements the return statement
703 /// </summary>
704 public class Return : ExitStatement {
705 protected Expression Expr;
706 public Return (Expression expr, Location l)
708 Expr = expr;
709 loc = l;
712 protected override bool DoResolve (BlockContext ec)
714 if (Expr == null) {
715 if (ec.ReturnType == TypeManager.void_type)
716 return true;
718 ec.Report.Error (126, loc,
719 "An object of a type convertible to `{0}' is required for the return statement",
720 TypeManager.CSharpName (ec.ReturnType));
721 return false;
724 if (ec.CurrentBlock.Toplevel.IsIterator) {
725 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
726 "statement to return a value, or yield break to end the iteration");
729 AnonymousExpression am = ec.CurrentAnonymousMethod;
730 if (am == null && ec.ReturnType == TypeManager.void_type) {
731 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
732 ec.GetSignatureForError ());
735 Expr = Expr.Resolve (ec);
736 if (Expr == null)
737 return false;
739 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
740 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
741 return true;
744 if (Expr.Type != ec.ReturnType) {
745 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
747 if (Expr == null) {
748 if (am != null) {
749 ec.Report.Error (1662, loc,
750 "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",
751 am.ContainerType, am.GetSignatureForError ());
753 return false;
757 return true;
760 protected override void DoEmit (EmitContext ec)
762 if (Expr != null) {
763 Expr.Emit (ec);
765 if (unwind_protect)
766 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
769 if (unwind_protect)
770 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
771 else
772 ec.ig.Emit (OpCodes.Ret);
775 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
777 if (Expr != null)
778 Expr.MutateHoistedGenericType (storey);
781 protected override void CloneTo (CloneContext clonectx, Statement t)
783 Return target = (Return) t;
784 // It's null for simple return;
785 if (Expr != null)
786 target.Expr = Expr.Clone (clonectx);
790 public class Goto : Statement {
791 string target;
792 LabeledStatement label;
793 bool unwind_protect;
795 public override bool Resolve (BlockContext ec)
797 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
798 ec.CurrentBranching.CurrentUsageVector.Goto ();
799 return true;
802 public Goto (string label, Location l)
804 loc = l;
805 target = label;
808 public string Target {
809 get { return target; }
812 public void SetResolvedTarget (LabeledStatement label)
814 this.label = label;
815 label.AddReference ();
818 protected override void CloneTo (CloneContext clonectx, Statement target)
820 // Nothing to clone
823 protected override void DoEmit (EmitContext ec)
825 if (label == null)
826 throw new InternalErrorException ("goto emitted before target resolved");
827 Label l = label.LabelTarget (ec);
828 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
831 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
836 public class LabeledStatement : Statement {
837 string name;
838 bool defined;
839 bool referenced;
840 Label label;
841 ILGenerator ig;
843 FlowBranching.UsageVector vectors;
845 public LabeledStatement (string name, Location l)
847 this.name = name;
848 this.loc = l;
851 public Label LabelTarget (EmitContext ec)
853 if (defined)
854 return label;
855 ig = ec.ig;
856 label = ec.ig.DefineLabel ();
857 defined = true;
859 return label;
862 public string Name {
863 get { return name; }
866 public bool IsDefined {
867 get { return defined; }
870 public bool HasBeenReferenced {
871 get { return referenced; }
874 public FlowBranching.UsageVector JumpOrigins {
875 get { return vectors; }
878 public void AddUsageVector (FlowBranching.UsageVector vector)
880 vector = vector.Clone ();
881 vector.Next = vectors;
882 vectors = vector;
885 protected override void CloneTo (CloneContext clonectx, Statement target)
887 // nothing to clone
890 public override bool Resolve (BlockContext ec)
892 // this flow-branching will be terminated when the surrounding block ends
893 ec.StartFlowBranching (this);
894 return true;
897 protected override void DoEmit (EmitContext ec)
899 if (ig != null && ig != ec.ig)
900 throw new InternalErrorException ("cannot happen");
901 LabelTarget (ec);
902 ec.ig.MarkLabel (label);
905 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
909 public void AddReference ()
911 referenced = true;
916 /// <summary>
917 /// `goto default' statement
918 /// </summary>
919 public class GotoDefault : Statement {
921 public GotoDefault (Location l)
923 loc = l;
926 protected override void CloneTo (CloneContext clonectx, Statement target)
928 // nothing to clone
931 public override bool Resolve (BlockContext ec)
933 ec.CurrentBranching.CurrentUsageVector.Goto ();
935 if (ec.Switch == null) {
936 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
937 return false;
940 if (!ec.Switch.GotDefault) {
941 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
942 return false;
945 return true;
948 protected override void DoEmit (EmitContext ec)
950 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
953 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
958 /// <summary>
959 /// `goto case' statement
960 /// </summary>
961 public class GotoCase : Statement {
962 Expression expr;
963 SwitchLabel sl;
965 public GotoCase (Expression e, Location l)
967 expr = e;
968 loc = l;
971 public override bool Resolve (BlockContext ec)
973 if (ec.Switch == null){
974 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
975 return false;
978 ec.CurrentBranching.CurrentUsageVector.Goto ();
980 expr = expr.Resolve (ec);
981 if (expr == null)
982 return false;
984 Constant c = expr as Constant;
985 if (c == null) {
986 ec.Report.Error (150, expr.Location, "A constant value is expected");
987 return false;
990 Type type = ec.Switch.SwitchType;
991 Constant res = c.TryReduce (ec, type, c.Location);
992 if (res == null) {
993 c.Error_ValueCannotBeConverted (ec, loc, type, true);
994 return false;
997 if (!Convert.ImplicitStandardConversionExists (c, type))
998 ec.Report.Warning (469, 2, loc,
999 "The `goto case' value is not implicitly convertible to type `{0}'",
1000 TypeManager.CSharpName (type));
1002 object val = res.GetValue ();
1003 if (val == null)
1004 val = SwitchLabel.NullStringCase;
1006 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1007 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1008 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1009 return false;
1012 return true;
1015 protected override void DoEmit (EmitContext ec)
1017 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1020 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1022 expr.MutateHoistedGenericType (storey);
1025 protected override void CloneTo (CloneContext clonectx, Statement t)
1027 GotoCase target = (GotoCase) t;
1029 target.expr = expr.Clone (clonectx);
1033 public class Throw : Statement {
1034 Expression expr;
1036 public Throw (Expression expr, Location l)
1038 this.expr = expr;
1039 loc = l;
1042 public override bool Resolve (BlockContext ec)
1044 if (expr == null) {
1045 ec.CurrentBranching.CurrentUsageVector.Goto ();
1046 return ec.CurrentBranching.CheckRethrow (loc);
1049 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1050 ec.CurrentBranching.CurrentUsageVector.Goto ();
1052 if (expr == null)
1053 return false;
1055 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1056 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1057 else
1058 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1060 return true;
1063 protected override void DoEmit (EmitContext ec)
1065 if (expr == null)
1066 ec.ig.Emit (OpCodes.Rethrow);
1067 else {
1068 expr.Emit (ec);
1070 ec.ig.Emit (OpCodes.Throw);
1074 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1076 if (expr != null)
1077 expr.MutateHoistedGenericType (storey);
1080 protected override void CloneTo (CloneContext clonectx, Statement t)
1082 Throw target = (Throw) t;
1084 if (expr != null)
1085 target.expr = expr.Clone (clonectx);
1089 public class Break : Statement {
1091 public Break (Location l)
1093 loc = l;
1096 bool unwind_protect;
1098 public override bool Resolve (BlockContext ec)
1100 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1101 ec.CurrentBranching.CurrentUsageVector.Goto ();
1102 return true;
1105 protected override void DoEmit (EmitContext ec)
1107 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1110 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1114 protected override void CloneTo (CloneContext clonectx, Statement t)
1116 // nothing needed
1120 public class Continue : Statement {
1122 public Continue (Location l)
1124 loc = l;
1127 bool unwind_protect;
1129 public override bool Resolve (BlockContext ec)
1131 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1132 ec.CurrentBranching.CurrentUsageVector.Goto ();
1133 return true;
1136 protected override void DoEmit (EmitContext ec)
1138 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1141 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1145 protected override void CloneTo (CloneContext clonectx, Statement t)
1147 // nothing needed.
1151 public interface ILocalVariable
1153 void Emit (EmitContext ec);
1154 void EmitAssign (EmitContext ec);
1155 void EmitAddressOf (EmitContext ec);
1158 public interface IKnownVariable {
1159 Block Block { get; }
1160 Location Location { get; }
1164 // The information about a user-perceived local variable
1166 public class LocalInfo : IKnownVariable, ILocalVariable {
1167 public readonly FullNamedExpression Type;
1169 public Type VariableType;
1170 public readonly string Name;
1171 public readonly Location Location;
1172 public readonly Block Block;
1174 public VariableInfo VariableInfo;
1175 HoistedVariable hoisted_variant;
1177 [Flags]
1178 enum Flags : byte {
1179 Used = 1,
1180 ReadOnly = 2,
1181 Pinned = 4,
1182 IsThis = 8,
1183 AddressTaken = 32,
1184 CompilerGenerated = 64,
1185 IsConstant = 128
1188 public enum ReadOnlyContext: byte {
1189 Using,
1190 Foreach,
1191 Fixed
1194 Flags flags;
1195 ReadOnlyContext ro_context;
1196 LocalBuilder builder;
1198 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1200 Type = type;
1201 Name = name;
1202 Block = block;
1203 Location = l;
1206 public LocalInfo (DeclSpace ds, Block block, Location l)
1208 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1209 Block = block;
1210 Location = l;
1213 public void ResolveVariable (EmitContext ec)
1215 if (HoistedVariant != null)
1216 return;
1218 if (builder == null) {
1219 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType), Pinned);
1223 public void Emit (EmitContext ec)
1225 ec.ig.Emit (OpCodes.Ldloc, builder);
1228 public void EmitAssign (EmitContext ec)
1230 ec.ig.Emit (OpCodes.Stloc, builder);
1233 public void EmitAddressOf (EmitContext ec)
1235 ec.ig.Emit (OpCodes.Ldloca, builder);
1238 public void EmitSymbolInfo (EmitContext ec)
1240 if (builder != null)
1241 ec.DefineLocalVariable (Name, builder);
1245 // Hoisted local variable variant
1247 public HoistedVariable HoistedVariant {
1248 get {
1249 return hoisted_variant;
1251 set {
1252 hoisted_variant = value;
1256 public bool IsThisAssigned (BlockContext ec, Block block)
1258 if (VariableInfo == null)
1259 throw new Exception ();
1261 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1262 return true;
1264 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1267 public bool IsAssigned (BlockContext ec)
1269 if (VariableInfo == null)
1270 throw new Exception ();
1272 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1275 public bool Resolve (ResolveContext ec)
1277 if (VariableType != null)
1278 return true;
1280 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1281 if (texpr == null)
1282 return false;
1284 VariableType = texpr.Type;
1286 if (TypeManager.IsGenericParameter (VariableType))
1287 return true;
1289 if (VariableType.IsAbstract && VariableType.IsSealed) {
1290 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1291 return false;
1294 if (VariableType.IsPointer && !ec.IsUnsafe)
1295 Expression.UnsafeError (ec, Location);
1297 return true;
1300 public bool IsConstant {
1301 get { return (flags & Flags.IsConstant) != 0; }
1302 set { flags |= Flags.IsConstant; }
1305 public bool AddressTaken {
1306 get { return (flags & Flags.AddressTaken) != 0; }
1307 set { flags |= Flags.AddressTaken; }
1310 public bool CompilerGenerated {
1311 get { return (flags & Flags.CompilerGenerated) != 0; }
1312 set { flags |= Flags.CompilerGenerated; }
1315 public override string ToString ()
1317 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1318 Name, Type, VariableInfo, Location);
1321 public bool Used {
1322 get { return (flags & Flags.Used) != 0; }
1323 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1326 public bool ReadOnly {
1327 get { return (flags & Flags.ReadOnly) != 0; }
1330 public void SetReadOnlyContext (ReadOnlyContext context)
1332 flags |= Flags.ReadOnly;
1333 ro_context = context;
1336 public string GetReadOnlyContext ()
1338 if (!ReadOnly)
1339 throw new InternalErrorException ("Variable is not readonly");
1341 switch (ro_context) {
1342 case ReadOnlyContext.Fixed:
1343 return "fixed variable";
1344 case ReadOnlyContext.Foreach:
1345 return "foreach iteration variable";
1346 case ReadOnlyContext.Using:
1347 return "using variable";
1349 throw new NotImplementedException ();
1353 // Whether the variable is pinned, if Pinned the variable has been
1354 // allocated in a pinned slot with DeclareLocal.
1356 public bool Pinned {
1357 get { return (flags & Flags.Pinned) != 0; }
1358 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1361 public bool IsThis {
1362 get { return (flags & Flags.IsThis) != 0; }
1363 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1366 Block IKnownVariable.Block {
1367 get { return Block; }
1370 Location IKnownVariable.Location {
1371 get { return Location; }
1374 public LocalInfo Clone (CloneContext clonectx)
1377 // Variables in anonymous block are not resolved yet
1379 if (VariableType == null)
1380 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1383 // Variables in method block are resolved
1385 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1386 li.VariableType = VariableType;
1387 return li;
1391 /// <summary>
1392 /// Block represents a C# block.
1393 /// </summary>
1395 /// <remarks>
1396 /// This class is used in a number of places: either to represent
1397 /// explicit blocks that the programmer places or implicit blocks.
1399 /// Implicit blocks are used as labels or to introduce variable
1400 /// declarations.
1402 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1403 /// they contain extra information that is not necessary on normal blocks.
1404 /// </remarks>
1405 public class Block : Statement {
1406 public Block Parent;
1407 public Location StartLocation;
1408 public Location EndLocation = Location.Null;
1410 public ExplicitBlock Explicit;
1411 public ToplevelBlock Toplevel; // TODO: Use Explicit
1413 [Flags]
1414 public enum Flags
1416 Unchecked = 1,
1417 BlockUsed = 2,
1418 VariablesInitialized = 4,
1419 HasRet = 8,
1420 Unsafe = 16,
1421 IsIterator = 32,
1422 HasCapturedVariable = 64,
1423 HasCapturedThis = 1 << 7,
1424 IsExpressionTree = 1 << 8
1427 protected Flags flags;
1429 public bool Unchecked {
1430 get { return (flags & Flags.Unchecked) != 0; }
1431 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1434 public bool Unsafe {
1435 get { return (flags & Flags.Unsafe) != 0; }
1436 set { flags |= Flags.Unsafe; }
1440 // The statements in this block
1442 protected List<Statement> statements;
1445 // An array of Blocks. We keep track of children just
1446 // to generate the local variable declarations.
1448 // Statements and child statements are handled through the
1449 // statements.
1451 List<Block> children;
1454 // Labels. (label, block) pairs.
1456 protected Dictionary<string, LabeledStatement> labels;
1459 // Keeps track of (name, type) pairs
1461 Dictionary<string, LocalInfo> variables;
1464 // Keeps track of constants
1465 Dictionary<string, Expression> constants;
1468 // Temporary variables.
1470 List<LocalInfo> temporary_variables;
1473 // If this is a switch section, the enclosing switch block.
1475 Block switch_block;
1477 protected List<Statement> scope_initializers;
1479 List<ToplevelBlock> anonymous_children;
1481 protected static int id;
1483 int this_id;
1485 int assignable_slots;
1486 bool unreachable_shown;
1487 bool unreachable;
1489 public Block (Block parent)
1490 : this (parent, (Flags) 0, Location.Null, Location.Null)
1493 public Block (Block parent, Flags flags)
1494 : this (parent, flags, Location.Null, Location.Null)
1497 public Block (Block parent, Location start, Location end)
1498 : this (parent, (Flags) 0, start, end)
1502 // Useful when TopLevel block is downgraded to normal block
1504 public Block (ToplevelBlock parent, ToplevelBlock source)
1505 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1507 statements = source.statements;
1508 children = source.children;
1509 labels = source.labels;
1510 variables = source.variables;
1511 constants = source.constants;
1512 switch_block = source.switch_block;
1515 public Block (Block parent, Flags flags, Location start, Location end)
1517 if (parent != null) {
1518 parent.AddChild (this);
1520 // the appropriate constructors will fixup these fields
1521 Toplevel = parent.Toplevel;
1522 Explicit = parent.Explicit;
1525 this.Parent = parent;
1526 this.flags = flags;
1527 this.StartLocation = start;
1528 this.EndLocation = end;
1529 this.loc = start;
1530 this_id = id++;
1531 statements = new List<Statement> (4);
1534 public Block CreateSwitchBlock (Location start)
1536 // FIXME: should this be implicit?
1537 Block new_block = new ExplicitBlock (this, start, start);
1538 new_block.switch_block = this;
1539 return new_block;
1542 public int ID {
1543 get { return this_id; }
1546 public IDictionary<string, LocalInfo> Variables {
1547 get {
1548 if (variables == null)
1549 variables = new Dictionary<string, LocalInfo> ();
1550 return variables;
1554 void AddChild (Block b)
1556 if (children == null)
1557 children = new List<Block> (1);
1559 children.Add (b);
1562 public void SetEndLocation (Location loc)
1564 EndLocation = loc;
1567 protected void Error_158 (string name, Location loc)
1569 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1570 "by the same name in a contained scope", name);
1573 /// <summary>
1574 /// Adds a label to the current block.
1575 /// </summary>
1577 /// <returns>
1578 /// false if the name already exists in this block. true
1579 /// otherwise.
1580 /// </returns>
1582 public bool AddLabel (LabeledStatement target)
1584 if (switch_block != null)
1585 return switch_block.AddLabel (target);
1587 string name = target.Name;
1589 Block cur = this;
1590 while (cur != null) {
1591 LabeledStatement s = cur.DoLookupLabel (name);
1592 if (s != null) {
1593 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1594 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1595 return false;
1598 if (this == Explicit)
1599 break;
1601 cur = cur.Parent;
1604 while (cur != null) {
1605 if (cur.DoLookupLabel (name) != null) {
1606 Error_158 (name, target.loc);
1607 return false;
1610 if (children != null) {
1611 foreach (Block b in children) {
1612 LabeledStatement s = b.DoLookupLabel (name);
1613 if (s == null)
1614 continue;
1616 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1617 Error_158 (name, target.loc);
1618 return false;
1622 cur = cur.Parent;
1625 Toplevel.CheckError158 (name, target.loc);
1627 if (labels == null)
1628 labels = new Dictionary<string, LabeledStatement> ();
1630 labels.Add (name, target);
1631 return true;
1634 public LabeledStatement LookupLabel (string name)
1636 LabeledStatement s = DoLookupLabel (name);
1637 if (s != null)
1638 return s;
1640 if (children == null)
1641 return null;
1643 foreach (Block child in children) {
1644 if (Explicit != child.Explicit)
1645 continue;
1647 s = child.LookupLabel (name);
1648 if (s != null)
1649 return s;
1652 return null;
1655 LabeledStatement DoLookupLabel (string name)
1657 if (switch_block != null)
1658 return switch_block.LookupLabel (name);
1660 if (labels != null)
1661 if (labels.ContainsKey (name))
1662 return labels [name];
1664 return null;
1667 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1669 Block b = this;
1670 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1671 while (kvi == null) {
1672 b = b.Explicit.Parent;
1673 if (b == null)
1674 return true;
1675 kvi = b.Explicit.GetKnownVariable (name);
1678 if (kvi.Block == b)
1679 return true;
1681 // Is kvi.Block nested inside 'b'
1682 if (b.Explicit != kvi.Block.Explicit) {
1684 // If a variable by the same name it defined in a nested block of this
1685 // block, we violate the invariant meaning in a block.
1687 if (b == this) {
1688 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1689 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1690 return false;
1694 // It's ok if the definition is in a nested subblock of b, but not
1695 // nested inside this block -- a definition in a sibling block
1696 // should not affect us.
1698 return true;
1702 // Block 'b' and kvi.Block are the same textual block.
1703 // However, different variables are extant.
1705 // Check if the variable is in scope in both blocks. We use
1706 // an indirect check that depends on AddVariable doing its
1707 // part in maintaining the invariant-meaning-in-block property.
1709 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1710 return true;
1712 if (this is ToplevelBlock) {
1713 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1714 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1715 return false;
1719 // Even though we detected the error when the name is used, we
1720 // treat it as if the variable declaration was in error.
1722 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1723 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1724 return false;
1727 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1729 LocalInfo vi = GetLocalInfo (name);
1730 if (vi != null) {
1731 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1732 if (Explicit == vi.Block.Explicit) {
1733 Error_AlreadyDeclared (l, name, null);
1734 } else {
1735 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1736 "parent or current" : "parent");
1738 return false;
1741 if (block != null) {
1742 Expression e = block.GetParameterReference (name, Location.Null);
1743 if (e != null) {
1744 ParameterReference pr = e as ParameterReference;
1745 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1746 Error_AlreadyDeclared (loc, name);
1747 else
1748 Error_AlreadyDeclared (loc, name, "parent or current");
1749 return false;
1753 return true;
1756 public LocalInfo AddVariable (Expression type, string name, Location l)
1758 if (!CheckParentConflictName (Toplevel, name, l))
1759 return null;
1761 if (Toplevel.GenericMethod != null) {
1762 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1763 if (tp.Name == name) {
1764 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1765 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1766 return null;
1771 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1772 if (kvi != null) {
1773 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1774 Error_AlreadyDeclared (l, name, "child");
1775 return null;
1778 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1779 AddVariable (vi);
1781 if ((flags & Flags.VariablesInitialized) != 0)
1782 throw new InternalErrorException ("block has already been resolved");
1784 return vi;
1787 protected virtual void AddVariable (LocalInfo li)
1789 Variables.Add (li.Name, li);
1790 Explicit.AddKnownVariable (li.Name, li);
1793 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1795 if (reason == null) {
1796 Error_AlreadyDeclared (loc, var);
1797 return;
1800 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1801 "in this scope because it would give a different meaning " +
1802 "to `{0}', which is already used in a `{1}' scope " +
1803 "to denote something else", var, reason);
1806 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1808 Toplevel.Report.Error (128, loc,
1809 "A local variable named `{0}' is already defined in this scope", name);
1812 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1814 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1815 name, conflict);
1818 public bool AddConstant (Expression type, string name, Expression value, Location l)
1820 if (AddVariable (type, name, l) == null)
1821 return false;
1823 if (constants == null)
1824 constants = new Dictionary<string, Expression> ();
1826 constants.Add (name, value);
1828 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1829 Use ();
1830 return true;
1833 static int next_temp_id = 0;
1835 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1837 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1839 if (temporary_variables == null)
1840 temporary_variables = new List<LocalInfo> ();
1842 int id = ++next_temp_id;
1843 string name = "$s_" + id.ToString ();
1845 LocalInfo li = new LocalInfo (te, name, this, loc);
1846 li.CompilerGenerated = true;
1847 temporary_variables.Add (li);
1848 return li;
1851 public LocalInfo GetLocalInfo (string name)
1853 LocalInfo ret;
1854 for (Block b = this; b != null; b = b.Parent) {
1855 if (b.variables != null) {
1856 if (b.variables.TryGetValue (name, out ret))
1857 return ret;
1861 return null;
1864 public Expression GetVariableType (string name)
1866 LocalInfo vi = GetLocalInfo (name);
1867 return vi == null ? null : vi.Type;
1870 public Expression GetConstantExpression (string name)
1872 Expression ret;
1873 for (Block b = this; b != null; b = b.Parent) {
1874 if (b.constants != null) {
1875 if (b.constants.TryGetValue (name, out ret))
1876 return ret;
1879 return null;
1883 // It should be used by expressions which require to
1884 // register a statement during resolve process.
1886 public void AddScopeStatement (Statement s)
1888 if (scope_initializers == null)
1889 scope_initializers = new List<Statement> ();
1891 scope_initializers.Add (s);
1894 public void AddStatement (Statement s)
1896 statements.Add (s);
1897 flags |= Flags.BlockUsed;
1900 public bool Used {
1901 get { return (flags & Flags.BlockUsed) != 0; }
1904 public void Use ()
1906 flags |= Flags.BlockUsed;
1909 public bool HasRet {
1910 get { return (flags & Flags.HasRet) != 0; }
1913 public int AssignableSlots {
1914 get {
1915 // TODO: Re-enable
1916 // if ((flags & Flags.VariablesInitialized) == 0)
1917 // throw new Exception ("Variables have not been initialized yet");
1918 return assignable_slots;
1922 public IList<ToplevelBlock> AnonymousChildren {
1923 get { return anonymous_children; }
1926 public void AddAnonymousChild (ToplevelBlock b)
1928 if (anonymous_children == null)
1929 anonymous_children = new List<ToplevelBlock> ();
1931 anonymous_children.Add (b);
1934 void DoResolveConstants (BlockContext ec)
1936 if (constants == null)
1937 return;
1939 if (variables == null)
1940 throw new InternalErrorException ("cannot happen");
1942 foreach (var de in variables) {
1943 string name = de.Key;
1944 LocalInfo vi = de.Value;
1945 Type variable_type = vi.VariableType;
1947 if (variable_type == null) {
1948 if (vi.Type is VarExpr)
1949 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1951 continue;
1954 Expression cv;
1955 if (!constants.TryGetValue (name, out cv))
1956 continue;
1958 // Don't let 'const int Foo = Foo;' succeed.
1959 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1960 // which in turn causes the 'must be constant' error to be triggered.
1961 constants.Remove (name);
1963 if (!Const.IsConstantTypeValid (variable_type)) {
1964 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1965 continue;
1968 ec.CurrentBlock = this;
1969 Expression e;
1970 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1971 e = cv.Resolve (ec);
1973 if (e == null)
1974 continue;
1976 Constant ce = e as Constant;
1977 if (ce == null) {
1978 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1979 continue;
1982 e = ce.ConvertImplicitly (ec, variable_type);
1983 if (e == null) {
1984 if (TypeManager.IsReferenceType (variable_type))
1985 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1986 else
1987 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1988 continue;
1991 constants.Add (name, e);
1992 vi.IsConstant = true;
1996 protected void ResolveMeta (BlockContext ec, int offset)
1998 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2000 // If some parent block was unsafe, we remain unsafe even if this block
2001 // isn't explicitly marked as such.
2002 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2003 flags |= Flags.VariablesInitialized;
2005 if (variables != null) {
2006 foreach (LocalInfo li in variables.Values) {
2007 if (!li.Resolve (ec))
2008 continue;
2009 li.VariableInfo = new VariableInfo (li, offset);
2010 offset += li.VariableInfo.Length;
2013 assignable_slots = offset;
2015 DoResolveConstants (ec);
2017 if (children == null)
2018 return;
2019 foreach (Block b in children)
2020 b.ResolveMeta (ec, offset);
2025 // Emits the local variable declarations for a block
2027 public virtual void EmitMeta (EmitContext ec)
2029 if (variables != null){
2030 foreach (LocalInfo vi in variables.Values)
2031 vi.ResolveVariable (ec);
2034 if (temporary_variables != null) {
2035 for (int i = 0; i < temporary_variables.Count; i++)
2036 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2039 if (children != null) {
2040 for (int i = 0; i < children.Count; i++)
2041 ((Block)children[i]).EmitMeta(ec);
2045 void UsageWarning (BlockContext ec)
2047 if (variables == null || ec.Report.WarningLevel < 3)
2048 return;
2050 foreach (var de in variables) {
2051 LocalInfo vi = de.Value;
2053 if (!vi.Used) {
2054 string name = de.Key;
2056 // vi.VariableInfo can be null for 'catch' variables
2057 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2058 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2059 else
2060 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2065 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2067 Statement body;
2069 // Some statements are wrapped by a Block. Since
2070 // others' internal could be changed, here I treat
2071 // them as possibly wrapped by Block equally.
2072 Block b = s as Block;
2073 if (b != null && b.statements.Count == 1)
2074 s = (Statement) b.statements [0];
2076 if (s is Lock)
2077 body = ((Lock) s).Statement;
2078 else if (s is For)
2079 body = ((For) s).Statement;
2080 else if (s is Foreach)
2081 body = ((Foreach) s).Statement;
2082 else if (s is While)
2083 body = ((While) s).Statement;
2084 else if (s is Fixed)
2085 body = ((Fixed) s).Statement;
2086 else if (s is Using)
2087 body = ((Using) s).EmbeddedStatement;
2088 else if (s is UsingTemporary)
2089 body = ((UsingTemporary) s).Statement;
2090 else
2091 return;
2093 if (body == null || body is EmptyStatement)
2094 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2097 public override bool Resolve (BlockContext ec)
2099 Block prev_block = ec.CurrentBlock;
2100 bool ok = true;
2102 int errors = ec.Report.Errors;
2104 ec.CurrentBlock = this;
2105 ec.StartFlowBranching (this);
2107 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2110 // Compiler generated scope statements
2112 if (scope_initializers != null) {
2113 foreach (Statement s in scope_initializers)
2114 s.Resolve (ec);
2118 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2119 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2120 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2121 // responsible for handling the situation.
2123 int statement_count = statements.Count;
2124 for (int ix = 0; ix < statement_count; ix++){
2125 Statement s = statements [ix];
2126 // Check possible empty statement (CS0642)
2127 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2128 statements [ix + 1] is ExplicitBlock)
2129 CheckPossibleMistakenEmptyStatement (ec, s);
2132 // Warn if we detect unreachable code.
2134 if (unreachable) {
2135 if (s is EmptyStatement)
2136 continue;
2138 if (!unreachable_shown && !(s is LabeledStatement)) {
2139 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2140 unreachable_shown = true;
2143 Block c_block = s as Block;
2144 if (c_block != null)
2145 c_block.unreachable = c_block.unreachable_shown = true;
2149 // Note that we're not using ResolveUnreachable() for unreachable
2150 // statements here. ResolveUnreachable() creates a temporary
2151 // flow branching and kills it afterwards. This leads to problems
2152 // if you have two unreachable statements where the first one
2153 // assigns a variable and the second one tries to access it.
2156 if (!s.Resolve (ec)) {
2157 ok = false;
2158 if (ec.IsInProbingMode)
2159 break;
2161 statements [ix] = EmptyStatement.Value;
2162 continue;
2165 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2166 statements [ix] = EmptyStatement.Value;
2168 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2169 if (unreachable && s is LabeledStatement)
2170 throw new InternalErrorException ("should not happen");
2173 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2174 ec.CurrentBranching, statement_count);
2176 while (ec.CurrentBranching is FlowBranchingLabeled)
2177 ec.EndFlowBranching ();
2179 bool flow_unreachable = ec.EndFlowBranching ();
2181 ec.CurrentBlock = prev_block;
2183 if (flow_unreachable)
2184 flags |= Flags.HasRet;
2186 // If we're a non-static `struct' constructor which doesn't have an
2187 // initializer, then we must initialize all of the struct's fields.
2188 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2189 ok = false;
2191 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2192 foreach (LabeledStatement label in labels.Values)
2193 if (!label.HasBeenReferenced)
2194 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2197 if (ok && errors == ec.Report.Errors)
2198 UsageWarning (ec);
2200 return ok;
2203 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2205 unreachable_shown = true;
2206 unreachable = true;
2208 if (warn)
2209 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2211 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2212 bool ok = Resolve (ec);
2213 ec.KillFlowBranching ();
2215 return ok;
2218 protected override void DoEmit (EmitContext ec)
2220 for (int ix = 0; ix < statements.Count; ix++){
2221 Statement s = (Statement) statements [ix];
2222 s.Emit (ec);
2226 public override void Emit (EmitContext ec)
2228 if (scope_initializers != null)
2229 EmitScopeInitializers (ec);
2231 ec.Mark (StartLocation);
2232 DoEmit (ec);
2234 if (SymbolWriter.HasSymbolWriter)
2235 EmitSymbolInfo (ec);
2238 protected void EmitScopeInitializers (EmitContext ec)
2240 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2242 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2243 foreach (Statement s in scope_initializers)
2244 s.Emit (ec);
2247 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2250 protected virtual void EmitSymbolInfo (EmitContext ec)
2252 if (variables != null) {
2253 foreach (LocalInfo vi in variables.Values) {
2254 vi.EmitSymbolInfo (ec);
2259 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2261 MutateVariables (storey);
2263 if (scope_initializers != null) {
2264 foreach (Statement s in scope_initializers)
2265 s.MutateHoistedGenericType (storey);
2268 foreach (Statement s in statements)
2269 s.MutateHoistedGenericType (storey);
2272 void MutateVariables (AnonymousMethodStorey storey)
2274 if (variables != null) {
2275 foreach (LocalInfo vi in variables.Values) {
2276 vi.VariableType = storey.MutateType (vi.VariableType);
2280 if (temporary_variables != null) {
2281 foreach (LocalInfo vi in temporary_variables)
2282 vi.VariableType = storey.MutateType (vi.VariableType);
2286 public override string ToString ()
2288 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2291 protected override void CloneTo (CloneContext clonectx, Statement t)
2293 Block target = (Block) t;
2295 clonectx.AddBlockMap (this, target);
2297 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2298 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2299 if (Parent != null)
2300 target.Parent = clonectx.RemapBlockCopy (Parent);
2302 if (variables != null){
2303 target.variables = new Dictionary<string, LocalInfo> ();
2305 foreach (var de in variables){
2306 LocalInfo newlocal = de.Value.Clone (clonectx);
2307 target.variables [de.Key] = newlocal;
2308 clonectx.AddVariableMap (de.Value, newlocal);
2312 target.statements = new List<Statement> (statements.Count);
2313 foreach (Statement s in statements)
2314 target.statements.Add (s.Clone (clonectx));
2316 if (target.children != null){
2317 target.children = new List<Block> (children.Count);
2318 foreach (Block b in children){
2319 target.children.Add (clonectx.LookupBlock (b));
2324 // TODO: labels, switch_block, constants (?), anonymous_children
2329 public class ExplicitBlock : Block
2331 Dictionary<string, IKnownVariable> known_variables;
2332 protected AnonymousMethodStorey am_storey;
2334 public ExplicitBlock (Block parent, Location start, Location end)
2335 : this (parent, (Flags) 0, start, end)
2339 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2340 : base (parent, flags, start, end)
2342 this.Explicit = this;
2345 // <summary>
2346 // Marks a variable with name @name as being used in this or a child block.
2347 // If a variable name has been used in a child block, it's illegal to
2348 // declare a variable with the same name in the current block.
2349 // </summary>
2350 internal void AddKnownVariable (string name, IKnownVariable info)
2352 if (known_variables == null)
2353 known_variables = new Dictionary<string, IKnownVariable> ();
2355 known_variables [name] = info;
2357 if (Parent != null)
2358 Parent.Explicit.AddKnownVariable (name, info);
2361 public AnonymousMethodStorey AnonymousMethodStorey {
2362 get { return am_storey; }
2366 // Creates anonymous method storey in current block
2368 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2371 // When referencing a variable in iterator storey from children anonymous method
2373 if (Toplevel.am_storey is IteratorStorey) {
2374 return Toplevel.am_storey;
2378 // An iterator has only 1 storey block
2380 if (ec.CurrentIterator != null)
2381 return ec.CurrentIterator.Storey;
2383 if (am_storey == null) {
2384 MemberBase mc = ec.MemberContext as MemberBase;
2385 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2388 // Creates anonymous method storey for this block
2390 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2393 return am_storey;
2396 public override void Emit (EmitContext ec)
2398 if (am_storey != null)
2399 am_storey.EmitStoreyInstantiation (ec);
2401 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2402 if (emit_debug_info)
2403 ec.BeginScope ();
2405 base.Emit (ec);
2407 if (emit_debug_info)
2408 ec.EndScope ();
2411 public override void EmitMeta (EmitContext ec)
2414 // Creates anonymous method storey
2416 if (am_storey != null) {
2417 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2419 // Creates parent storey reference when hoisted this is accessible
2421 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2422 ExplicitBlock parent = Toplevel.Parent.Explicit;
2425 // Hoisted this exists in top-level parent storey only
2427 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2428 parent = parent.Parent.Explicit;
2430 am_storey.AddParentStoreyReference (parent.am_storey);
2433 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2436 am_storey.DefineType ();
2437 am_storey.ResolveType ();
2438 am_storey.Define ();
2439 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2441 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2442 if (ref_blocks != null) {
2443 foreach (ExplicitBlock ref_block in ref_blocks) {
2444 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2445 if (b.am_storey != null) {
2446 b.am_storey.AddParentStoreyReference (am_storey);
2448 // Stop propagation inside same top block
2449 if (b.Toplevel == Toplevel)
2450 break;
2452 b = b.Toplevel;
2454 b.HasCapturedVariable = true;
2460 base.EmitMeta (ec);
2463 internal IKnownVariable GetKnownVariable (string name)
2465 if (known_variables == null)
2466 return null;
2468 IKnownVariable kw;
2469 if (!known_variables.TryGetValue (name, out kw))
2470 return null;
2472 return kw;
2475 public bool HasCapturedThis
2477 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2478 get { return (flags & Flags.HasCapturedThis) != 0; }
2481 public bool HasCapturedVariable
2483 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2484 get { return (flags & Flags.HasCapturedVariable) != 0; }
2487 protected override void CloneTo (CloneContext clonectx, Statement t)
2489 ExplicitBlock target = (ExplicitBlock) t;
2490 target.known_variables = null;
2491 base.CloneTo (clonectx, t);
2495 public class ToplevelParameterInfo : IKnownVariable {
2496 public readonly ToplevelBlock Block;
2497 public readonly int Index;
2498 public VariableInfo VariableInfo;
2500 Block IKnownVariable.Block {
2501 get { return Block; }
2503 public Parameter Parameter {
2504 get { return Block.Parameters [Index]; }
2507 public Type ParameterType {
2508 get { return Block.Parameters.Types [Index]; }
2511 public Location Location {
2512 get { return Parameter.Location; }
2515 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2517 this.Block = block;
2518 this.Index = idx;
2523 // A toplevel block contains extra information, the split is done
2524 // only to separate information that would otherwise bloat the more
2525 // lightweight Block.
2527 // In particular, this was introduced when the support for Anonymous
2528 // Methods was implemented.
2530 public class ToplevelBlock : ExplicitBlock
2533 // Block is converted to an expression
2535 sealed class BlockScopeExpression : Expression
2537 Expression child;
2538 readonly ToplevelBlock block;
2540 public BlockScopeExpression (Expression child, ToplevelBlock block)
2542 this.child = child;
2543 this.block = block;
2546 public override Expression CreateExpressionTree (ResolveContext ec)
2548 throw new NotSupportedException ();
2551 protected override Expression DoResolve (ResolveContext ec)
2553 if (child == null)
2554 return null;
2556 child = child.Resolve (ec);
2557 if (child == null)
2558 return null;
2560 eclass = child.eclass;
2561 type = child.Type;
2562 return this;
2565 public override void Emit (EmitContext ec)
2567 block.EmitMeta (ec);
2568 block.EmitScopeInitializers (ec);
2569 child.Emit (ec);
2572 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2574 type = storey.MutateType (type);
2575 child.MutateHoistedGenericType (storey);
2576 block.MutateHoistedGenericType (storey);
2580 GenericMethod generic;
2581 protected ParametersCompiled parameters;
2582 ToplevelParameterInfo[] parameter_info;
2583 LocalInfo this_variable;
2584 bool resolved;
2585 bool unreachable;
2586 CompilerContext compiler;
2588 public HoistedVariable HoistedThisVariable;
2590 public bool Resolved {
2591 get {
2592 return resolved;
2597 // The parameters for the block.
2599 public ParametersCompiled Parameters {
2600 get { return parameters; }
2603 public Report Report {
2604 get { return compiler.Report; }
2607 public GenericMethod GenericMethod {
2608 get { return generic; }
2611 public ToplevelBlock Container {
2612 get { return Parent == null ? null : Parent.Toplevel; }
2615 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2616 this (ctx, parent, (Flags) 0, parameters, start)
2620 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2621 this (ctx, parent, parameters, start)
2623 this.generic = generic;
2626 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2627 this (ctx, null, (Flags) 0, parameters, start)
2631 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2632 this (ctx, null, flags, parameters, start)
2636 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2637 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2638 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2639 base (null, flags, start, Location.Null)
2641 this.compiler = ctx;
2642 this.Toplevel = this;
2644 this.parameters = parameters;
2645 this.Parent = parent;
2646 if (parent != null)
2647 parent.AddAnonymousChild (this);
2649 if (!this.parameters.IsEmpty)
2650 ProcessParameters ();
2653 public ToplevelBlock (CompilerContext ctx, Location loc)
2654 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2658 protected override void CloneTo (CloneContext clonectx, Statement t)
2660 ToplevelBlock target = (ToplevelBlock) t;
2661 base.CloneTo (clonectx, t);
2663 if (parameters.Count != 0)
2664 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2665 for (int i = 0; i < parameters.Count; ++i)
2666 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2669 public bool CheckError158 (string name, Location loc)
2671 if (AnonymousChildren != null) {
2672 foreach (ToplevelBlock child in AnonymousChildren) {
2673 if (!child.CheckError158 (name, loc))
2674 return false;
2678 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2679 if (!c.DoCheckError158 (name, loc))
2680 return false;
2683 return true;
2686 void ProcessParameters ()
2688 int n = parameters.Count;
2689 parameter_info = new ToplevelParameterInfo [n];
2690 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2691 for (int i = 0; i < n; ++i) {
2692 parameter_info [i] = new ToplevelParameterInfo (this, i);
2694 Parameter p = parameters [i];
2695 if (p == null)
2696 continue;
2698 string name = p.Name;
2699 if (CheckParentConflictName (top_parent, name, loc))
2700 AddKnownVariable (name, parameter_info [i]);
2703 // mark this block as "used" so that we create local declarations in a sub-block
2704 // FIXME: This appears to uncover a lot of bugs
2705 //this.Use ();
2708 bool DoCheckError158 (string name, Location loc)
2710 LabeledStatement s = LookupLabel (name);
2711 if (s != null) {
2712 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2713 Error_158 (name, loc);
2714 return false;
2717 return true;
2720 public override Expression CreateExpressionTree (ResolveContext ec)
2722 if (statements.Count == 1) {
2723 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2724 if (scope_initializers != null)
2725 expr = new BlockScopeExpression (expr, this);
2727 return expr;
2730 return base.CreateExpressionTree (ec);
2734 // Reformats this block to be top-level iterator block
2736 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2738 IsIterator = true;
2740 // Creates block with original statements
2741 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2743 source.statements = new List<Statement> (1);
2744 source.AddStatement (new Return (iterator, iterator.Location));
2745 source.IsIterator = false;
2747 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2748 source.am_storey = iterator_storey;
2749 return iterator_storey;
2753 // Returns a parameter reference expression for the given name,
2754 // or null if there is no such parameter
2756 public Expression GetParameterReference (string name, Location loc)
2758 for (ToplevelBlock t = this; t != null; t = t.Container) {
2759 Expression expr = t.GetParameterReferenceExpression (name, loc);
2760 if (expr != null)
2761 return expr;
2764 return null;
2767 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2769 int idx = parameters.GetParameterIndexByName (name);
2770 return idx < 0 ?
2771 null : new ParameterReference (parameter_info [idx], loc);
2774 // <summary>
2775 // Returns the "this" instance variable of this block.
2776 // See AddThisVariable() for more information.
2777 // </summary>
2778 public LocalInfo ThisVariable {
2779 get { return this_variable; }
2782 // <summary>
2783 // This is used by non-static `struct' constructors which do not have an
2784 // initializer - in this case, the constructor must initialize all of the
2785 // struct's fields. To do this, we add a "this" variable and use the flow
2786 // analysis code to ensure that it's been fully initialized before control
2787 // leaves the constructor.
2788 // </summary>
2789 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2791 if (this_variable == null) {
2792 this_variable = new LocalInfo (ds, this, l);
2793 this_variable.Used = true;
2794 this_variable.IsThis = true;
2796 Variables.Add ("this", this_variable);
2799 return this_variable;
2802 public bool IsIterator {
2803 get { return (flags & Flags.IsIterator) != 0; }
2804 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2808 // Block has been converted to expression tree
2810 public bool IsExpressionTree {
2811 get { return (flags & Flags.IsExpressionTree) != 0; }
2814 public bool IsThisAssigned (BlockContext ec)
2816 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2819 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2821 if (resolved)
2822 return true;
2824 resolved = true;
2826 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2827 flags |= Flags.IsExpressionTree;
2829 try {
2830 if (!ResolveMeta (rc, ip))
2831 return false;
2833 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2834 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2836 if (!Resolve (rc))
2837 return false;
2839 unreachable = top_level.End ();
2841 } catch (Exception) {
2842 #if PRODUCTION
2843 if (rc.CurrentBlock != null) {
2844 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2845 } else {
2846 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2848 #endif
2849 throw;
2852 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2853 if (rc.CurrentAnonymousMethod == null) {
2854 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2855 return false;
2856 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2857 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2858 rc.CurrentAnonymousMethod.GetSignatureForError ());
2859 return false;
2863 return true;
2866 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2868 int errors = ec.Report.Errors;
2869 int orig_count = parameters.Count;
2871 if (ip != null)
2872 parameters = ip;
2874 // Assert: orig_count != parameter.Count => orig_count == 0
2875 if (orig_count != 0 && orig_count != parameters.Count)
2876 throw new InternalErrorException ("parameter information mismatch");
2878 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2880 for (int i = 0; i < orig_count; ++i) {
2881 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2883 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2884 continue;
2886 VariableInfo vi = new VariableInfo (ip, i, offset);
2887 parameter_info [i].VariableInfo = vi;
2888 offset += vi.Length;
2891 ResolveMeta (ec, offset);
2893 return ec.Report.Errors == errors;
2896 // <summary>
2897 // Check whether all `out' parameters have been assigned.
2898 // </summary>
2899 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2901 if (vector.IsUnreachable)
2902 return;
2904 int n = parameter_info == null ? 0 : parameter_info.Length;
2906 for (int i = 0; i < n; i++) {
2907 VariableInfo var = parameter_info [i].VariableInfo;
2909 if (var == null)
2910 continue;
2912 if (vector.IsAssigned (var, false))
2913 continue;
2915 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2916 var.Name);
2920 public override void Emit (EmitContext ec)
2922 if (Report.Errors > 0)
2923 return;
2925 #if PRODUCTION
2926 try {
2927 #endif
2928 EmitMeta (ec);
2930 if (ec.HasReturnLabel)
2931 ec.ReturnLabel = ec.ig.DefineLabel ();
2933 base.Emit (ec);
2935 ec.Mark (EndLocation);
2937 if (ec.HasReturnLabel)
2938 ec.ig.MarkLabel (ec.ReturnLabel);
2940 if (ec.return_value != null) {
2941 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2942 ec.ig.Emit (OpCodes.Ret);
2943 } else {
2945 // If `HasReturnLabel' is set, then we already emitted a
2946 // jump to the end of the method, so we must emit a `ret'
2947 // there.
2949 // Unfortunately, System.Reflection.Emit automatically emits
2950 // a leave to the end of a finally block. This is a problem
2951 // if no code is following the try/finally block since we may
2952 // jump to a point after the end of the method.
2953 // As a workaround, we're always creating a return label in
2954 // this case.
2957 if (ec.HasReturnLabel || !unreachable) {
2958 if (ec.ReturnType != TypeManager.void_type)
2959 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2960 ec.ig.Emit (OpCodes.Ret);
2964 #if PRODUCTION
2965 } catch (Exception e){
2966 Console.WriteLine ("Exception caught by the compiler while emitting:");
2967 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2969 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2970 throw;
2972 #endif
2975 public override void EmitMeta (EmitContext ec)
2977 parameters.ResolveVariable ();
2979 // Avoid declaring an IL variable for this_variable since it is not accessed
2980 // from the generated IL
2981 if (this_variable != null)
2982 Variables.Remove ("this");
2983 base.EmitMeta (ec);
2986 protected override void EmitSymbolInfo (EmitContext ec)
2988 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2989 if ((ae != null) && (ae.Storey != null))
2990 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2992 base.EmitSymbolInfo (ec);
2996 public class SwitchLabel {
2997 Expression label;
2998 object converted;
2999 Location loc;
3001 Label il_label;
3002 bool il_label_set;
3003 Label il_label_code;
3004 bool il_label_code_set;
3006 public static readonly object NullStringCase = new object ();
3009 // if expr == null, then it is the default case.
3011 public SwitchLabel (Expression expr, Location l)
3013 label = expr;
3014 loc = l;
3017 public Expression Label {
3018 get {
3019 return label;
3023 public Location Location {
3024 get { return loc; }
3027 public object Converted {
3028 get {
3029 return converted;
3033 public Label GetILLabel (EmitContext ec)
3035 if (!il_label_set){
3036 il_label = ec.ig.DefineLabel ();
3037 il_label_set = true;
3039 return il_label;
3042 public Label GetILLabelCode (EmitContext ec)
3044 if (!il_label_code_set){
3045 il_label_code = ec.ig.DefineLabel ();
3046 il_label_code_set = true;
3048 return il_label_code;
3052 // Resolves the expression, reduces it to a literal if possible
3053 // and then converts it to the requested type.
3055 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3057 Expression e = label.Resolve (ec);
3059 if (e == null)
3060 return false;
3062 Constant c = e as Constant;
3063 if (c == null){
3064 ec.Report.Error (150, loc, "A constant value is expected");
3065 return false;
3068 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3069 converted = NullStringCase;
3070 return true;
3073 if (allow_nullable && c.GetValue () == null) {
3074 converted = NullStringCase;
3075 return true;
3078 c = c.ImplicitConversionRequired (ec, required_type, loc);
3079 if (c == null)
3080 return false;
3082 converted = c.GetValue ();
3083 return true;
3086 public void Error_AlreadyOccurs (ResolveContext ec, Type switch_type, SwitchLabel collision_with)
3088 string label;
3089 if (converted == null)
3090 label = "default";
3091 else if (converted == NullStringCase)
3092 label = "null";
3093 else
3094 label = converted.ToString ();
3096 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3097 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3100 public SwitchLabel Clone (CloneContext clonectx)
3102 return new SwitchLabel (label.Clone (clonectx), loc);
3106 public class SwitchSection {
3107 // An array of SwitchLabels.
3108 public readonly List<SwitchLabel> Labels;
3109 public readonly Block Block;
3111 public SwitchSection (List<SwitchLabel> labels, Block block)
3113 Labels = labels;
3114 Block = block;
3117 public SwitchSection Clone (CloneContext clonectx)
3119 var cloned_labels = new List<SwitchLabel> ();
3121 foreach (SwitchLabel sl in cloned_labels)
3122 cloned_labels.Add (sl.Clone (clonectx));
3124 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3128 public class Switch : Statement {
3129 public List<SwitchSection> Sections;
3130 public Expression Expr;
3132 /// <summary>
3133 /// Maps constants whose type type SwitchType to their SwitchLabels.
3134 /// </summary>
3135 public IDictionary<object, SwitchLabel> Elements;
3137 /// <summary>
3138 /// The governing switch type
3139 /// </summary>
3140 public Type SwitchType;
3143 // Computed
3145 Label default_target;
3146 Label null_target;
3147 Expression new_expr;
3148 bool is_constant;
3149 bool has_null_case;
3150 SwitchSection constant_section;
3151 SwitchSection default_section;
3153 ExpressionStatement string_dictionary;
3154 FieldExpr switch_cache_field;
3155 static int unique_counter;
3158 // Nullable Types support
3160 Nullable.Unwrap unwrap;
3162 protected bool HaveUnwrap {
3163 get { return unwrap != null; }
3167 // The types allowed to be implicitly cast from
3168 // on the governing type
3170 static Type [] allowed_types;
3172 public Switch (Expression e, List<SwitchSection> sects, Location l)
3174 Expr = e;
3175 Sections = sects;
3176 loc = l;
3179 public bool GotDefault {
3180 get {
3181 return default_section != null;
3185 public Label DefaultTarget {
3186 get {
3187 return default_target;
3192 // Determines the governing type for a switch. The returned
3193 // expression might be the expression from the switch, or an
3194 // expression that includes any potential conversions to the
3195 // integral types or to string.
3197 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3199 Type t = expr.Type;
3201 if (t == TypeManager.byte_type ||
3202 t == TypeManager.sbyte_type ||
3203 t == TypeManager.ushort_type ||
3204 t == TypeManager.short_type ||
3205 t == TypeManager.uint32_type ||
3206 t == TypeManager.int32_type ||
3207 t == TypeManager.uint64_type ||
3208 t == TypeManager.int64_type ||
3209 t == TypeManager.char_type ||
3210 t == TypeManager.string_type ||
3211 t == TypeManager.bool_type ||
3212 TypeManager.IsEnumType (t))
3213 return expr;
3215 if (allowed_types == null){
3216 allowed_types = new Type [] {
3217 TypeManager.sbyte_type,
3218 TypeManager.byte_type,
3219 TypeManager.short_type,
3220 TypeManager.ushort_type,
3221 TypeManager.int32_type,
3222 TypeManager.uint32_type,
3223 TypeManager.int64_type,
3224 TypeManager.uint64_type,
3225 TypeManager.char_type,
3226 TypeManager.string_type
3231 // Try to find a *user* defined implicit conversion.
3233 // If there is no implicit conversion, or if there are multiple
3234 // conversions, we have to report an error
3236 Expression converted = null;
3237 foreach (Type tt in allowed_types){
3238 Expression e;
3240 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3241 if (e == null)
3242 continue;
3245 // Ignore over-worked ImplicitUserConversions that do
3246 // an implicit conversion in addition to the user conversion.
3248 if (!(e is UserCast))
3249 continue;
3251 if (converted != null){
3252 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3253 return null;
3256 converted = e;
3258 return converted;
3262 // Performs the basic sanity checks on the switch statement
3263 // (looks for duplicate keys and non-constant expressions).
3265 // It also returns a hashtable with the keys that we will later
3266 // use to compute the switch tables
3268 bool CheckSwitch (ResolveContext ec)
3270 bool error = false;
3271 Elements = new Dictionary<object, SwitchLabel> ();
3273 foreach (SwitchSection ss in Sections){
3274 foreach (SwitchLabel sl in ss.Labels){
3275 if (sl.Label == null){
3276 if (default_section != null){
3277 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3278 error = true;
3280 default_section = ss;
3281 continue;
3284 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3285 error = true;
3286 continue;
3289 object key = sl.Converted;
3290 if (key == SwitchLabel.NullStringCase)
3291 has_null_case = true;
3293 try {
3294 Elements.Add (key, sl);
3295 } catch (ArgumentException) {
3296 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3297 error = true;
3301 return !error;
3304 void EmitObjectInteger (ILGenerator ig, object k)
3306 if (k is int)
3307 IntConstant.EmitInt (ig, (int) k);
3308 else if (k is Constant) {
3309 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3311 else if (k is uint)
3312 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3313 else if (k is long)
3315 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3317 IntConstant.EmitInt (ig, (int) (long) k);
3318 ig.Emit (OpCodes.Conv_I8);
3320 else
3321 LongConstant.EmitLong (ig, (long) k);
3323 else if (k is ulong)
3325 ulong ul = (ulong) k;
3326 if (ul < (1L<<32))
3328 IntConstant.EmitInt (ig, unchecked ((int) ul));
3329 ig.Emit (OpCodes.Conv_U8);
3331 else
3333 LongConstant.EmitLong (ig, unchecked ((long) ul));
3336 else if (k is char)
3337 IntConstant.EmitInt (ig, (int) ((char) k));
3338 else if (k is sbyte)
3339 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3340 else if (k is byte)
3341 IntConstant.EmitInt (ig, (int) ((byte) k));
3342 else if (k is short)
3343 IntConstant.EmitInt (ig, (int) ((short) k));
3344 else if (k is ushort)
3345 IntConstant.EmitInt (ig, (int) ((ushort) k));
3346 else if (k is bool)
3347 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3348 else
3349 throw new Exception ("Unhandled case");
3352 // structure used to hold blocks of keys while calculating table switch
3353 class KeyBlock : IComparable
3355 public KeyBlock (long _first)
3357 first = last = _first;
3359 public long first;
3360 public long last;
3361 public List<object> element_keys;
3362 // how many items are in the bucket
3363 public int Size = 1;
3364 public int Length
3366 get { return (int) (last - first + 1); }
3368 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3370 return kb_last.last - kb_first.first + 1;
3372 public int CompareTo (object obj)
3374 KeyBlock kb = (KeyBlock) obj;
3375 int nLength = Length;
3376 int nLengthOther = kb.Length;
3377 if (nLengthOther == nLength)
3378 return (int) (kb.first - first);
3379 return nLength - nLengthOther;
3383 /// <summary>
3384 /// This method emits code for a lookup-based switch statement (non-string)
3385 /// Basically it groups the cases into blocks that are at least half full,
3386 /// and then spits out individual lookup opcodes for each block.
3387 /// It emits the longest blocks first, and short blocks are just
3388 /// handled with direct compares.
3389 /// </summary>
3390 /// <param name="ec"></param>
3391 /// <param name="val"></param>
3392 /// <returns></returns>
3393 void TableSwitchEmit (EmitContext ec, Expression val)
3395 int element_count = Elements.Count;
3396 object [] element_keys = new object [element_count];
3397 Elements.Keys.CopyTo (element_keys, 0);
3398 Array.Sort (element_keys);
3400 // initialize the block list with one element per key
3401 var key_blocks = new List<KeyBlock> (element_count);
3402 foreach (object key in element_keys)
3403 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3405 KeyBlock current_kb;
3406 // iteratively merge the blocks while they are at least half full
3407 // there's probably a really cool way to do this with a tree...
3408 while (key_blocks.Count > 1)
3410 var key_blocks_new = new List<KeyBlock> ();
3411 current_kb = (KeyBlock) key_blocks [0];
3412 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3414 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3415 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3417 // merge blocks
3418 current_kb.last = kb.last;
3419 current_kb.Size += kb.Size;
3421 else
3423 // start a new block
3424 key_blocks_new.Add (current_kb);
3425 current_kb = kb;
3428 key_blocks_new.Add (current_kb);
3429 if (key_blocks.Count == key_blocks_new.Count)
3430 break;
3431 key_blocks = key_blocks_new;
3434 // initialize the key lists
3435 foreach (KeyBlock kb in key_blocks)
3436 kb.element_keys = new List<object> ();
3438 // fill the key lists
3439 int iBlockCurr = 0;
3440 if (key_blocks.Count > 0) {
3441 current_kb = (KeyBlock) key_blocks [0];
3442 foreach (object key in element_keys)
3444 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3445 System.Convert.ToInt64 (key) > current_kb.last;
3446 if (next_block)
3447 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3448 current_kb.element_keys.Add (key);
3452 // sort the blocks so we can tackle the largest ones first
3453 key_blocks.Sort ();
3455 // okay now we can start...
3456 ILGenerator ig = ec.ig;
3457 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3458 Label lbl_default = default_target;
3460 Type type_keys = null;
3461 if (element_keys.Length > 0)
3462 type_keys = element_keys [0].GetType (); // used for conversions
3464 Type compare_type;
3466 if (TypeManager.IsEnumType (SwitchType))
3467 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3468 else
3469 compare_type = SwitchType;
3471 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3473 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3474 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3475 if (kb.Length <= 2)
3477 foreach (object key in kb.element_keys) {
3478 SwitchLabel sl = (SwitchLabel) Elements [key];
3479 if (key is int && (int) key == 0) {
3480 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3481 } else {
3482 val.Emit (ec);
3483 EmitObjectInteger (ig, key);
3484 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3488 else
3490 // TODO: if all the keys in the block are the same and there are
3491 // no gaps/defaults then just use a range-check.
3492 if (compare_type == TypeManager.int64_type ||
3493 compare_type == TypeManager.uint64_type)
3495 // TODO: optimize constant/I4 cases
3497 // check block range (could be > 2^31)
3498 val.Emit (ec);
3499 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3500 ig.Emit (OpCodes.Blt, lbl_default);
3501 val.Emit (ec);
3502 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3503 ig.Emit (OpCodes.Bgt, lbl_default);
3505 // normalize range
3506 val.Emit (ec);
3507 if (kb.first != 0)
3509 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3510 ig.Emit (OpCodes.Sub);
3512 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3514 else
3516 // normalize range
3517 val.Emit (ec);
3518 int first = (int) kb.first;
3519 if (first > 0)
3521 IntConstant.EmitInt (ig, first);
3522 ig.Emit (OpCodes.Sub);
3524 else if (first < 0)
3526 IntConstant.EmitInt (ig, -first);
3527 ig.Emit (OpCodes.Add);
3531 // first, build the list of labels for the switch
3532 int iKey = 0;
3533 int cJumps = kb.Length;
3534 Label [] switch_labels = new Label [cJumps];
3535 for (int iJump = 0; iJump < cJumps; iJump++)
3537 object key = kb.element_keys [iKey];
3538 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3540 SwitchLabel sl = (SwitchLabel) Elements [key];
3541 switch_labels [iJump] = sl.GetILLabel (ec);
3542 iKey++;
3544 else
3545 switch_labels [iJump] = lbl_default;
3547 // emit the switch opcode
3548 ig.Emit (OpCodes.Switch, switch_labels);
3551 // mark the default for this block
3552 if (iBlock != 0)
3553 ig.MarkLabel (lbl_default);
3556 // TODO: find the default case and emit it here,
3557 // to prevent having to do the following jump.
3558 // make sure to mark other labels in the default section
3560 // the last default just goes to the end
3561 if (element_keys.Length > 0)
3562 ig.Emit (OpCodes.Br, lbl_default);
3564 // now emit the code for the sections
3565 bool found_default = false;
3567 foreach (SwitchSection ss in Sections) {
3568 foreach (SwitchLabel sl in ss.Labels) {
3569 if (sl.Converted == SwitchLabel.NullStringCase) {
3570 ig.MarkLabel (null_target);
3571 } else if (sl.Label == null) {
3572 ig.MarkLabel (lbl_default);
3573 found_default = true;
3574 if (!has_null_case)
3575 ig.MarkLabel (null_target);
3577 ig.MarkLabel (sl.GetILLabel (ec));
3578 ig.MarkLabel (sl.GetILLabelCode (ec));
3580 ss.Block.Emit (ec);
3583 if (!found_default) {
3584 ig.MarkLabel (lbl_default);
3585 if (!has_null_case) {
3586 ig.MarkLabel (null_target);
3590 ig.MarkLabel (lbl_end);
3593 SwitchSection FindSection (SwitchLabel label)
3595 foreach (SwitchSection ss in Sections){
3596 foreach (SwitchLabel sl in ss.Labels){
3597 if (label == sl)
3598 return ss;
3602 return null;
3605 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3607 foreach (SwitchSection ss in Sections)
3608 ss.Block.MutateHoistedGenericType (storey);
3611 public static void Reset ()
3613 unique_counter = 0;
3614 allowed_types = null;
3617 public override bool Resolve (BlockContext ec)
3619 Expr = Expr.Resolve (ec);
3620 if (Expr == null)
3621 return false;
3623 new_expr = SwitchGoverningType (ec, Expr);
3625 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3626 unwrap = Nullable.Unwrap.Create (Expr, false);
3627 if (unwrap == null)
3628 return false;
3630 new_expr = SwitchGoverningType (ec, unwrap);
3633 if (new_expr == null){
3634 ec.Report.Error (151, loc,
3635 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3636 TypeManager.CSharpName (Expr.Type));
3637 return false;
3640 // Validate switch.
3641 SwitchType = new_expr.Type;
3643 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3644 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3645 return false;
3648 if (!CheckSwitch (ec))
3649 return false;
3651 if (HaveUnwrap)
3652 Elements.Remove (SwitchLabel.NullStringCase);
3654 Switch old_switch = ec.Switch;
3655 ec.Switch = this;
3656 ec.Switch.SwitchType = SwitchType;
3658 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3659 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3661 var constant = new_expr as Constant;
3662 if (constant != null) {
3663 is_constant = true;
3664 object key = constant.GetValue ();
3665 SwitchLabel label;
3666 if (Elements.TryGetValue (key, out label))
3667 constant_section = FindSection (label);
3669 if (constant_section == null)
3670 constant_section = default_section;
3673 bool first = true;
3674 bool ok = true;
3675 foreach (SwitchSection ss in Sections){
3676 if (!first)
3677 ec.CurrentBranching.CreateSibling (
3678 null, FlowBranching.SiblingType.SwitchSection);
3679 else
3680 first = false;
3682 if (is_constant && (ss != constant_section)) {
3683 // If we're a constant switch, we're only emitting
3684 // one single section - mark all the others as
3685 // unreachable.
3686 ec.CurrentBranching.CurrentUsageVector.Goto ();
3687 if (!ss.Block.ResolveUnreachable (ec, true)) {
3688 ok = false;
3690 } else {
3691 if (!ss.Block.Resolve (ec))
3692 ok = false;
3696 if (default_section == null)
3697 ec.CurrentBranching.CreateSibling (
3698 null, FlowBranching.SiblingType.SwitchSection);
3700 ec.EndFlowBranching ();
3701 ec.Switch = old_switch;
3703 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3705 if (!ok)
3706 return false;
3708 if (SwitchType == TypeManager.string_type && !is_constant) {
3709 // TODO: Optimize single case, and single+default case
3710 ResolveStringSwitchMap (ec);
3713 return true;
3716 void ResolveStringSwitchMap (ResolveContext ec)
3718 FullNamedExpression string_dictionary_type;
3719 if (TypeManager.generic_ienumerable_type != null) {
3720 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3721 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3723 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3724 new TypeArguments (
3725 new TypeExpression (TypeManager.string_type, loc),
3726 new TypeExpression (TypeManager.int32_type, loc)), loc);
3727 } else {
3728 MemberAccess system_collections_generic = new MemberAccess (
3729 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3731 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3734 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3735 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3736 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3737 if (!field.Define ())
3738 return;
3739 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3741 var init = new List<Expression> ();
3742 int counter = 0;
3743 Elements.Clear ();
3744 string value = null;
3745 foreach (SwitchSection section in Sections) {
3746 int last_count = init.Count;
3747 foreach (SwitchLabel sl in section.Labels) {
3748 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3749 continue;
3751 value = (string) sl.Converted;
3752 var init_args = new List<Expression> (2);
3753 init_args.Add (new StringLiteral (value, sl.Location));
3754 init_args.Add (new IntConstant (counter, loc));
3755 init.Add (new CollectionElementInitializer (init_args, loc));
3759 // Don't add empty sections
3761 if (last_count == init.Count)
3762 continue;
3764 Elements.Add (counter, section.Labels [0]);
3765 ++counter;
3768 Arguments args = new Arguments (1);
3769 args.Add (new Argument (new IntConstant (init.Count, loc)));
3770 Expression initializer = new NewInitialize (string_dictionary_type, args,
3771 new CollectionOrObjectInitializers (init, loc), loc);
3773 switch_cache_field = new FieldExpr (field, loc);
3774 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3777 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3779 ILGenerator ig = ec.ig;
3780 Label l_initialized = ig.DefineLabel ();
3783 // Skip initialization when value is null
3785 value.EmitBranchable (ec, null_target, false);
3788 // Check if string dictionary is initialized and initialize
3790 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3791 string_dictionary.EmitStatement (ec);
3792 ig.MarkLabel (l_initialized);
3794 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3796 ResolveContext rc = new ResolveContext (ec.MemberContext);
3798 if (TypeManager.generic_ienumerable_type != null) {
3799 Arguments get_value_args = new Arguments (2);
3800 get_value_args.Add (new Argument (value));
3801 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3802 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3803 if (get_item == null)
3804 return;
3807 // A value was not found, go to default case
3809 get_item.EmitBranchable (ec, default_target, false);
3810 } else {
3811 Arguments get_value_args = new Arguments (1);
3812 get_value_args.Add (new Argument (value));
3814 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3815 if (get_item == null)
3816 return;
3818 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3819 get_item_object.EmitAssign (ec, get_item, true, false);
3820 ec.ig.Emit (OpCodes.Brfalse, default_target);
3822 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3823 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3825 get_item_int.EmitStatement (ec);
3826 get_item_object.Release (ec);
3829 TableSwitchEmit (ec, string_switch_variable);
3830 string_switch_variable.Release (ec);
3833 protected override void DoEmit (EmitContext ec)
3835 ILGenerator ig = ec.ig;
3837 default_target = ig.DefineLabel ();
3838 null_target = ig.DefineLabel ();
3840 // Store variable for comparission purposes
3841 // TODO: Don't duplicate non-captured VariableReference
3842 LocalTemporary value;
3843 if (HaveUnwrap) {
3844 value = new LocalTemporary (SwitchType);
3845 unwrap.EmitCheck (ec);
3846 ig.Emit (OpCodes.Brfalse, null_target);
3847 new_expr.Emit (ec);
3848 value.Store (ec);
3849 } else if (!is_constant) {
3850 value = new LocalTemporary (SwitchType);
3851 new_expr.Emit (ec);
3852 value.Store (ec);
3853 } else
3854 value = null;
3857 // Setup the codegen context
3859 Label old_end = ec.LoopEnd;
3860 Switch old_switch = ec.Switch;
3862 ec.LoopEnd = ig.DefineLabel ();
3863 ec.Switch = this;
3865 // Emit Code.
3866 if (is_constant) {
3867 if (constant_section != null)
3868 constant_section.Block.Emit (ec);
3869 } else if (string_dictionary != null) {
3870 DoEmitStringSwitch (value, ec);
3871 } else {
3872 TableSwitchEmit (ec, value);
3875 if (value != null)
3876 value.Release (ec);
3878 // Restore context state.
3879 ig.MarkLabel (ec.LoopEnd);
3882 // Restore the previous context
3884 ec.LoopEnd = old_end;
3885 ec.Switch = old_switch;
3888 protected override void CloneTo (CloneContext clonectx, Statement t)
3890 Switch target = (Switch) t;
3892 target.Expr = Expr.Clone (clonectx);
3893 target.Sections = new List<SwitchSection> ();
3894 foreach (SwitchSection ss in Sections){
3895 target.Sections.Add (ss.Clone (clonectx));
3900 // A place where execution can restart in an iterator
3901 public abstract class ResumableStatement : Statement
3903 bool prepared;
3904 protected Label resume_point;
3906 public Label PrepareForEmit (EmitContext ec)
3908 if (!prepared) {
3909 prepared = true;
3910 resume_point = ec.ig.DefineLabel ();
3912 return resume_point;
3915 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3917 return end;
3919 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3924 // Base class for statements that are implemented in terms of try...finally
3925 public abstract class ExceptionStatement : ResumableStatement
3927 bool code_follows;
3928 Iterator iter;
3929 List<ResumableStatement> resume_points;
3930 int first_resume_pc;
3932 protected abstract void EmitPreTryBody (EmitContext ec);
3933 protected abstract void EmitTryBody (EmitContext ec);
3934 protected abstract void EmitFinallyBody (EmitContext ec);
3936 protected sealed override void DoEmit (EmitContext ec)
3938 ILGenerator ig = ec.ig;
3940 EmitPreTryBody (ec);
3942 if (resume_points != null) {
3943 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3944 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3947 ig.BeginExceptionBlock ();
3949 if (resume_points != null) {
3950 ig.MarkLabel (resume_point);
3952 // For normal control flow, we want to fall-through the Switch
3953 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3954 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3955 IntConstant.EmitInt (ig, first_resume_pc);
3956 ig.Emit (OpCodes.Sub);
3958 Label [] labels = new Label [resume_points.Count];
3959 for (int i = 0; i < resume_points.Count; ++i)
3960 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3961 ig.Emit (OpCodes.Switch, labels);
3964 EmitTryBody (ec);
3966 ig.BeginFinallyBlock ();
3968 Label start_finally = ec.ig.DefineLabel ();
3969 if (resume_points != null) {
3970 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3971 ig.Emit (OpCodes.Brfalse_S, start_finally);
3972 ig.Emit (OpCodes.Endfinally);
3975 ig.MarkLabel (start_finally);
3976 EmitFinallyBody (ec);
3978 ig.EndExceptionBlock ();
3981 public void SomeCodeFollows ()
3983 code_follows = true;
3986 public override bool Resolve (BlockContext ec)
3988 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3989 // So, ensure there's some IL code after this statement.
3990 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3991 ec.NeedReturnLabel ();
3993 iter = ec.CurrentIterator;
3994 return true;
3997 public void AddResumePoint (ResumableStatement stmt, int pc)
3999 if (resume_points == null) {
4000 resume_points = new List<ResumableStatement> ();
4001 first_resume_pc = pc;
4004 if (pc != first_resume_pc + resume_points.Count)
4005 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4007 resume_points.Add (stmt);
4010 Label dispose_try_block;
4011 bool prepared_for_dispose, emitted_dispose;
4012 public override Label PrepareForDispose (EmitContext ec, Label end)
4014 if (!prepared_for_dispose) {
4015 prepared_for_dispose = true;
4016 dispose_try_block = ec.ig.DefineLabel ();
4018 return dispose_try_block;
4021 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4023 if (emitted_dispose)
4024 return;
4026 emitted_dispose = true;
4028 ILGenerator ig = ec.ig;
4030 Label end_of_try = ig.DefineLabel ();
4032 // Ensure that the only way we can get into this code is through a dispatcher
4033 if (have_dispatcher)
4034 ig.Emit (OpCodes.Br, end);
4036 ig.BeginExceptionBlock ();
4038 ig.MarkLabel (dispose_try_block);
4040 Label [] labels = null;
4041 for (int i = 0; i < resume_points.Count; ++i) {
4042 ResumableStatement s = (ResumableStatement) resume_points [i];
4043 Label ret = s.PrepareForDispose (ec, end_of_try);
4044 if (ret.Equals (end_of_try) && labels == null)
4045 continue;
4046 if (labels == null) {
4047 labels = new Label [resume_points.Count];
4048 for (int j = 0; j < i; ++j)
4049 labels [j] = end_of_try;
4051 labels [i] = ret;
4054 if (labels != null) {
4055 int j;
4056 for (j = 1; j < labels.Length; ++j)
4057 if (!labels [0].Equals (labels [j]))
4058 break;
4059 bool emit_dispatcher = j < labels.Length;
4061 if (emit_dispatcher) {
4062 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4063 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4064 IntConstant.EmitInt (ig, first_resume_pc);
4065 ig.Emit (OpCodes.Sub);
4066 ig.Emit (OpCodes.Switch, labels);
4067 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4070 foreach (ResumableStatement s in resume_points)
4071 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4074 ig.MarkLabel (end_of_try);
4076 ig.BeginFinallyBlock ();
4078 EmitFinallyBody (ec);
4080 ig.EndExceptionBlock ();
4084 public class Lock : ExceptionStatement {
4085 Expression expr;
4086 public Statement Statement;
4087 TemporaryVariable temp;
4089 public Lock (Expression expr, Statement stmt, Location l)
4091 this.expr = expr;
4092 Statement = stmt;
4093 loc = l;
4096 public override bool Resolve (BlockContext ec)
4098 expr = expr.Resolve (ec);
4099 if (expr == null)
4100 return false;
4102 if (!TypeManager.IsReferenceType (expr.Type)){
4103 ec.Report.Error (185, loc,
4104 "`{0}' is not a reference type as required by the lock statement",
4105 TypeManager.CSharpName (expr.Type));
4106 return false;
4109 ec.StartFlowBranching (this);
4110 bool ok = Statement.Resolve (ec);
4111 ec.EndFlowBranching ();
4113 ok &= base.Resolve (ec);
4115 // Avoid creating libraries that reference the internal
4116 // mcs NullType:
4117 Type t = expr.Type;
4118 if (t == TypeManager.null_type)
4119 t = TypeManager.object_type;
4121 temp = new TemporaryVariable (t, loc);
4122 temp.Resolve (ec);
4124 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4125 Type monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4126 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4127 monitor_type, "Enter", loc, TypeManager.object_type);
4128 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4129 monitor_type, "Exit", loc, TypeManager.object_type);
4132 return ok;
4135 protected override void EmitPreTryBody (EmitContext ec)
4137 ILGenerator ig = ec.ig;
4139 temp.EmitAssign (ec, expr);
4140 temp.Emit (ec);
4141 ig.Emit (OpCodes.Call, (MethodInfo) TypeManager.void_monitor_enter_object.MetaInfo);
4144 protected override void EmitTryBody (EmitContext ec)
4146 Statement.Emit (ec);
4149 protected override void EmitFinallyBody (EmitContext ec)
4151 temp.Emit (ec);
4152 ec.ig.Emit (OpCodes.Call, (MethodInfo) TypeManager.void_monitor_exit_object.MetaInfo);
4155 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4157 expr.MutateHoistedGenericType (storey);
4158 temp.MutateHoistedGenericType (storey);
4159 Statement.MutateHoistedGenericType (storey);
4162 protected override void CloneTo (CloneContext clonectx, Statement t)
4164 Lock target = (Lock) t;
4166 target.expr = expr.Clone (clonectx);
4167 target.Statement = Statement.Clone (clonectx);
4171 public class Unchecked : Statement {
4172 public Block Block;
4174 public Unchecked (Block b)
4176 Block = b;
4177 b.Unchecked = true;
4180 public override bool Resolve (BlockContext ec)
4182 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4183 return Block.Resolve (ec);
4186 protected override void DoEmit (EmitContext ec)
4188 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4189 Block.Emit (ec);
4192 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4194 Block.MutateHoistedGenericType (storey);
4197 protected override void CloneTo (CloneContext clonectx, Statement t)
4199 Unchecked target = (Unchecked) t;
4201 target.Block = clonectx.LookupBlock (Block);
4205 public class Checked : Statement {
4206 public Block Block;
4208 public Checked (Block b)
4210 Block = b;
4211 b.Unchecked = false;
4214 public override bool Resolve (BlockContext ec)
4216 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4217 return Block.Resolve (ec);
4220 protected override void DoEmit (EmitContext ec)
4222 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4223 Block.Emit (ec);
4226 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4228 Block.MutateHoistedGenericType (storey);
4231 protected override void CloneTo (CloneContext clonectx, Statement t)
4233 Checked target = (Checked) t;
4235 target.Block = clonectx.LookupBlock (Block);
4239 public class Unsafe : Statement {
4240 public Block Block;
4242 public Unsafe (Block b)
4244 Block = b;
4245 Block.Unsafe = true;
4246 loc = b.StartLocation;
4249 public override bool Resolve (BlockContext ec)
4251 if (ec.CurrentIterator != null)
4252 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4254 using (ec.Set (ResolveContext.Options.UnsafeScope))
4255 return Block.Resolve (ec);
4258 protected override void DoEmit (EmitContext ec)
4260 Block.Emit (ec);
4263 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4265 Block.MutateHoistedGenericType (storey);
4268 protected override void CloneTo (CloneContext clonectx, Statement t)
4270 Unsafe target = (Unsafe) t;
4272 target.Block = clonectx.LookupBlock (Block);
4277 // Fixed statement
4279 class Fixed : Statement {
4280 Expression type;
4281 List<KeyValuePair<LocalInfo, Expression>> declarators;
4282 Statement statement;
4283 Type expr_type;
4284 Emitter[] data;
4285 bool has_ret;
4287 abstract class Emitter
4289 protected LocalInfo vi;
4290 protected Expression converted;
4292 protected Emitter (Expression expr, LocalInfo li)
4294 converted = expr;
4295 vi = li;
4298 public abstract void Emit (EmitContext ec);
4299 public abstract void EmitExit (EmitContext ec);
4302 class ExpressionEmitter : Emitter {
4303 public ExpressionEmitter (Expression converted, LocalInfo li) :
4304 base (converted, li)
4308 public override void Emit (EmitContext ec) {
4310 // Store pointer in pinned location
4312 converted.Emit (ec);
4313 vi.EmitAssign (ec);
4316 public override void EmitExit (EmitContext ec)
4318 ec.ig.Emit (OpCodes.Ldc_I4_0);
4319 ec.ig.Emit (OpCodes.Conv_U);
4320 vi.EmitAssign (ec);
4324 class StringEmitter : Emitter
4326 LocalInfo pinned_string;
4328 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4329 base (expr, li)
4331 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4332 pinned_string.Pinned = true;
4335 public StringEmitter Resolve (ResolveContext rc)
4337 pinned_string.Resolve (rc);
4339 if (TypeManager.int_get_offset_to_string_data == null) {
4340 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4341 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4344 return this;
4347 public override void Emit (EmitContext ec)
4349 pinned_string.ResolveVariable (ec);
4351 converted.Emit (ec);
4352 pinned_string.EmitAssign (ec);
4354 // TODO: Should use Binary::Add
4355 pinned_string.Emit (ec);
4356 ec.ig.Emit (OpCodes.Conv_I);
4358 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4359 //pe.InstanceExpression = pinned_string;
4360 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4362 ec.ig.Emit (OpCodes.Add);
4363 vi.EmitAssign (ec);
4366 public override void EmitExit (EmitContext ec)
4368 ec.ig.Emit (OpCodes.Ldnull);
4369 pinned_string.EmitAssign (ec);
4373 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4375 this.type = type;
4376 declarators = decls;
4377 statement = stmt;
4378 loc = l;
4381 public Statement Statement {
4382 get { return statement; }
4385 public override bool Resolve (BlockContext ec)
4387 if (!ec.IsUnsafe){
4388 Expression.UnsafeError (ec, loc);
4389 return false;
4392 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4393 if (texpr == null) {
4394 if (type is VarExpr)
4395 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4397 return false;
4400 expr_type = texpr.Type;
4402 data = new Emitter [declarators.Count];
4404 if (!expr_type.IsPointer){
4405 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4406 return false;
4409 int i = 0;
4410 foreach (var p in declarators){
4411 LocalInfo vi = p.Key;
4412 Expression e = p.Value;
4414 vi.VariableInfo.SetAssigned (ec);
4415 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4418 // The rules for the possible declarators are pretty wise,
4419 // but the production on the grammar is more concise.
4421 // So we have to enforce these rules here.
4423 // We do not resolve before doing the case 1 test,
4424 // because the grammar is explicit in that the token &
4425 // is present, so we need to test for this particular case.
4428 if (e is Cast){
4429 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4430 return false;
4433 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4434 e = e.Resolve (ec);
4437 if (e == null)
4438 return false;
4441 // Case 2: Array
4443 if (e.Type.IsArray){
4444 Type array_type = TypeManager.GetElementType (e.Type);
4447 // Provided that array_type is unmanaged,
4449 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4450 return false;
4453 // and T* is implicitly convertible to the
4454 // pointer type given in the fixed statement.
4456 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4458 Expression converted = Convert.ImplicitConversionRequired (
4459 ec, array_ptr, vi.VariableType, loc);
4460 if (converted == null)
4461 return false;
4464 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4466 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4467 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4468 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4469 new NullPointer (loc),
4470 converted);
4472 converted = converted.Resolve (ec);
4474 data [i] = new ExpressionEmitter (converted, vi);
4475 i++;
4477 continue;
4481 // Case 3: string
4483 if (e.Type == TypeManager.string_type){
4484 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4485 i++;
4486 continue;
4489 // Case 4: fixed buffer
4490 if (e is FixedBufferPtr) {
4491 data [i++] = new ExpressionEmitter (e, vi);
4492 continue;
4496 // Case 1: & object.
4498 Unary u = e as Unary;
4499 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4500 IVariableReference vr = u.Expr as IVariableReference;
4501 if (vr == null || !vr.IsFixed) {
4502 data [i] = new ExpressionEmitter (e, vi);
4506 if (data [i++] == null)
4507 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4509 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4512 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4513 bool ok = statement.Resolve (ec);
4514 bool flow_unreachable = ec.EndFlowBranching ();
4515 has_ret = flow_unreachable;
4517 return ok;
4520 protected override void DoEmit (EmitContext ec)
4522 for (int i = 0; i < data.Length; i++) {
4523 data [i].Emit (ec);
4526 statement.Emit (ec);
4528 if (has_ret)
4529 return;
4532 // Clear the pinned variable
4534 for (int i = 0; i < data.Length; i++) {
4535 data [i].EmitExit (ec);
4539 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4541 // Fixed statement cannot be used inside anonymous methods or lambdas
4542 throw new NotSupportedException ();
4545 protected override void CloneTo (CloneContext clonectx, Statement t)
4547 Fixed target = (Fixed) t;
4549 target.type = type.Clone (clonectx);
4550 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4551 foreach (var p in declarators) {
4552 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4553 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4556 target.statement = statement.Clone (clonectx);
4560 public class Catch : Statement {
4561 public readonly string Name;
4562 public Block Block;
4563 public Block VarBlock;
4565 Expression type_expr;
4566 Type type;
4568 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4570 type_expr = type;
4571 Name = name;
4572 Block = block;
4573 VarBlock = var_block;
4574 loc = l;
4577 public Type CatchType {
4578 get {
4579 return type;
4583 public bool IsGeneral {
4584 get {
4585 return type_expr == null;
4589 protected override void DoEmit (EmitContext ec)
4591 ILGenerator ig = ec.ig;
4593 if (CatchType != null)
4594 ig.BeginCatchBlock (CatchType);
4595 else
4596 ig.BeginCatchBlock (TypeManager.object_type);
4598 if (VarBlock != null)
4599 VarBlock.Emit (ec);
4601 if (Name != null) {
4602 // TODO: Move to resolve
4603 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4604 lvr.Resolve (new ResolveContext (ec.MemberContext));
4606 // Only to make verifier happy
4607 if (TypeManager.IsGenericParameter (lvr.Type))
4608 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4610 Expression source;
4611 if (lvr.IsHoisted) {
4612 LocalTemporary lt = new LocalTemporary (lvr.Type);
4613 lt.Store (ec);
4614 source = lt;
4615 } else {
4616 // Variable is at the top of the stack
4617 source = EmptyExpression.Null;
4620 lvr.EmitAssign (ec, source, false, false);
4621 } else
4622 ig.Emit (OpCodes.Pop);
4624 Block.Emit (ec);
4627 public override bool Resolve (BlockContext ec)
4629 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4630 if (type_expr != null) {
4631 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4632 if (te == null)
4633 return false;
4635 type = te.Type;
4637 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4638 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4639 return false;
4641 } else
4642 type = null;
4644 if (!Block.Resolve (ec))
4645 return false;
4647 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4648 // emit the "unused variable" warnings.
4649 if (VarBlock != null)
4650 return VarBlock.Resolve (ec);
4652 return true;
4656 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4658 if (type != null)
4659 type = storey.MutateType (type);
4660 if (VarBlock != null)
4661 VarBlock.MutateHoistedGenericType (storey);
4662 Block.MutateHoistedGenericType (storey);
4665 protected override void CloneTo (CloneContext clonectx, Statement t)
4667 Catch target = (Catch) t;
4669 if (type_expr != null)
4670 target.type_expr = type_expr.Clone (clonectx);
4671 if (VarBlock != null)
4672 target.VarBlock = clonectx.LookupBlock (VarBlock);
4673 target.Block = clonectx.LookupBlock (Block);
4677 public class TryFinally : ExceptionStatement {
4678 Statement stmt;
4679 Block fini;
4681 public TryFinally (Statement stmt, Block fini, Location l)
4683 this.stmt = stmt;
4684 this.fini = fini;
4685 loc = l;
4688 public override bool Resolve (BlockContext ec)
4690 bool ok = true;
4692 ec.StartFlowBranching (this);
4694 if (!stmt.Resolve (ec))
4695 ok = false;
4697 if (ok)
4698 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4699 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4700 if (!fini.Resolve (ec))
4701 ok = false;
4704 ec.EndFlowBranching ();
4706 ok &= base.Resolve (ec);
4708 return ok;
4711 protected override void EmitPreTryBody (EmitContext ec)
4715 protected override void EmitTryBody (EmitContext ec)
4717 stmt.Emit (ec);
4720 protected override void EmitFinallyBody (EmitContext ec)
4722 fini.Emit (ec);
4725 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4727 stmt.MutateHoistedGenericType (storey);
4728 fini.MutateHoistedGenericType (storey);
4731 protected override void CloneTo (CloneContext clonectx, Statement t)
4733 TryFinally target = (TryFinally) t;
4735 target.stmt = (Statement) stmt.Clone (clonectx);
4736 if (fini != null)
4737 target.fini = clonectx.LookupBlock (fini);
4741 public class TryCatch : Statement {
4742 public Block Block;
4743 public List<Catch> Specific;
4744 public Catch General;
4745 bool inside_try_finally, code_follows;
4747 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4749 this.Block = block;
4750 this.Specific = catch_clauses;
4751 this.inside_try_finally = inside_try_finally;
4753 Catch c = catch_clauses [0];
4754 if (c.IsGeneral) {
4755 this.General = c;
4756 catch_clauses.RemoveAt (0);
4759 loc = l;
4762 public override bool Resolve (BlockContext ec)
4764 bool ok = true;
4766 ec.StartFlowBranching (this);
4768 if (!Block.Resolve (ec))
4769 ok = false;
4771 Type[] prev_catches = new Type [Specific.Count];
4772 int last_index = 0;
4773 foreach (Catch c in Specific){
4774 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4776 if (c.Name != null) {
4777 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4778 if (vi == null)
4779 throw new Exception ();
4781 vi.VariableInfo = null;
4784 if (!c.Resolve (ec)) {
4785 ok = false;
4786 continue;
4789 Type resolved_type = c.CatchType;
4790 for (int ii = 0; ii < last_index; ++ii) {
4791 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4792 ec.Report.Error (160, c.loc,
4793 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4794 TypeManager.CSharpName (prev_catches [ii]));
4795 ok = false;
4799 prev_catches [last_index++] = resolved_type;
4802 if (General != null) {
4803 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4804 foreach (Catch c in Specific){
4805 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4806 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'");
4811 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4813 if (!General.Resolve (ec))
4814 ok = false;
4817 ec.EndFlowBranching ();
4819 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4820 // So, ensure there's some IL code after this statement
4821 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4822 ec.NeedReturnLabel ();
4824 return ok;
4827 public void SomeCodeFollows ()
4829 code_follows = true;
4832 protected override void DoEmit (EmitContext ec)
4834 ILGenerator ig = ec.ig;
4836 if (!inside_try_finally)
4837 ig.BeginExceptionBlock ();
4839 Block.Emit (ec);
4841 foreach (Catch c in Specific)
4842 c.Emit (ec);
4844 if (General != null)
4845 General.Emit (ec);
4847 if (!inside_try_finally)
4848 ig.EndExceptionBlock ();
4851 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4853 Block.MutateHoistedGenericType (storey);
4855 if (General != null)
4856 General.MutateHoistedGenericType (storey);
4857 if (Specific != null) {
4858 foreach (Catch c in Specific)
4859 c.MutateHoistedGenericType (storey);
4863 protected override void CloneTo (CloneContext clonectx, Statement t)
4865 TryCatch target = (TryCatch) t;
4867 target.Block = clonectx.LookupBlock (Block);
4868 if (General != null)
4869 target.General = (Catch) General.Clone (clonectx);
4870 if (Specific != null){
4871 target.Specific = new List<Catch> ();
4872 foreach (Catch c in Specific)
4873 target.Specific.Add ((Catch) c.Clone (clonectx));
4878 // FIXME: Why is it almost exact copy of Using ??
4879 public class UsingTemporary : ExceptionStatement {
4880 TemporaryVariable local_copy;
4881 public Statement Statement;
4882 Expression expr;
4883 Type expr_type;
4885 public UsingTemporary (Expression expr, Statement stmt, Location l)
4887 this.expr = expr;
4888 Statement = stmt;
4889 loc = l;
4892 public override bool Resolve (BlockContext ec)
4894 expr = expr.Resolve (ec);
4895 if (expr == null)
4896 return false;
4898 expr_type = expr.Type;
4900 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type) &&
4901 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4902 if (!TypeManager.IsDynamicType (expr_type)) {
4903 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4904 return false;
4907 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4908 expr_type = expr.Type;
4911 local_copy = new TemporaryVariable (expr_type, loc);
4912 local_copy.Resolve (ec);
4914 ec.StartFlowBranching (this);
4916 bool ok = Statement.Resolve (ec);
4918 ec.EndFlowBranching ();
4920 ok &= base.Resolve (ec);
4922 if (TypeManager.void_dispose_void == null) {
4923 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4924 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4927 return ok;
4930 protected override void EmitPreTryBody (EmitContext ec)
4932 local_copy.EmitAssign (ec, expr);
4935 protected override void EmitTryBody (EmitContext ec)
4937 Statement.Emit (ec);
4940 protected override void EmitFinallyBody (EmitContext ec)
4942 ILGenerator ig = ec.ig;
4943 if (!TypeManager.IsStruct (expr_type)) {
4944 Label skip = ig.DefineLabel ();
4945 local_copy.Emit (ec);
4946 ig.Emit (OpCodes.Brfalse, skip);
4947 local_copy.Emit (ec);
4948 ig.Emit (OpCodes.Callvirt, (MethodInfo) TypeManager.void_dispose_void.MetaInfo);
4949 ig.MarkLabel (skip);
4950 return;
4953 Expression ml = Expression.MemberLookup (RootContext.ToplevelTypes.Compiler,
4954 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4955 "Dispose", Location.Null);
4957 if (!(ml is MethodGroupExpr)) {
4958 local_copy.Emit (ec);
4959 ig.Emit (OpCodes.Box, expr_type);
4960 ig.Emit (OpCodes.Callvirt, (MethodInfo) TypeManager.void_dispose_void.MetaInfo);
4961 return;
4964 MethodSpec mi = null;
4966 foreach (var mk in ((MethodGroupExpr) ml).Methods) {
4967 if (mk.Parameters.IsEmpty) {
4968 mi = mk;
4969 break;
4973 if (mi == null) {
4974 ec.Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4975 return;
4978 local_copy.AddressOf (ec, AddressOp.Load);
4979 ig.Emit (OpCodes.Call, (MethodInfo) mi.MetaInfo);
4982 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4984 expr_type = storey.MutateType (expr_type);
4985 local_copy.MutateHoistedGenericType (storey);
4986 Statement.MutateHoistedGenericType (storey);
4989 protected override void CloneTo (CloneContext clonectx, Statement t)
4991 UsingTemporary target = (UsingTemporary) t;
4993 target.expr = expr.Clone (clonectx);
4994 target.Statement = Statement.Clone (clonectx);
4998 public class Using : ExceptionStatement {
4999 Statement stmt;
5000 public Statement EmbeddedStatement {
5001 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5004 Expression var;
5005 Expression init;
5007 ExpressionStatement assign;
5009 public Using (Expression var, Expression init, Statement stmt, Location l)
5011 this.var = var;
5012 this.init = init;
5013 this.stmt = stmt;
5014 loc = l;
5017 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
5019 ec.Report.SymbolRelatedToPreviousError (expr.Type);
5020 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5021 TypeManager.CSharpName (expr.Type));
5024 protected override void EmitPreTryBody (EmitContext ec)
5026 assign.EmitStatement (ec);
5029 protected override void EmitTryBody (EmitContext ec)
5031 stmt.Emit (ec);
5034 protected override void EmitFinallyBody (EmitContext ec)
5036 ILGenerator ig = ec.ig;
5037 Label skip = ig.DefineLabel ();
5039 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5040 if (emit_null_check) {
5041 var.Emit (ec);
5042 ig.Emit (OpCodes.Brfalse, skip);
5045 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5047 if (emit_null_check)
5048 ig.MarkLabel (skip);
5051 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5053 assign.MutateHoistedGenericType (storey);
5054 var.MutateHoistedGenericType (storey);
5055 stmt.MutateHoistedGenericType (storey);
5058 public override bool Resolve (BlockContext ec)
5060 if (!ResolveVariable (ec))
5061 return false;
5063 ec.StartFlowBranching (this);
5065 bool ok = stmt.Resolve (ec);
5067 ec.EndFlowBranching ();
5069 ok &= base.Resolve (ec);
5071 if (TypeManager.void_dispose_void == null) {
5072 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5073 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5076 return ok;
5079 bool ResolveVariable (BlockContext ec)
5081 assign = new SimpleAssign (var, init, loc);
5082 assign = assign.ResolveStatement (ec);
5083 if (assign == null)
5084 return false;
5086 if (assign.Type == TypeManager.idisposable_type ||
5087 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5088 return true;
5091 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5092 if (e == null) {
5093 if (TypeManager.IsDynamicType (assign.Type)) {
5094 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
5095 var = new TemporaryVariable (e.Type, loc);
5096 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
5097 return true;
5100 Error_IsNotConvertibleToIDisposable (ec, var);
5101 return false;
5104 throw new NotImplementedException ("covariance?");
5107 protected override void CloneTo (CloneContext clonectx, Statement t)
5109 Using target = (Using) t;
5111 target.var = var.Clone (clonectx);
5112 target.init = init.Clone (clonectx);
5113 target.stmt = stmt.Clone (clonectx);
5117 /// <summary>
5118 /// Implementation of the foreach C# statement
5119 /// </summary>
5120 public class Foreach : Statement {
5122 sealed class ArrayForeach : Statement
5124 class ArrayCounter : TemporaryVariable
5126 StatementExpression increment;
5128 public ArrayCounter (Location loc)
5129 : base (TypeManager.int32_type, loc)
5133 public void ResolveIncrement (BlockContext ec)
5135 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5136 increment.Resolve (ec);
5139 public void EmitIncrement (EmitContext ec)
5141 increment.Emit (ec);
5145 readonly Foreach for_each;
5146 readonly Statement statement;
5148 Expression conv;
5149 TemporaryVariable[] lengths;
5150 Expression [] length_exprs;
5151 ArrayCounter[] counter;
5153 TemporaryVariable copy;
5154 Expression access;
5156 public ArrayForeach (Foreach @foreach, int rank)
5158 for_each = @foreach;
5159 statement = for_each.statement;
5160 loc = @foreach.loc;
5162 counter = new ArrayCounter [rank];
5163 length_exprs = new Expression [rank];
5166 // Only use temporary length variables when dealing with
5167 // multi-dimensional arrays
5169 if (rank > 1)
5170 lengths = new TemporaryVariable [rank];
5173 protected override void CloneTo (CloneContext clonectx, Statement target)
5175 throw new NotImplementedException ();
5178 public override bool Resolve (BlockContext ec)
5180 copy = new TemporaryVariable (for_each.expr.Type, loc);
5181 copy.Resolve (ec);
5183 int rank = length_exprs.Length;
5184 Arguments list = new Arguments (rank);
5185 for (int i = 0; i < rank; i++) {
5186 counter [i] = new ArrayCounter (loc);
5187 counter [i].ResolveIncrement (ec);
5189 if (rank == 1) {
5190 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5191 } else {
5192 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5193 lengths [i].Resolve (ec);
5195 Arguments args = new Arguments (1);
5196 args.Add (new Argument (new IntConstant (i, loc)));
5197 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5200 list.Add (new Argument (counter [i]));
5203 access = new ElementAccess (copy, list).Resolve (ec);
5204 if (access == null)
5205 return false;
5207 Expression var_type = for_each.type;
5208 VarExpr ve = var_type as VarExpr;
5209 if (ve != null) {
5210 // Infer implicitly typed local variable from foreach array type
5211 var_type = new TypeExpression (access.Type, ve.Location);
5214 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5215 if (var_type == null)
5216 return false;
5218 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5219 if (conv == null)
5220 return false;
5222 bool ok = true;
5224 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5225 ec.CurrentBranching.CreateSibling ();
5227 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5228 if (for_each.variable == null)
5229 ok = false;
5231 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5232 if (!statement.Resolve (ec))
5233 ok = false;
5234 ec.EndFlowBranching ();
5236 // There's no direct control flow from the end of the embedded statement to the end of the loop
5237 ec.CurrentBranching.CurrentUsageVector.Goto ();
5239 ec.EndFlowBranching ();
5241 return ok;
5244 protected override void DoEmit (EmitContext ec)
5246 ILGenerator ig = ec.ig;
5248 copy.EmitAssign (ec, for_each.expr);
5250 int rank = length_exprs.Length;
5251 Label[] test = new Label [rank];
5252 Label[] loop = new Label [rank];
5254 for (int i = 0; i < rank; i++) {
5255 test [i] = ig.DefineLabel ();
5256 loop [i] = ig.DefineLabel ();
5258 if (lengths != null)
5259 lengths [i].EmitAssign (ec, length_exprs [i]);
5262 IntConstant zero = new IntConstant (0, loc);
5263 for (int i = 0; i < rank; i++) {
5264 counter [i].EmitAssign (ec, zero);
5266 ig.Emit (OpCodes.Br, test [i]);
5267 ig.MarkLabel (loop [i]);
5270 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5272 statement.Emit (ec);
5274 ig.MarkLabel (ec.LoopBegin);
5276 for (int i = rank - 1; i >= 0; i--){
5277 counter [i].EmitIncrement (ec);
5279 ig.MarkLabel (test [i]);
5280 counter [i].Emit (ec);
5282 if (lengths != null)
5283 lengths [i].Emit (ec);
5284 else
5285 length_exprs [i].Emit (ec);
5287 ig.Emit (OpCodes.Blt, loop [i]);
5290 ig.MarkLabel (ec.LoopEnd);
5293 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5295 for_each.expr.MutateHoistedGenericType (storey);
5297 copy.MutateHoistedGenericType (storey);
5298 conv.MutateHoistedGenericType (storey);
5299 statement.MutateHoistedGenericType (storey);
5301 for (int i = 0; i < counter.Length; i++) {
5302 counter [i].MutateHoistedGenericType (storey);
5303 if (lengths != null)
5304 lengths [i].MutateHoistedGenericType (storey);
5309 sealed class CollectionForeach : Statement
5311 class CollectionForeachStatement : Statement
5313 Type type;
5314 Expression variable, current, conv;
5315 Statement statement;
5316 Assign assign;
5318 public CollectionForeachStatement (Type type, Expression variable,
5319 Expression current, Statement statement,
5320 Location loc)
5322 this.type = type;
5323 this.variable = variable;
5324 this.current = current;
5325 this.statement = statement;
5326 this.loc = loc;
5329 protected override void CloneTo (CloneContext clonectx, Statement target)
5331 throw new NotImplementedException ();
5334 public override bool Resolve (BlockContext ec)
5336 current = current.Resolve (ec);
5337 if (current == null)
5338 return false;
5340 conv = Convert.ExplicitConversion (ec, current, type, loc);
5341 if (conv == null)
5342 return false;
5344 assign = new SimpleAssign (variable, conv, loc);
5345 if (assign.Resolve (ec) == null)
5346 return false;
5348 if (!statement.Resolve (ec))
5349 return false;
5351 return true;
5354 protected override void DoEmit (EmitContext ec)
5356 assign.EmitStatement (ec);
5357 statement.Emit (ec);
5360 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5362 assign.MutateHoistedGenericType (storey);
5363 statement.MutateHoistedGenericType (storey);
5367 Expression variable, expr;
5368 Statement statement;
5370 TemporaryVariable enumerator;
5371 Expression init;
5372 Statement loop;
5373 Statement wrapper;
5375 MethodGroupExpr get_enumerator;
5376 PropertyExpr get_current;
5377 MethodSpec move_next;
5378 Expression var_type;
5379 Type enumerator_type;
5380 bool enumerator_found;
5382 public CollectionForeach (Expression var_type, Expression var,
5383 Expression expr, Statement stmt, Location l)
5385 this.var_type = var_type;
5386 this.variable = var;
5387 this.expr = expr;
5388 statement = stmt;
5389 loc = l;
5392 protected override void CloneTo (CloneContext clonectx, Statement target)
5394 throw new NotImplementedException ();
5397 bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi)
5399 Type return_type = mi.ReturnType;
5402 // Ok, we can access it, now make sure that we can do something
5403 // with this `GetEnumerator'
5406 if (return_type == TypeManager.ienumerator_type ||
5407 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5409 // If it is not an interface, lets try to find the methods ourselves.
5410 // For example, if we have:
5411 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5412 // We can avoid the iface call. This is a runtime perf boost.
5413 // even bigger if we have a ValueType, because we avoid the cost
5414 // of boxing.
5416 // We have to make sure that both methods exist for us to take
5417 // this path. If one of the methods does not exist, we will just
5418 // use the interface. Sadly, this complex if statement is the only
5419 // way I could do this without a goto
5422 if (TypeManager.bool_movenext_void == null) {
5423 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5424 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5427 if (TypeManager.ienumerator_getcurrent == null) {
5428 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5429 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5433 // Prefer a generic enumerator over a non-generic one.
5435 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5436 enumerator_type = return_type;
5437 if (!FetchGetCurrent (ec, return_type))
5438 get_current = new PropertyExpr (
5439 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5440 if (!FetchMoveNext (return_type))
5441 move_next = TypeManager.bool_movenext_void;
5442 return true;
5445 if (return_type.IsInterface ||
5446 !FetchMoveNext (return_type) ||
5447 !FetchGetCurrent (ec, return_type)) {
5448 enumerator_type = return_type;
5449 move_next = TypeManager.bool_movenext_void;
5450 get_current = new PropertyExpr (
5451 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5452 return true;
5454 } else {
5456 // Ok, so they dont return an IEnumerable, we will have to
5457 // find if they support the GetEnumerator pattern.
5460 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5461 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",
5462 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5463 return false;
5467 enumerator_type = return_type;
5469 return true;
5473 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5475 bool FetchMoveNext (Type t)
5477 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5478 MemberTypes.Method,
5479 BindingFlags.Public | BindingFlags.Instance,
5480 "MoveNext", null);
5482 if (move_next_list == null)
5483 return false;
5485 foreach (MemberInfo m in move_next_list){
5486 MethodInfo mi = (MethodInfo) m;
5488 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5489 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5490 move_next = Import.CreateMethod (mi);
5491 return true;
5495 return false;
5499 // Retrieves a `public T get_Current ()' method from the Type `t'
5501 bool FetchGetCurrent (ResolveContext ec, Type t)
5503 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5504 ec.CurrentType, t, "Current", MemberTypes.Property,
5505 Expression.AllBindingFlags, loc) as PropertyExpr;
5506 if (pe == null)
5507 return false;
5509 get_current = pe;
5510 return true;
5513 void Error_Enumerator (BlockContext ec)
5515 if (enumerator_found) {
5516 return;
5519 ec.Report.Error (1579, loc,
5520 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5521 TypeManager.CSharpName (expr.Type));
5524 bool IsOverride (MethodSpec ms)
5526 MethodInfo m = (MethodInfo) ms.MetaInfo;
5527 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5529 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5530 return false;
5531 if (m is MethodBuilder)
5532 return true;
5534 MethodInfo base_method = m.GetBaseDefinition ();
5535 return base_method != m;
5538 bool TryType (ResolveContext ec, Type t)
5540 MethodGroupExpr mg = Expression.MemberLookup (ec.Compiler,
5541 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5542 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5543 if (mg == null)
5544 return false;
5546 MethodSpec result = null;
5547 MethodSpec tmp_move_next = null;
5548 PropertyExpr tmp_get_cur = null;
5549 Type tmp_enumerator_type = enumerator_type;
5550 foreach (var mi in mg.Methods) {
5551 if (!mi.Parameters.IsEmpty)
5552 continue;
5554 // Check whether GetEnumerator is public
5555 if ((mi.MetaInfo.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5556 continue;
5558 if (IsOverride (mi))
5559 continue;
5561 enumerator_found = true;
5563 if (!GetEnumeratorFilter (ec, mi))
5564 continue;
5566 if (result != null) {
5567 if (TypeManager.IsGenericType (result.ReturnType)) {
5568 if (!TypeManager.IsGenericType (mi.ReturnType))
5569 continue;
5571 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5572 ec.Report.SymbolRelatedToPreviousError (t);
5573 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5574 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5575 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5576 return false;
5579 // Always prefer generics enumerators
5580 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5581 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5582 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5583 continue;
5585 ec.Report.SymbolRelatedToPreviousError (result.MetaInfo);
5586 ec.Report.SymbolRelatedToPreviousError (mi.MetaInfo);
5587 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5588 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result.MetaInfo), TypeManager.CSharpSignature (mi.MetaInfo));
5589 return false;
5592 result = mi;
5593 tmp_move_next = move_next;
5594 tmp_get_cur = get_current;
5595 tmp_enumerator_type = enumerator_type;
5596 if (mi.DeclaringType == t)
5597 break;
5600 if (result != null) {
5601 move_next = tmp_move_next;
5602 get_current = tmp_get_cur;
5603 enumerator_type = tmp_enumerator_type;
5604 var mi = new [] { result };
5605 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5607 if (t != expr.Type) {
5608 expr = Convert.ExplicitConversion (
5609 ec, expr, t, loc);
5610 if (expr == null)
5611 throw new InternalErrorException ();
5614 get_enumerator.InstanceExpression = expr;
5615 get_enumerator.IsBase = t != expr.Type;
5617 return true;
5620 return false;
5623 bool ProbeCollectionType (ResolveContext ec, Type t)
5625 int errors = ec.Report.Errors;
5626 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5627 if (TryType (ec, tt))
5628 return true;
5629 tt = tt.BaseType;
5632 if (ec.Report.Errors > errors)
5633 return false;
5636 // Now try to find the method in the interfaces
5638 Type [] ifaces = TypeManager.GetInterfaces (t);
5639 foreach (Type i in ifaces){
5640 if (TryType (ec, i))
5641 return true;
5644 return false;
5647 public override bool Resolve (BlockContext ec)
5649 enumerator_type = TypeManager.ienumerator_type;
5651 bool is_dynamic = TypeManager.IsDynamicType (expr.Type);
5652 if (is_dynamic)
5653 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5655 if (!ProbeCollectionType (ec, expr.Type)) {
5656 Error_Enumerator (ec);
5657 return false;
5660 VarExpr ve = var_type as VarExpr;
5661 if (ve != null) {
5662 // Infer implicitly typed local variable from foreach enumerable type
5663 var_type = new TypeExpression (
5664 is_dynamic ? InternalType.Dynamic : get_current.PropertyInfo.PropertyType,
5665 var_type.Location);
5668 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5669 if (var_type == null)
5670 return false;
5672 enumerator = new TemporaryVariable (enumerator_type, loc);
5673 enumerator.Resolve (ec);
5675 init = new Invocation (get_enumerator, null);
5676 init = init.Resolve (ec);
5677 if (init == null)
5678 return false;
5680 Expression move_next_expr;
5682 var mi = new [] { move_next };
5683 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5684 mg.InstanceExpression = enumerator;
5686 move_next_expr = new Invocation (mg, null);
5689 get_current.InstanceExpression = enumerator;
5691 Statement block = new CollectionForeachStatement (
5692 var_type.Type, variable, get_current, statement, loc);
5694 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5697 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5698 if (implements_idisposable || !enumerator_type.IsSealed) {
5699 wrapper = new DisposableWrapper (this, implements_idisposable);
5700 } else {
5701 wrapper = new NonDisposableWrapper (this);
5704 return wrapper.Resolve (ec);
5707 protected override void DoEmit (EmitContext ec)
5709 wrapper.Emit (ec);
5712 class NonDisposableWrapper : Statement {
5713 CollectionForeach parent;
5715 internal NonDisposableWrapper (CollectionForeach parent)
5717 this.parent = parent;
5720 protected override void CloneTo (CloneContext clonectx, Statement target)
5722 throw new NotSupportedException ();
5725 public override bool Resolve (BlockContext ec)
5727 return parent.ResolveLoop (ec);
5730 protected override void DoEmit (EmitContext ec)
5732 parent.EmitLoopInit (ec);
5733 parent.EmitLoopBody (ec);
5736 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5738 throw new NotSupportedException ();
5742 sealed class DisposableWrapper : ExceptionStatement
5744 CollectionForeach parent;
5745 bool implements_idisposable;
5747 internal DisposableWrapper (CollectionForeach parent, bool implements)
5749 this.parent = parent;
5750 this.implements_idisposable = implements;
5753 protected override void CloneTo (CloneContext clonectx, Statement target)
5755 throw new NotSupportedException ();
5758 public override bool Resolve (BlockContext ec)
5760 bool ok = true;
5762 ec.StartFlowBranching (this);
5764 if (!parent.ResolveLoop (ec))
5765 ok = false;
5767 ec.EndFlowBranching ();
5769 ok &= base.Resolve (ec);
5771 if (TypeManager.void_dispose_void == null) {
5772 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5773 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5775 return ok;
5778 protected override void EmitPreTryBody (EmitContext ec)
5780 parent.EmitLoopInit (ec);
5783 protected override void EmitTryBody (EmitContext ec)
5785 parent.EmitLoopBody (ec);
5788 protected override void EmitFinallyBody (EmitContext ec)
5790 Expression instance = parent.enumerator;
5791 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5792 ILGenerator ig = ec.ig;
5794 parent.enumerator.Emit (ec);
5796 Label call_dispose = ig.DefineLabel ();
5798 if (!implements_idisposable) {
5799 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5800 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5801 temp.Store (ec);
5802 temp.Emit (ec);
5803 instance = temp;
5806 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5808 // using 'endfinally' to empty the evaluation stack
5809 ig.Emit (OpCodes.Endfinally);
5810 ig.MarkLabel (call_dispose);
5813 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5816 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5818 throw new NotSupportedException ();
5822 bool ResolveLoop (BlockContext ec)
5824 return loop.Resolve (ec);
5827 void EmitLoopInit (EmitContext ec)
5829 enumerator.EmitAssign (ec, init);
5832 void EmitLoopBody (EmitContext ec)
5834 loop.Emit (ec);
5837 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5839 enumerator_type = storey.MutateType (enumerator_type);
5840 init.MutateHoistedGenericType (storey);
5841 loop.MutateHoistedGenericType (storey);
5845 Expression type;
5846 Expression variable;
5847 Expression expr;
5848 Statement statement;
5850 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5851 Statement stmt, Location l)
5853 this.type = type;
5854 this.variable = var;
5855 this.expr = expr;
5856 statement = stmt;
5857 loc = l;
5860 public Statement Statement {
5861 get { return statement; }
5864 public override bool Resolve (BlockContext ec)
5866 expr = expr.Resolve (ec);
5867 if (expr == null)
5868 return false;
5870 if (expr.IsNull) {
5871 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5872 return false;
5875 if (expr.Type == TypeManager.string_type) {
5876 statement = new ArrayForeach (this, 1);
5877 } else if (expr.Type.IsArray) {
5878 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5879 } else {
5880 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5881 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5882 expr.ExprClassName);
5883 return false;
5886 statement = new CollectionForeach (type, variable, expr, statement, loc);
5889 return statement.Resolve (ec);
5892 protected override void DoEmit (EmitContext ec)
5894 ILGenerator ig = ec.ig;
5896 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5897 ec.LoopBegin = ig.DefineLabel ();
5898 ec.LoopEnd = ig.DefineLabel ();
5900 statement.Emit (ec);
5902 ec.LoopBegin = old_begin;
5903 ec.LoopEnd = old_end;
5906 protected override void CloneTo (CloneContext clonectx, Statement t)
5908 Foreach target = (Foreach) t;
5910 target.type = type.Clone (clonectx);
5911 target.variable = variable.Clone (clonectx);
5912 target.expr = expr.Clone (clonectx);
5913 target.statement = statement.Clone (clonectx);
5916 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5918 statement.MutateHoistedGenericType (storey);