2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / statement.cs
blob12bd6b6977dee2cb219ee6bb34f6c54440036f05
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 public EmptyStatement (Location loc)
103 this.loc = loc;
106 public override bool Resolve (BlockContext ec)
108 return true;
111 public override bool ResolveUnreachable (BlockContext ec, bool warn)
113 return true;
116 public override void Emit (EmitContext ec)
120 protected override void DoEmit (EmitContext ec)
122 throw new NotSupportedException ();
125 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
129 protected override void CloneTo (CloneContext clonectx, Statement target)
131 // nothing needed.
135 public class If : Statement {
136 Expression expr;
137 public Statement TrueStatement;
138 public Statement FalseStatement;
140 bool is_true_ret;
142 public If (Expression bool_expr, Statement true_statement, Location l)
143 : this (bool_expr, true_statement, null, l)
147 public If (Expression bool_expr,
148 Statement true_statement,
149 Statement false_statement,
150 Location l)
152 this.expr = bool_expr;
153 TrueStatement = true_statement;
154 FalseStatement = false_statement;
155 loc = l;
158 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
160 expr.MutateHoistedGenericType (storey);
161 TrueStatement.MutateHoistedGenericType (storey);
162 if (FalseStatement != null)
163 FalseStatement.MutateHoistedGenericType (storey);
166 public override bool Resolve (BlockContext ec)
168 bool ok = true;
170 Report.Debug (1, "START IF BLOCK", loc);
172 expr = expr.Resolve (ec);
173 if (expr == null) {
174 ok = false;
175 } else {
177 // Dead code elimination
179 if (expr is Constant) {
180 bool take = !((Constant) expr).IsDefaultValue;
182 if (take) {
183 if (!TrueStatement.Resolve (ec))
184 return false;
186 if ((FalseStatement != null) &&
187 !FalseStatement.ResolveUnreachable (ec, true))
188 return false;
189 FalseStatement = null;
190 } else {
191 if (!TrueStatement.ResolveUnreachable (ec, true))
192 return false;
193 TrueStatement = null;
195 if ((FalseStatement != null) &&
196 !FalseStatement.Resolve (ec))
197 return false;
200 return true;
204 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
206 ok &= TrueStatement.Resolve (ec);
208 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
210 ec.CurrentBranching.CreateSibling ();
212 if (FalseStatement != null)
213 ok &= FalseStatement.Resolve (ec);
215 ec.EndFlowBranching ();
217 Report.Debug (1, "END IF BLOCK", loc);
219 return ok;
222 protected override void DoEmit (EmitContext ec)
224 ILGenerator ig = ec.ig;
225 Label false_target = ig.DefineLabel ();
226 Label end;
229 // If we're a boolean constant, Resolve() already
230 // eliminated dead code for us.
232 Constant c = expr as Constant;
233 if (c != null){
234 c.EmitSideEffect (ec);
236 if (!c.IsDefaultValue)
237 TrueStatement.Emit (ec);
238 else if (FalseStatement != null)
239 FalseStatement.Emit (ec);
241 return;
244 expr.EmitBranchable (ec, false_target, false);
246 TrueStatement.Emit (ec);
248 if (FalseStatement != null){
249 bool branch_emitted = false;
251 end = ig.DefineLabel ();
252 if (!is_true_ret){
253 ig.Emit (OpCodes.Br, end);
254 branch_emitted = true;
257 ig.MarkLabel (false_target);
258 FalseStatement.Emit (ec);
260 if (branch_emitted)
261 ig.MarkLabel (end);
262 } else {
263 ig.MarkLabel (false_target);
267 protected override void CloneTo (CloneContext clonectx, Statement t)
269 If target = (If) t;
271 target.expr = expr.Clone (clonectx);
272 target.TrueStatement = TrueStatement.Clone (clonectx);
273 if (FalseStatement != null)
274 target.FalseStatement = FalseStatement.Clone (clonectx);
278 public class Do : Statement {
279 public Expression expr;
280 public Statement EmbeddedStatement;
282 public Do (Statement statement, BooleanExpression bool_expr, Location l)
284 expr = bool_expr;
285 EmbeddedStatement = statement;
286 loc = l;
289 public override bool Resolve (BlockContext ec)
291 bool ok = true;
293 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
295 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
297 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
298 if (!EmbeddedStatement.Resolve (ec))
299 ok = false;
300 ec.EndFlowBranching ();
302 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
303 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
305 expr = expr.Resolve (ec);
306 if (expr == null)
307 ok = false;
308 else if (expr is Constant){
309 bool infinite = !((Constant) expr).IsDefaultValue;
310 if (infinite)
311 ec.CurrentBranching.CurrentUsageVector.Goto ();
314 ec.EndFlowBranching ();
316 return ok;
319 protected override void DoEmit (EmitContext ec)
321 ILGenerator ig = ec.ig;
322 Label loop = ig.DefineLabel ();
323 Label old_begin = ec.LoopBegin;
324 Label old_end = ec.LoopEnd;
326 ec.LoopBegin = ig.DefineLabel ();
327 ec.LoopEnd = ig.DefineLabel ();
329 ig.MarkLabel (loop);
330 EmbeddedStatement.Emit (ec);
331 ig.MarkLabel (ec.LoopBegin);
334 // Dead code elimination
336 if (expr is Constant){
337 bool res = !((Constant) expr).IsDefaultValue;
339 expr.EmitSideEffect (ec);
340 if (res)
341 ec.ig.Emit (OpCodes.Br, loop);
342 } else
343 expr.EmitBranchable (ec, loop, true);
345 ig.MarkLabel (ec.LoopEnd);
347 ec.LoopBegin = old_begin;
348 ec.LoopEnd = old_end;
351 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
353 expr.MutateHoistedGenericType (storey);
354 EmbeddedStatement.MutateHoistedGenericType (storey);
357 protected override void CloneTo (CloneContext clonectx, Statement t)
359 Do target = (Do) t;
361 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
362 target.expr = expr.Clone (clonectx);
366 public class While : Statement {
367 public Expression expr;
368 public Statement Statement;
369 bool infinite, empty;
371 public While (BooleanExpression bool_expr, Statement statement, Location l)
373 this.expr = bool_expr;
374 Statement = statement;
375 loc = l;
378 public override bool Resolve (BlockContext ec)
380 bool ok = true;
382 expr = expr.Resolve (ec);
383 if (expr == null)
384 ok = false;
387 // Inform whether we are infinite or not
389 if (expr is Constant){
390 bool value = !((Constant) expr).IsDefaultValue;
392 if (value == false){
393 if (!Statement.ResolveUnreachable (ec, true))
394 return false;
395 empty = true;
396 return true;
397 } else
398 infinite = true;
401 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
402 if (!infinite)
403 ec.CurrentBranching.CreateSibling ();
405 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
406 if (!Statement.Resolve (ec))
407 ok = false;
408 ec.EndFlowBranching ();
410 // There's no direct control flow from the end of the embedded statement to the end of the loop
411 ec.CurrentBranching.CurrentUsageVector.Goto ();
413 ec.EndFlowBranching ();
415 return ok;
418 protected override void DoEmit (EmitContext ec)
420 if (empty) {
421 expr.EmitSideEffect (ec);
422 return;
425 ILGenerator ig = ec.ig;
426 Label old_begin = ec.LoopBegin;
427 Label old_end = ec.LoopEnd;
429 ec.LoopBegin = ig.DefineLabel ();
430 ec.LoopEnd = ig.DefineLabel ();
433 // Inform whether we are infinite or not
435 if (expr is Constant){
436 // expr is 'true', since the 'empty' case above handles the 'false' case
437 ig.MarkLabel (ec.LoopBegin);
438 expr.EmitSideEffect (ec);
439 Statement.Emit (ec);
440 ig.Emit (OpCodes.Br, ec.LoopBegin);
443 // Inform that we are infinite (ie, `we return'), only
444 // if we do not `break' inside the code.
446 ig.MarkLabel (ec.LoopEnd);
447 } else {
448 Label while_loop = ig.DefineLabel ();
450 ig.Emit (OpCodes.Br, ec.LoopBegin);
451 ig.MarkLabel (while_loop);
453 Statement.Emit (ec);
455 ig.MarkLabel (ec.LoopBegin);
456 ec.Mark (loc);
458 expr.EmitBranchable (ec, while_loop, true);
460 ig.MarkLabel (ec.LoopEnd);
463 ec.LoopBegin = old_begin;
464 ec.LoopEnd = old_end;
467 public override void Emit (EmitContext ec)
469 DoEmit (ec);
472 protected override void CloneTo (CloneContext clonectx, Statement t)
474 While target = (While) t;
476 target.expr = expr.Clone (clonectx);
477 target.Statement = Statement.Clone (clonectx);
480 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
482 expr.MutateHoistedGenericType (storey);
483 Statement.MutateHoistedGenericType (storey);
487 public class For : Statement {
488 Expression Test;
489 Statement InitStatement;
490 Statement Increment;
491 public Statement Statement;
492 bool infinite, empty;
494 public For (Statement init_statement,
495 BooleanExpression test,
496 Statement increment,
497 Statement statement,
498 Location l)
500 InitStatement = init_statement;
501 Test = test;
502 Increment = increment;
503 Statement = statement;
504 loc = l;
507 public override bool Resolve (BlockContext ec)
509 bool ok = true;
511 if (InitStatement != null){
512 if (!InitStatement.Resolve (ec))
513 ok = false;
516 if (Test != null){
517 Test = Test.Resolve (ec);
518 if (Test == null)
519 ok = false;
520 else if (Test is Constant){
521 bool value = !((Constant) Test).IsDefaultValue;
523 if (value == false){
524 if (!Statement.ResolveUnreachable (ec, true))
525 return false;
526 if ((Increment != null) &&
527 !Increment.ResolveUnreachable (ec, false))
528 return false;
529 empty = true;
530 return true;
531 } else
532 infinite = true;
534 } else
535 infinite = true;
537 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
538 if (!infinite)
539 ec.CurrentBranching.CreateSibling ();
541 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
543 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
544 if (!Statement.Resolve (ec))
545 ok = false;
546 ec.EndFlowBranching ();
548 if (Increment != null){
549 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
550 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
551 ok = false;
552 } else {
553 if (!Increment.Resolve (ec))
554 ok = false;
558 // There's no direct control flow from the end of the embedded statement to the end of the loop
559 ec.CurrentBranching.CurrentUsageVector.Goto ();
561 ec.EndFlowBranching ();
563 return ok;
566 protected override void DoEmit (EmitContext ec)
568 if (InitStatement != null)
569 InitStatement.Emit (ec);
571 if (empty) {
572 Test.EmitSideEffect (ec);
573 return;
576 ILGenerator ig = ec.ig;
577 Label old_begin = ec.LoopBegin;
578 Label old_end = ec.LoopEnd;
579 Label loop = ig.DefineLabel ();
580 Label test = ig.DefineLabel ();
582 ec.LoopBegin = ig.DefineLabel ();
583 ec.LoopEnd = ig.DefineLabel ();
585 ig.Emit (OpCodes.Br, test);
586 ig.MarkLabel (loop);
587 Statement.Emit (ec);
589 ig.MarkLabel (ec.LoopBegin);
590 Increment.Emit (ec);
592 ig.MarkLabel (test);
594 // If test is null, there is no test, and we are just
595 // an infinite loop
597 if (Test != null){
599 // The Resolve code already catches the case for
600 // Test == Constant (false) so we know that
601 // this is true
603 if (Test is Constant) {
604 Test.EmitSideEffect (ec);
605 ig.Emit (OpCodes.Br, loop);
606 } else {
607 Test.EmitBranchable (ec, loop, true);
610 } else
611 ig.Emit (OpCodes.Br, loop);
612 ig.MarkLabel (ec.LoopEnd);
614 ec.LoopBegin = old_begin;
615 ec.LoopEnd = old_end;
618 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
620 if (InitStatement != null)
621 InitStatement.MutateHoistedGenericType (storey);
622 if (Test != null)
623 Test.MutateHoistedGenericType (storey);
624 if (Increment != null)
625 Increment.MutateHoistedGenericType (storey);
627 Statement.MutateHoistedGenericType (storey);
630 protected override void CloneTo (CloneContext clonectx, Statement t)
632 For target = (For) t;
634 if (InitStatement != null)
635 target.InitStatement = InitStatement.Clone (clonectx);
636 if (Test != null)
637 target.Test = Test.Clone (clonectx);
638 if (Increment != null)
639 target.Increment = Increment.Clone (clonectx);
640 target.Statement = Statement.Clone (clonectx);
644 public class StatementExpression : Statement {
645 ExpressionStatement expr;
647 public StatementExpression (ExpressionStatement expr)
649 this.expr = expr;
650 loc = expr.Location;
653 public override bool Resolve (BlockContext ec)
655 expr = expr.ResolveStatement (ec);
656 return expr != null;
659 protected override void DoEmit (EmitContext ec)
661 expr.EmitStatement (ec);
664 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
666 expr.MutateHoistedGenericType (storey);
669 public override string ToString ()
671 return "StatementExpression (" + expr + ")";
674 protected override void CloneTo (CloneContext clonectx, Statement t)
676 StatementExpression target = (StatementExpression) t;
678 target.expr = (ExpressionStatement) expr.Clone (clonectx);
682 // A 'return' or a 'yield break'
683 public abstract class ExitStatement : Statement
685 protected bool unwind_protect;
686 protected abstract bool DoResolve (BlockContext ec);
688 public virtual void Error_FinallyClause (Report Report)
690 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
693 public sealed override bool Resolve (BlockContext ec)
695 if (!DoResolve (ec))
696 return false;
698 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
699 if (unwind_protect)
700 ec.NeedReturnLabel ();
701 ec.CurrentBranching.CurrentUsageVector.Goto ();
702 return true;
706 /// <summary>
707 /// Implements the return statement
708 /// </summary>
709 public class Return : ExitStatement {
710 protected Expression Expr;
711 public Return (Expression expr, Location l)
713 Expr = expr;
714 loc = l;
717 protected override bool DoResolve (BlockContext ec)
719 if (Expr == null) {
720 if (ec.ReturnType == TypeManager.void_type)
721 return true;
723 ec.Report.Error (126, loc,
724 "An object of a type convertible to `{0}' is required for the return statement",
725 TypeManager.CSharpName (ec.ReturnType));
726 return false;
729 if (ec.CurrentBlock.Toplevel.IsIterator) {
730 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
731 "statement to return a value, or yield break to end the iteration");
734 AnonymousExpression am = ec.CurrentAnonymousMethod;
735 if (am == null && ec.ReturnType == TypeManager.void_type) {
736 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
737 ec.GetSignatureForError ());
740 Expr = Expr.Resolve (ec);
741 if (Expr == null)
742 return false;
744 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
745 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
746 return true;
749 if (Expr.Type != ec.ReturnType) {
750 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
752 if (Expr == null) {
753 if (am != null) {
754 ec.Report.Error (1662, loc,
755 "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",
756 am.ContainerType, am.GetSignatureForError ());
758 return false;
762 return true;
765 protected override void DoEmit (EmitContext ec)
767 if (Expr != null) {
768 Expr.Emit (ec);
770 if (unwind_protect)
771 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
774 if (unwind_protect)
775 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
776 else
777 ec.ig.Emit (OpCodes.Ret);
780 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
782 if (Expr != null)
783 Expr.MutateHoistedGenericType (storey);
786 protected override void CloneTo (CloneContext clonectx, Statement t)
788 Return target = (Return) t;
789 // It's null for simple return;
790 if (Expr != null)
791 target.Expr = Expr.Clone (clonectx);
795 public class Goto : Statement {
796 string target;
797 LabeledStatement label;
798 bool unwind_protect;
800 public override bool Resolve (BlockContext ec)
802 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
803 ec.CurrentBranching.CurrentUsageVector.Goto ();
804 return true;
807 public Goto (string label, Location l)
809 loc = l;
810 target = label;
813 public string Target {
814 get { return target; }
817 public void SetResolvedTarget (LabeledStatement label)
819 this.label = label;
820 label.AddReference ();
823 protected override void CloneTo (CloneContext clonectx, Statement target)
825 // Nothing to clone
828 protected override void DoEmit (EmitContext ec)
830 if (label == null)
831 throw new InternalErrorException ("goto emitted before target resolved");
832 Label l = label.LabelTarget (ec);
833 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
836 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
841 public class LabeledStatement : Statement {
842 string name;
843 bool defined;
844 bool referenced;
845 Label label;
846 ILGenerator ig;
848 FlowBranching.UsageVector vectors;
850 public LabeledStatement (string name, Location l)
852 this.name = name;
853 this.loc = l;
856 public Label LabelTarget (EmitContext ec)
858 if (defined)
859 return label;
860 ig = ec.ig;
861 label = ec.ig.DefineLabel ();
862 defined = true;
864 return label;
867 public string Name {
868 get { return name; }
871 public bool IsDefined {
872 get { return defined; }
875 public bool HasBeenReferenced {
876 get { return referenced; }
879 public FlowBranching.UsageVector JumpOrigins {
880 get { return vectors; }
883 public void AddUsageVector (FlowBranching.UsageVector vector)
885 vector = vector.Clone ();
886 vector.Next = vectors;
887 vectors = vector;
890 protected override void CloneTo (CloneContext clonectx, Statement target)
892 // nothing to clone
895 public override bool Resolve (BlockContext ec)
897 // this flow-branching will be terminated when the surrounding block ends
898 ec.StartFlowBranching (this);
899 return true;
902 protected override void DoEmit (EmitContext ec)
904 if (ig != null && ig != ec.ig)
905 throw new InternalErrorException ("cannot happen");
906 LabelTarget (ec);
907 ec.ig.MarkLabel (label);
910 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
914 public void AddReference ()
916 referenced = true;
921 /// <summary>
922 /// `goto default' statement
923 /// </summary>
924 public class GotoDefault : Statement {
926 public GotoDefault (Location l)
928 loc = l;
931 protected override void CloneTo (CloneContext clonectx, Statement target)
933 // nothing to clone
936 public override bool Resolve (BlockContext ec)
938 ec.CurrentBranching.CurrentUsageVector.Goto ();
940 if (ec.Switch == null) {
941 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
942 return false;
945 if (!ec.Switch.GotDefault) {
946 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
947 return false;
950 return true;
953 protected override void DoEmit (EmitContext ec)
955 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
958 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
963 /// <summary>
964 /// `goto case' statement
965 /// </summary>
966 public class GotoCase : Statement {
967 Expression expr;
968 SwitchLabel sl;
970 public GotoCase (Expression e, Location l)
972 expr = e;
973 loc = l;
976 public override bool Resolve (BlockContext ec)
978 if (ec.Switch == null){
979 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
980 return false;
983 ec.CurrentBranching.CurrentUsageVector.Goto ();
985 expr = expr.Resolve (ec);
986 if (expr == null)
987 return false;
989 Constant c = expr as Constant;
990 if (c == null) {
991 ec.Report.Error (150, expr.Location, "A constant value is expected");
992 return false;
995 Type type = ec.Switch.SwitchType;
996 Constant res = c.TryReduce (ec, type, c.Location);
997 if (res == null) {
998 c.Error_ValueCannotBeConverted (ec, loc, type, true);
999 return false;
1002 if (!Convert.ImplicitStandardConversionExists (c, type))
1003 ec.Report.Warning (469, 2, loc,
1004 "The `goto case' value is not implicitly convertible to type `{0}'",
1005 TypeManager.CSharpName (type));
1007 object val = res.GetValue ();
1008 if (val == null)
1009 val = SwitchLabel.NullStringCase;
1011 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1012 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1013 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1014 return false;
1017 return true;
1020 protected override void DoEmit (EmitContext ec)
1022 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1025 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1027 expr.MutateHoistedGenericType (storey);
1030 protected override void CloneTo (CloneContext clonectx, Statement t)
1032 GotoCase target = (GotoCase) t;
1034 target.expr = expr.Clone (clonectx);
1038 public class Throw : Statement {
1039 Expression expr;
1041 public Throw (Expression expr, Location l)
1043 this.expr = expr;
1044 loc = l;
1047 public override bool Resolve (BlockContext ec)
1049 if (expr == null) {
1050 ec.CurrentBranching.CurrentUsageVector.Goto ();
1051 return ec.CurrentBranching.CheckRethrow (loc);
1054 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1055 ec.CurrentBranching.CurrentUsageVector.Goto ();
1057 if (expr == null)
1058 return false;
1060 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1061 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1062 else
1063 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1065 return true;
1068 protected override void DoEmit (EmitContext ec)
1070 if (expr == null)
1071 ec.ig.Emit (OpCodes.Rethrow);
1072 else {
1073 expr.Emit (ec);
1075 ec.ig.Emit (OpCodes.Throw);
1079 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1081 if (expr != null)
1082 expr.MutateHoistedGenericType (storey);
1085 protected override void CloneTo (CloneContext clonectx, Statement t)
1087 Throw target = (Throw) t;
1089 if (expr != null)
1090 target.expr = expr.Clone (clonectx);
1094 public class Break : Statement {
1096 public Break (Location l)
1098 loc = l;
1101 bool unwind_protect;
1103 public override bool Resolve (BlockContext ec)
1105 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1106 ec.CurrentBranching.CurrentUsageVector.Goto ();
1107 return true;
1110 protected override void DoEmit (EmitContext ec)
1112 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1115 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1119 protected override void CloneTo (CloneContext clonectx, Statement t)
1121 // nothing needed
1125 public class Continue : Statement {
1127 public Continue (Location l)
1129 loc = l;
1132 bool unwind_protect;
1134 public override bool Resolve (BlockContext ec)
1136 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1137 ec.CurrentBranching.CurrentUsageVector.Goto ();
1138 return true;
1141 protected override void DoEmit (EmitContext ec)
1143 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1146 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1150 protected override void CloneTo (CloneContext clonectx, Statement t)
1152 // nothing needed.
1156 public interface ILocalVariable
1158 void Emit (EmitContext ec);
1159 void EmitAssign (EmitContext ec);
1160 void EmitAddressOf (EmitContext ec);
1163 public interface IKnownVariable {
1164 Block Block { get; }
1165 Location Location { get; }
1169 // The information about a user-perceived local variable
1171 public class LocalInfo : IKnownVariable, ILocalVariable {
1172 public readonly FullNamedExpression Type;
1174 public Type VariableType;
1175 public readonly string Name;
1176 public readonly Location Location;
1177 public readonly Block Block;
1179 public VariableInfo VariableInfo;
1180 HoistedVariable hoisted_variant;
1182 [Flags]
1183 enum Flags : byte {
1184 Used = 1,
1185 ReadOnly = 2,
1186 Pinned = 4,
1187 IsThis = 8,
1188 AddressTaken = 32,
1189 CompilerGenerated = 64,
1190 IsConstant = 128
1193 public enum ReadOnlyContext: byte {
1194 Using,
1195 Foreach,
1196 Fixed
1199 Flags flags;
1200 ReadOnlyContext ro_context;
1201 LocalBuilder builder;
1203 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1205 Type = type;
1206 Name = name;
1207 Block = block;
1208 Location = l;
1211 public LocalInfo (DeclSpace ds, Block block, Location l)
1213 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1214 Block = block;
1215 Location = l;
1218 public void ResolveVariable (EmitContext ec)
1220 if (HoistedVariant != null)
1221 return;
1223 if (builder == null) {
1224 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType), Pinned);
1228 public void Emit (EmitContext ec)
1230 ec.ig.Emit (OpCodes.Ldloc, builder);
1233 public void EmitAssign (EmitContext ec)
1235 ec.ig.Emit (OpCodes.Stloc, builder);
1238 public void EmitAddressOf (EmitContext ec)
1240 ec.ig.Emit (OpCodes.Ldloca, builder);
1243 public void EmitSymbolInfo (EmitContext ec)
1245 if (builder != null)
1246 ec.DefineLocalVariable (Name, builder);
1250 // Hoisted local variable variant
1252 public HoistedVariable HoistedVariant {
1253 get {
1254 return hoisted_variant;
1256 set {
1257 hoisted_variant = value;
1261 public bool IsThisAssigned (BlockContext ec, Block block)
1263 if (VariableInfo == null)
1264 throw new Exception ();
1266 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1267 return true;
1269 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1272 public bool IsAssigned (BlockContext ec)
1274 if (VariableInfo == null)
1275 throw new Exception ();
1277 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1280 public bool Resolve (ResolveContext ec)
1282 if (VariableType != null)
1283 return true;
1285 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1286 if (texpr == null)
1287 return false;
1289 VariableType = texpr.Type;
1291 if (TypeManager.IsGenericParameter (VariableType))
1292 return true;
1294 if (VariableType.IsAbstract && VariableType.IsSealed) {
1295 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1296 return false;
1299 if (VariableType.IsPointer && !ec.IsUnsafe)
1300 Expression.UnsafeError (ec, Location);
1302 return true;
1305 public bool IsConstant {
1306 get { return (flags & Flags.IsConstant) != 0; }
1307 set { flags |= Flags.IsConstant; }
1310 public bool AddressTaken {
1311 get { return (flags & Flags.AddressTaken) != 0; }
1312 set { flags |= Flags.AddressTaken; }
1315 public bool CompilerGenerated {
1316 get { return (flags & Flags.CompilerGenerated) != 0; }
1317 set { flags |= Flags.CompilerGenerated; }
1320 public override string ToString ()
1322 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1323 Name, Type, VariableInfo, Location);
1326 public bool Used {
1327 get { return (flags & Flags.Used) != 0; }
1328 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1331 public bool ReadOnly {
1332 get { return (flags & Flags.ReadOnly) != 0; }
1335 public void SetReadOnlyContext (ReadOnlyContext context)
1337 flags |= Flags.ReadOnly;
1338 ro_context = context;
1341 public string GetReadOnlyContext ()
1343 if (!ReadOnly)
1344 throw new InternalErrorException ("Variable is not readonly");
1346 switch (ro_context) {
1347 case ReadOnlyContext.Fixed:
1348 return "fixed variable";
1349 case ReadOnlyContext.Foreach:
1350 return "foreach iteration variable";
1351 case ReadOnlyContext.Using:
1352 return "using variable";
1354 throw new NotImplementedException ();
1358 // Whether the variable is pinned, if Pinned the variable has been
1359 // allocated in a pinned slot with DeclareLocal.
1361 public bool Pinned {
1362 get { return (flags & Flags.Pinned) != 0; }
1363 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1366 public bool IsThis {
1367 get { return (flags & Flags.IsThis) != 0; }
1368 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1371 Block IKnownVariable.Block {
1372 get { return Block; }
1375 Location IKnownVariable.Location {
1376 get { return Location; }
1379 public LocalInfo Clone (CloneContext clonectx)
1382 // Variables in anonymous block are not resolved yet
1384 if (VariableType == null)
1385 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1388 // Variables in method block are resolved
1390 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1391 li.VariableType = VariableType;
1392 return li;
1396 /// <summary>
1397 /// Block represents a C# block.
1398 /// </summary>
1400 /// <remarks>
1401 /// This class is used in a number of places: either to represent
1402 /// explicit blocks that the programmer places or implicit blocks.
1404 /// Implicit blocks are used as labels or to introduce variable
1405 /// declarations.
1407 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1408 /// they contain extra information that is not necessary on normal blocks.
1409 /// </remarks>
1410 public class Block : Statement {
1411 public Block Parent;
1412 public Location StartLocation;
1413 public Location EndLocation = Location.Null;
1415 public ExplicitBlock Explicit;
1416 public ToplevelBlock Toplevel; // TODO: Use Explicit
1418 [Flags]
1419 public enum Flags
1421 Unchecked = 1,
1422 BlockUsed = 2,
1423 VariablesInitialized = 4,
1424 HasRet = 8,
1425 Unsafe = 16,
1426 IsIterator = 32,
1427 HasCapturedVariable = 64,
1428 HasCapturedThis = 1 << 7,
1429 IsExpressionTree = 1 << 8
1432 protected Flags flags;
1434 public bool Unchecked {
1435 get { return (flags & Flags.Unchecked) != 0; }
1436 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1439 public bool Unsafe {
1440 get { return (flags & Flags.Unsafe) != 0; }
1441 set { flags |= Flags.Unsafe; }
1445 // The statements in this block
1447 protected List<Statement> statements;
1450 // An array of Blocks. We keep track of children just
1451 // to generate the local variable declarations.
1453 // Statements and child statements are handled through the
1454 // statements.
1456 List<Block> children;
1459 // Labels. (label, block) pairs.
1461 protected Dictionary<string, LabeledStatement> labels;
1464 // Keeps track of (name, type) pairs
1466 Dictionary<string, LocalInfo> variables;
1469 // Keeps track of constants
1470 Dictionary<string, Expression> constants;
1473 // Temporary variables.
1475 List<LocalInfo> temporary_variables;
1478 // If this is a switch section, the enclosing switch block.
1480 Block switch_block;
1482 protected List<Statement> scope_initializers;
1484 List<ToplevelBlock> anonymous_children;
1486 protected static int id;
1488 int this_id;
1490 int assignable_slots;
1491 bool unreachable_shown;
1492 bool unreachable;
1494 public Block (Block parent)
1495 : this (parent, (Flags) 0, Location.Null, Location.Null)
1498 public Block (Block parent, Flags flags)
1499 : this (parent, flags, Location.Null, Location.Null)
1502 public Block (Block parent, Location start, Location end)
1503 : this (parent, (Flags) 0, start, end)
1507 // Useful when TopLevel block is downgraded to normal block
1509 public Block (ToplevelBlock parent, ToplevelBlock source)
1510 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1512 statements = source.statements;
1513 children = source.children;
1514 labels = source.labels;
1515 variables = source.variables;
1516 constants = source.constants;
1517 switch_block = source.switch_block;
1520 public Block (Block parent, Flags flags, Location start, Location end)
1522 if (parent != null) {
1523 parent.AddChild (this);
1525 // the appropriate constructors will fixup these fields
1526 Toplevel = parent.Toplevel;
1527 Explicit = parent.Explicit;
1530 this.Parent = parent;
1531 this.flags = flags;
1532 this.StartLocation = start;
1533 this.EndLocation = end;
1534 this.loc = start;
1535 this_id = id++;
1536 statements = new List<Statement> (4);
1539 public Block CreateSwitchBlock (Location start)
1541 // FIXME: should this be implicit?
1542 Block new_block = new ExplicitBlock (this, start, start);
1543 new_block.switch_block = this;
1544 return new_block;
1547 public int ID {
1548 get { return this_id; }
1551 public IDictionary<string, LocalInfo> Variables {
1552 get {
1553 if (variables == null)
1554 variables = new Dictionary<string, LocalInfo> ();
1555 return variables;
1559 void AddChild (Block b)
1561 if (children == null)
1562 children = new List<Block> (1);
1564 children.Add (b);
1567 public void SetEndLocation (Location loc)
1569 EndLocation = loc;
1572 protected void Error_158 (string name, Location loc)
1574 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1575 "by the same name in a contained scope", name);
1578 /// <summary>
1579 /// Adds a label to the current block.
1580 /// </summary>
1582 /// <returns>
1583 /// false if the name already exists in this block. true
1584 /// otherwise.
1585 /// </returns>
1587 public bool AddLabel (LabeledStatement target)
1589 if (switch_block != null)
1590 return switch_block.AddLabel (target);
1592 string name = target.Name;
1594 Block cur = this;
1595 while (cur != null) {
1596 LabeledStatement s = cur.DoLookupLabel (name);
1597 if (s != null) {
1598 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1599 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1600 return false;
1603 if (this == Explicit)
1604 break;
1606 cur = cur.Parent;
1609 while (cur != null) {
1610 if (cur.DoLookupLabel (name) != null) {
1611 Error_158 (name, target.loc);
1612 return false;
1615 if (children != null) {
1616 foreach (Block b in children) {
1617 LabeledStatement s = b.DoLookupLabel (name);
1618 if (s == null)
1619 continue;
1621 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1622 Error_158 (name, target.loc);
1623 return false;
1627 cur = cur.Parent;
1630 Toplevel.CheckError158 (name, target.loc);
1632 if (labels == null)
1633 labels = new Dictionary<string, LabeledStatement> ();
1635 labels.Add (name, target);
1636 return true;
1639 public LabeledStatement LookupLabel (string name)
1641 LabeledStatement s = DoLookupLabel (name);
1642 if (s != null)
1643 return s;
1645 if (children == null)
1646 return null;
1648 foreach (Block child in children) {
1649 if (Explicit != child.Explicit)
1650 continue;
1652 s = child.LookupLabel (name);
1653 if (s != null)
1654 return s;
1657 return null;
1660 LabeledStatement DoLookupLabel (string name)
1662 if (switch_block != null)
1663 return switch_block.LookupLabel (name);
1665 if (labels != null)
1666 if (labels.ContainsKey (name))
1667 return labels [name];
1669 return null;
1672 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1674 Block b = this;
1675 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1676 while (kvi == null) {
1677 b = b.Explicit.Parent;
1678 if (b == null)
1679 return true;
1680 kvi = b.Explicit.GetKnownVariable (name);
1683 if (kvi.Block == b)
1684 return true;
1686 // Is kvi.Block nested inside 'b'
1687 if (b.Explicit != kvi.Block.Explicit) {
1689 // If a variable by the same name it defined in a nested block of this
1690 // block, we violate the invariant meaning in a block.
1692 if (b == this) {
1693 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1694 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1695 return false;
1699 // It's ok if the definition is in a nested subblock of b, but not
1700 // nested inside this block -- a definition in a sibling block
1701 // should not affect us.
1703 return true;
1707 // Block 'b' and kvi.Block are the same textual block.
1708 // However, different variables are extant.
1710 // Check if the variable is in scope in both blocks. We use
1711 // an indirect check that depends on AddVariable doing its
1712 // part in maintaining the invariant-meaning-in-block property.
1714 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1715 return true;
1717 if (this is ToplevelBlock) {
1718 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1719 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1720 return false;
1724 // Even though we detected the error when the name is used, we
1725 // treat it as if the variable declaration was in error.
1727 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1728 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1729 return false;
1732 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1734 LocalInfo vi = GetLocalInfo (name);
1735 if (vi != null) {
1736 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1737 if (Explicit == vi.Block.Explicit) {
1738 Error_AlreadyDeclared (l, name, null);
1739 } else {
1740 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1741 "parent or current" : "parent");
1743 return false;
1746 if (block != null) {
1747 Expression e = block.GetParameterReference (name, Location.Null);
1748 if (e != null) {
1749 ParameterReference pr = e as ParameterReference;
1750 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1751 Error_AlreadyDeclared (loc, name);
1752 else
1753 Error_AlreadyDeclared (loc, name, "parent or current");
1754 return false;
1758 return true;
1761 public LocalInfo AddVariable (Expression type, string name, Location l)
1763 if (!CheckParentConflictName (Toplevel, name, l))
1764 return null;
1766 if (Toplevel.GenericMethod != null) {
1767 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1768 if (tp.Name == name) {
1769 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1770 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1771 return null;
1776 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1777 if (kvi != null) {
1778 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1779 Error_AlreadyDeclared (l, name, "child");
1780 return null;
1783 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1784 AddVariable (vi);
1786 if ((flags & Flags.VariablesInitialized) != 0)
1787 throw new InternalErrorException ("block has already been resolved");
1789 return vi;
1792 protected virtual void AddVariable (LocalInfo li)
1794 Variables.Add (li.Name, li);
1795 Explicit.AddKnownVariable (li.Name, li);
1798 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1800 if (reason == null) {
1801 Error_AlreadyDeclared (loc, var);
1802 return;
1805 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1806 "in this scope because it would give a different meaning " +
1807 "to `{0}', which is already used in a `{1}' scope " +
1808 "to denote something else", var, reason);
1811 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1813 Toplevel.Report.Error (128, loc,
1814 "A local variable named `{0}' is already defined in this scope", name);
1817 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1819 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1820 name, conflict);
1823 public bool AddConstant (Expression type, string name, Expression value, Location l)
1825 if (AddVariable (type, name, l) == null)
1826 return false;
1828 if (constants == null)
1829 constants = new Dictionary<string, Expression> ();
1831 constants.Add (name, value);
1833 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1834 Use ();
1835 return true;
1838 static int next_temp_id = 0;
1840 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1842 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1844 if (temporary_variables == null)
1845 temporary_variables = new List<LocalInfo> ();
1847 int id = ++next_temp_id;
1848 string name = "$s_" + id.ToString ();
1850 LocalInfo li = new LocalInfo (te, name, this, loc);
1851 li.CompilerGenerated = true;
1852 temporary_variables.Add (li);
1853 return li;
1856 public LocalInfo GetLocalInfo (string name)
1858 LocalInfo ret;
1859 for (Block b = this; b != null; b = b.Parent) {
1860 if (b.variables != null) {
1861 if (b.variables.TryGetValue (name, out ret))
1862 return ret;
1866 return null;
1869 public Expression GetVariableType (string name)
1871 LocalInfo vi = GetLocalInfo (name);
1872 return vi == null ? null : vi.Type;
1875 public Expression GetConstantExpression (string name)
1877 Expression ret;
1878 for (Block b = this; b != null; b = b.Parent) {
1879 if (b.constants != null) {
1880 if (b.constants.TryGetValue (name, out ret))
1881 return ret;
1884 return null;
1888 // It should be used by expressions which require to
1889 // register a statement during resolve process.
1891 public void AddScopeStatement (Statement s)
1893 if (scope_initializers == null)
1894 scope_initializers = new List<Statement> ();
1896 scope_initializers.Add (s);
1899 public void AddStatement (Statement s)
1901 statements.Add (s);
1902 flags |= Flags.BlockUsed;
1905 public bool Used {
1906 get { return (flags & Flags.BlockUsed) != 0; }
1909 public void Use ()
1911 flags |= Flags.BlockUsed;
1914 public bool HasRet {
1915 get { return (flags & Flags.HasRet) != 0; }
1918 public int AssignableSlots {
1919 get {
1920 // TODO: Re-enable
1921 // if ((flags & Flags.VariablesInitialized) == 0)
1922 // throw new Exception ("Variables have not been initialized yet");
1923 return assignable_slots;
1927 public IList<ToplevelBlock> AnonymousChildren {
1928 get { return anonymous_children; }
1931 public void AddAnonymousChild (ToplevelBlock b)
1933 if (anonymous_children == null)
1934 anonymous_children = new List<ToplevelBlock> ();
1936 anonymous_children.Add (b);
1939 void DoResolveConstants (BlockContext ec)
1941 if (constants == null)
1942 return;
1944 if (variables == null)
1945 throw new InternalErrorException ("cannot happen");
1947 foreach (var de in variables) {
1948 string name = de.Key;
1949 LocalInfo vi = de.Value;
1950 Type variable_type = vi.VariableType;
1952 if (variable_type == null) {
1953 if (vi.Type is VarExpr)
1954 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1956 continue;
1959 Expression cv;
1960 if (!constants.TryGetValue (name, out cv))
1961 continue;
1963 // Don't let 'const int Foo = Foo;' succeed.
1964 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1965 // which in turn causes the 'must be constant' error to be triggered.
1966 constants.Remove (name);
1968 if (!Const.IsConstantTypeValid (variable_type)) {
1969 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1970 continue;
1973 ec.CurrentBlock = this;
1974 Expression e;
1975 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1976 e = cv.Resolve (ec);
1978 if (e == null)
1979 continue;
1981 Constant ce = e as Constant;
1982 if (ce == null) {
1983 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1984 continue;
1987 e = ce.ConvertImplicitly (ec, variable_type);
1988 if (e == null) {
1989 if (TypeManager.IsReferenceType (variable_type))
1990 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1991 else
1992 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1993 continue;
1996 constants.Add (name, e);
1997 vi.IsConstant = true;
2001 protected void ResolveMeta (BlockContext ec, int offset)
2003 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2005 // If some parent block was unsafe, we remain unsafe even if this block
2006 // isn't explicitly marked as such.
2007 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2008 flags |= Flags.VariablesInitialized;
2010 if (variables != null) {
2011 foreach (LocalInfo li in variables.Values) {
2012 if (!li.Resolve (ec))
2013 continue;
2014 li.VariableInfo = new VariableInfo (li, offset);
2015 offset += li.VariableInfo.Length;
2018 assignable_slots = offset;
2020 DoResolveConstants (ec);
2022 if (children == null)
2023 return;
2024 foreach (Block b in children)
2025 b.ResolveMeta (ec, offset);
2030 // Emits the local variable declarations for a block
2032 public virtual void EmitMeta (EmitContext ec)
2034 if (variables != null){
2035 foreach (LocalInfo vi in variables.Values)
2036 vi.ResolveVariable (ec);
2039 if (temporary_variables != null) {
2040 for (int i = 0; i < temporary_variables.Count; i++)
2041 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2044 if (children != null) {
2045 for (int i = 0; i < children.Count; i++)
2046 ((Block)children[i]).EmitMeta(ec);
2050 void UsageWarning (BlockContext ec)
2052 if (variables == null || ec.Report.WarningLevel < 3)
2053 return;
2055 foreach (var de in variables) {
2056 LocalInfo vi = de.Value;
2058 if (!vi.Used) {
2059 string name = de.Key;
2061 // vi.VariableInfo can be null for 'catch' variables
2062 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2063 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2064 else
2065 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2070 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2072 Statement body;
2074 // Some statements are wrapped by a Block. Since
2075 // others' internal could be changed, here I treat
2076 // them as possibly wrapped by Block equally.
2077 Block b = s as Block;
2078 if (b != null && b.statements.Count == 1)
2079 s = (Statement) b.statements [0];
2081 if (s is Lock)
2082 body = ((Lock) s).Statement;
2083 else if (s is For)
2084 body = ((For) s).Statement;
2085 else if (s is Foreach)
2086 body = ((Foreach) s).Statement;
2087 else if (s is While)
2088 body = ((While) s).Statement;
2089 else if (s is Fixed)
2090 body = ((Fixed) s).Statement;
2091 else if (s is Using)
2092 body = ((Using) s).EmbeddedStatement;
2093 else if (s is UsingTemporary)
2094 body = ((UsingTemporary) s).Statement;
2095 else
2096 return;
2098 if (body == null || body is EmptyStatement)
2099 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2102 public override bool Resolve (BlockContext ec)
2104 Block prev_block = ec.CurrentBlock;
2105 bool ok = true;
2107 int errors = ec.Report.Errors;
2109 ec.CurrentBlock = this;
2110 ec.StartFlowBranching (this);
2112 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2115 // Compiler generated scope statements
2117 if (scope_initializers != null) {
2118 foreach (Statement s in scope_initializers)
2119 s.Resolve (ec);
2123 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2124 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2125 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2126 // responsible for handling the situation.
2128 int statement_count = statements.Count;
2129 for (int ix = 0; ix < statement_count; ix++){
2130 Statement s = statements [ix];
2131 // Check possible empty statement (CS0642)
2132 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2133 statements [ix + 1] is ExplicitBlock)
2134 CheckPossibleMistakenEmptyStatement (ec, s);
2137 // Warn if we detect unreachable code.
2139 if (unreachable) {
2140 if (s is EmptyStatement)
2141 continue;
2143 if (!unreachable_shown && !(s is LabeledStatement)) {
2144 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2145 unreachable_shown = true;
2148 Block c_block = s as Block;
2149 if (c_block != null)
2150 c_block.unreachable = c_block.unreachable_shown = true;
2154 // Note that we're not using ResolveUnreachable() for unreachable
2155 // statements here. ResolveUnreachable() creates a temporary
2156 // flow branching and kills it afterwards. This leads to problems
2157 // if you have two unreachable statements where the first one
2158 // assigns a variable and the second one tries to access it.
2161 if (!s.Resolve (ec)) {
2162 ok = false;
2163 if (ec.IsInProbingMode)
2164 break;
2166 statements [ix] = new EmptyStatement (s.loc);
2167 continue;
2170 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2171 statements [ix] = new EmptyStatement (s.loc);
2173 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2174 if (unreachable && s is LabeledStatement)
2175 throw new InternalErrorException ("should not happen");
2178 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2179 ec.CurrentBranching, statement_count);
2181 while (ec.CurrentBranching is FlowBranchingLabeled)
2182 ec.EndFlowBranching ();
2184 bool flow_unreachable = ec.EndFlowBranching ();
2186 ec.CurrentBlock = prev_block;
2188 if (flow_unreachable)
2189 flags |= Flags.HasRet;
2191 // If we're a non-static `struct' constructor which doesn't have an
2192 // initializer, then we must initialize all of the struct's fields.
2193 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2194 ok = false;
2196 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2197 foreach (LabeledStatement label in labels.Values)
2198 if (!label.HasBeenReferenced)
2199 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2202 if (ok && errors == ec.Report.Errors)
2203 UsageWarning (ec);
2205 return ok;
2208 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2210 unreachable_shown = true;
2211 unreachable = true;
2213 if (warn)
2214 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2216 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2217 bool ok = Resolve (ec);
2218 ec.KillFlowBranching ();
2220 return ok;
2223 protected override void DoEmit (EmitContext ec)
2225 for (int ix = 0; ix < statements.Count; ix++){
2226 Statement s = (Statement) statements [ix];
2227 s.Emit (ec);
2231 public override void Emit (EmitContext ec)
2233 if (scope_initializers != null)
2234 EmitScopeInitializers (ec);
2236 ec.Mark (StartLocation);
2237 DoEmit (ec);
2239 if (SymbolWriter.HasSymbolWriter)
2240 EmitSymbolInfo (ec);
2243 protected void EmitScopeInitializers (EmitContext ec)
2245 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2247 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2248 foreach (Statement s in scope_initializers)
2249 s.Emit (ec);
2252 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2255 protected virtual void EmitSymbolInfo (EmitContext ec)
2257 if (variables != null) {
2258 foreach (LocalInfo vi in variables.Values) {
2259 vi.EmitSymbolInfo (ec);
2264 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2266 MutateVariables (storey);
2268 if (scope_initializers != null) {
2269 foreach (Statement s in scope_initializers)
2270 s.MutateHoistedGenericType (storey);
2273 foreach (Statement s in statements)
2274 s.MutateHoistedGenericType (storey);
2277 void MutateVariables (AnonymousMethodStorey storey)
2279 if (variables != null) {
2280 foreach (LocalInfo vi in variables.Values) {
2281 vi.VariableType = storey.MutateType (vi.VariableType);
2285 if (temporary_variables != null) {
2286 foreach (LocalInfo vi in temporary_variables)
2287 vi.VariableType = storey.MutateType (vi.VariableType);
2291 public override string ToString ()
2293 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2296 protected override void CloneTo (CloneContext clonectx, Statement t)
2298 Block target = (Block) t;
2300 clonectx.AddBlockMap (this, target);
2302 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2303 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2304 if (Parent != null)
2305 target.Parent = clonectx.RemapBlockCopy (Parent);
2307 if (variables != null){
2308 target.variables = new Dictionary<string, LocalInfo> ();
2310 foreach (var de in variables){
2311 LocalInfo newlocal = de.Value.Clone (clonectx);
2312 target.variables [de.Key] = newlocal;
2313 clonectx.AddVariableMap (de.Value, newlocal);
2317 target.statements = new List<Statement> (statements.Count);
2318 foreach (Statement s in statements)
2319 target.statements.Add (s.Clone (clonectx));
2321 if (target.children != null){
2322 target.children = new List<Block> (children.Count);
2323 foreach (Block b in children){
2324 target.children.Add (clonectx.LookupBlock (b));
2329 // TODO: labels, switch_block, constants (?), anonymous_children
2334 public class ExplicitBlock : Block
2336 Dictionary<string, IKnownVariable> known_variables;
2337 protected AnonymousMethodStorey am_storey;
2339 public ExplicitBlock (Block parent, Location start, Location end)
2340 : this (parent, (Flags) 0, start, end)
2344 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2345 : base (parent, flags, start, end)
2347 this.Explicit = this;
2350 // <summary>
2351 // Marks a variable with name @name as being used in this or a child block.
2352 // If a variable name has been used in a child block, it's illegal to
2353 // declare a variable with the same name in the current block.
2354 // </summary>
2355 internal void AddKnownVariable (string name, IKnownVariable info)
2357 if (known_variables == null)
2358 known_variables = new Dictionary<string, IKnownVariable> ();
2360 known_variables [name] = info;
2362 if (Parent != null)
2363 Parent.Explicit.AddKnownVariable (name, info);
2366 public AnonymousMethodStorey AnonymousMethodStorey {
2367 get { return am_storey; }
2371 // Creates anonymous method storey in current block
2373 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2376 // When referencing a variable in iterator storey from children anonymous method
2378 if (Toplevel.am_storey is IteratorStorey) {
2379 return Toplevel.am_storey;
2383 // An iterator has only 1 storey block
2385 if (ec.CurrentIterator != null)
2386 return ec.CurrentIterator.Storey;
2388 if (am_storey == null) {
2389 MemberBase mc = ec.MemberContext as MemberBase;
2390 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2393 // Creates anonymous method storey for this block
2395 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2398 return am_storey;
2401 public override void Emit (EmitContext ec)
2403 if (am_storey != null)
2404 am_storey.EmitStoreyInstantiation (ec);
2406 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2407 if (emit_debug_info)
2408 ec.BeginScope ();
2410 base.Emit (ec);
2412 if (emit_debug_info)
2413 ec.EndScope ();
2416 public override void EmitMeta (EmitContext ec)
2419 // Creates anonymous method storey
2421 if (am_storey != null) {
2422 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2424 // Creates parent storey reference when hoisted this is accessible
2426 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2427 ExplicitBlock parent = Toplevel.Parent.Explicit;
2430 // Hoisted this exists in top-level parent storey only
2432 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2433 parent = parent.Parent.Explicit;
2435 am_storey.AddParentStoreyReference (parent.am_storey);
2438 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2441 am_storey.DefineType ();
2442 am_storey.ResolveType ();
2443 am_storey.Define ();
2444 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2446 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2447 if (ref_blocks != null) {
2448 foreach (ExplicitBlock ref_block in ref_blocks) {
2449 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2450 if (b.am_storey != null) {
2451 b.am_storey.AddParentStoreyReference (am_storey);
2453 // Stop propagation inside same top block
2454 if (b.Toplevel == Toplevel)
2455 break;
2457 b = b.Toplevel;
2459 b.HasCapturedVariable = true;
2465 base.EmitMeta (ec);
2468 internal IKnownVariable GetKnownVariable (string name)
2470 if (known_variables == null)
2471 return null;
2473 IKnownVariable kw;
2474 if (!known_variables.TryGetValue (name, out kw))
2475 return null;
2477 return kw;
2480 public bool HasCapturedThis
2482 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2483 get { return (flags & Flags.HasCapturedThis) != 0; }
2486 public bool HasCapturedVariable
2488 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2489 get { return (flags & Flags.HasCapturedVariable) != 0; }
2492 protected override void CloneTo (CloneContext clonectx, Statement t)
2494 ExplicitBlock target = (ExplicitBlock) t;
2495 target.known_variables = null;
2496 base.CloneTo (clonectx, t);
2500 public class ToplevelParameterInfo : IKnownVariable {
2501 public readonly ToplevelBlock Block;
2502 public readonly int Index;
2503 public VariableInfo VariableInfo;
2505 Block IKnownVariable.Block {
2506 get { return Block; }
2508 public Parameter Parameter {
2509 get { return Block.Parameters [Index]; }
2512 public Type ParameterType {
2513 get { return Block.Parameters.Types [Index]; }
2516 public Location Location {
2517 get { return Parameter.Location; }
2520 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2522 this.Block = block;
2523 this.Index = idx;
2528 // A toplevel block contains extra information, the split is done
2529 // only to separate information that would otherwise bloat the more
2530 // lightweight Block.
2532 // In particular, this was introduced when the support for Anonymous
2533 // Methods was implemented.
2535 public class ToplevelBlock : ExplicitBlock
2538 // Block is converted to an expression
2540 sealed class BlockScopeExpression : Expression
2542 Expression child;
2543 readonly ToplevelBlock block;
2545 public BlockScopeExpression (Expression child, ToplevelBlock block)
2547 this.child = child;
2548 this.block = block;
2551 public override Expression CreateExpressionTree (ResolveContext ec)
2553 throw new NotSupportedException ();
2556 protected override Expression DoResolve (ResolveContext ec)
2558 if (child == null)
2559 return null;
2561 child = child.Resolve (ec);
2562 if (child == null)
2563 return null;
2565 eclass = child.eclass;
2566 type = child.Type;
2567 return this;
2570 public override void Emit (EmitContext ec)
2572 block.EmitMeta (ec);
2573 block.EmitScopeInitializers (ec);
2574 child.Emit (ec);
2577 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2579 type = storey.MutateType (type);
2580 child.MutateHoistedGenericType (storey);
2581 block.MutateHoistedGenericType (storey);
2585 GenericMethod generic;
2586 protected ParametersCompiled parameters;
2587 ToplevelParameterInfo[] parameter_info;
2588 LocalInfo this_variable;
2589 bool resolved;
2590 bool unreachable;
2591 CompilerContext compiler;
2593 public HoistedVariable HoistedThisVariable;
2595 public bool Resolved {
2596 get {
2597 return resolved;
2602 // The parameters for the block.
2604 public ParametersCompiled Parameters {
2605 get { return parameters; }
2608 public Report Report {
2609 get { return compiler.Report; }
2612 public GenericMethod GenericMethod {
2613 get { return generic; }
2616 public ToplevelBlock Container {
2617 get { return Parent == null ? null : Parent.Toplevel; }
2620 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2621 this (ctx, parent, (Flags) 0, parameters, start)
2625 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2626 this (ctx, parent, parameters, start)
2628 this.generic = generic;
2631 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2632 this (ctx, null, (Flags) 0, parameters, start)
2636 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2637 this (ctx, null, flags, parameters, start)
2641 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2642 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2643 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2644 base (null, flags, start, Location.Null)
2646 this.compiler = ctx;
2647 this.Toplevel = this;
2649 this.parameters = parameters;
2650 this.Parent = parent;
2651 if (parent != null)
2652 parent.AddAnonymousChild (this);
2654 if (!this.parameters.IsEmpty)
2655 ProcessParameters ();
2658 public ToplevelBlock (CompilerContext ctx, Location loc)
2659 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2663 protected override void CloneTo (CloneContext clonectx, Statement t)
2665 ToplevelBlock target = (ToplevelBlock) t;
2666 base.CloneTo (clonectx, t);
2668 if (parameters.Count != 0)
2669 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2670 for (int i = 0; i < parameters.Count; ++i)
2671 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2674 public bool CheckError158 (string name, Location loc)
2676 if (AnonymousChildren != null) {
2677 foreach (ToplevelBlock child in AnonymousChildren) {
2678 if (!child.CheckError158 (name, loc))
2679 return false;
2683 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2684 if (!c.DoCheckError158 (name, loc))
2685 return false;
2688 return true;
2691 void ProcessParameters ()
2693 int n = parameters.Count;
2694 parameter_info = new ToplevelParameterInfo [n];
2695 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2696 for (int i = 0; i < n; ++i) {
2697 parameter_info [i] = new ToplevelParameterInfo (this, i);
2699 Parameter p = parameters [i];
2700 if (p == null)
2701 continue;
2703 string name = p.Name;
2704 if (CheckParentConflictName (top_parent, name, loc))
2705 AddKnownVariable (name, parameter_info [i]);
2708 // mark this block as "used" so that we create local declarations in a sub-block
2709 // FIXME: This appears to uncover a lot of bugs
2710 //this.Use ();
2713 bool DoCheckError158 (string name, Location loc)
2715 LabeledStatement s = LookupLabel (name);
2716 if (s != null) {
2717 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2718 Error_158 (name, loc);
2719 return false;
2722 return true;
2725 public override Expression CreateExpressionTree (ResolveContext ec)
2727 if (statements.Count == 1) {
2728 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2729 if (scope_initializers != null)
2730 expr = new BlockScopeExpression (expr, this);
2732 return expr;
2735 return base.CreateExpressionTree (ec);
2739 // Reformats this block to be top-level iterator block
2741 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2743 IsIterator = true;
2745 // Creates block with original statements
2746 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2748 source.statements = new List<Statement> (1);
2749 source.AddStatement (new Return (iterator, iterator.Location));
2750 source.IsIterator = false;
2752 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2753 source.am_storey = iterator_storey;
2754 return iterator_storey;
2758 // Returns a parameter reference expression for the given name,
2759 // or null if there is no such parameter
2761 public Expression GetParameterReference (string name, Location loc)
2763 for (ToplevelBlock t = this; t != null; t = t.Container) {
2764 Expression expr = t.GetParameterReferenceExpression (name, loc);
2765 if (expr != null)
2766 return expr;
2769 return null;
2772 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2774 int idx = parameters.GetParameterIndexByName (name);
2775 return idx < 0 ?
2776 null : new ParameterReference (parameter_info [idx], loc);
2779 // <summary>
2780 // Returns the "this" instance variable of this block.
2781 // See AddThisVariable() for more information.
2782 // </summary>
2783 public LocalInfo ThisVariable {
2784 get { return this_variable; }
2787 // <summary>
2788 // This is used by non-static `struct' constructors which do not have an
2789 // initializer - in this case, the constructor must initialize all of the
2790 // struct's fields. To do this, we add a "this" variable and use the flow
2791 // analysis code to ensure that it's been fully initialized before control
2792 // leaves the constructor.
2793 // </summary>
2794 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2796 if (this_variable == null) {
2797 this_variable = new LocalInfo (ds, this, l);
2798 this_variable.Used = true;
2799 this_variable.IsThis = true;
2801 Variables.Add ("this", this_variable);
2804 return this_variable;
2807 public bool IsIterator {
2808 get { return (flags & Flags.IsIterator) != 0; }
2809 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2813 // Block has been converted to expression tree
2815 public bool IsExpressionTree {
2816 get { return (flags & Flags.IsExpressionTree) != 0; }
2819 public bool IsThisAssigned (BlockContext ec)
2821 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2824 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2826 if (resolved)
2827 return true;
2829 resolved = true;
2831 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2832 flags |= Flags.IsExpressionTree;
2834 try {
2835 if (!ResolveMeta (rc, ip))
2836 return false;
2838 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2839 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2841 if (!Resolve (rc))
2842 return false;
2844 unreachable = top_level.End ();
2846 } catch (Exception) {
2847 #if PRODUCTION
2848 if (rc.CurrentBlock != null) {
2849 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2850 } else {
2851 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2853 #endif
2854 throw;
2857 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2858 if (rc.CurrentAnonymousMethod == null) {
2859 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2860 return false;
2861 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2862 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2863 rc.CurrentAnonymousMethod.GetSignatureForError ());
2864 return false;
2868 return true;
2871 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2873 int errors = ec.Report.Errors;
2874 int orig_count = parameters.Count;
2876 if (ip != null)
2877 parameters = ip;
2879 // Assert: orig_count != parameter.Count => orig_count == 0
2880 if (orig_count != 0 && orig_count != parameters.Count)
2881 throw new InternalErrorException ("parameter information mismatch");
2883 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2885 for (int i = 0; i < orig_count; ++i) {
2886 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2888 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2889 continue;
2891 VariableInfo vi = new VariableInfo (ip, i, offset);
2892 parameter_info [i].VariableInfo = vi;
2893 offset += vi.Length;
2896 ResolveMeta (ec, offset);
2898 return ec.Report.Errors == errors;
2901 // <summary>
2902 // Check whether all `out' parameters have been assigned.
2903 // </summary>
2904 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2906 if (vector.IsUnreachable)
2907 return;
2909 int n = parameter_info == null ? 0 : parameter_info.Length;
2911 for (int i = 0; i < n; i++) {
2912 VariableInfo var = parameter_info [i].VariableInfo;
2914 if (var == null)
2915 continue;
2917 if (vector.IsAssigned (var, false))
2918 continue;
2920 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2921 var.Name);
2925 public override void Emit (EmitContext ec)
2927 if (Report.Errors > 0)
2928 return;
2930 #if PRODUCTION
2931 try {
2932 #endif
2933 EmitMeta (ec);
2935 if (ec.HasReturnLabel)
2936 ec.ReturnLabel = ec.ig.DefineLabel ();
2938 base.Emit (ec);
2940 ec.Mark (EndLocation);
2942 if (ec.HasReturnLabel)
2943 ec.ig.MarkLabel (ec.ReturnLabel);
2945 if (ec.return_value != null) {
2946 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2947 ec.ig.Emit (OpCodes.Ret);
2948 } else {
2950 // If `HasReturnLabel' is set, then we already emitted a
2951 // jump to the end of the method, so we must emit a `ret'
2952 // there.
2954 // Unfortunately, System.Reflection.Emit automatically emits
2955 // a leave to the end of a finally block. This is a problem
2956 // if no code is following the try/finally block since we may
2957 // jump to a point after the end of the method.
2958 // As a workaround, we're always creating a return label in
2959 // this case.
2962 if (ec.HasReturnLabel || !unreachable) {
2963 if (ec.ReturnType != TypeManager.void_type)
2964 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2965 ec.ig.Emit (OpCodes.Ret);
2969 #if PRODUCTION
2970 } catch (Exception e){
2971 Console.WriteLine ("Exception caught by the compiler while emitting:");
2972 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2974 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2975 throw;
2977 #endif
2980 public override void EmitMeta (EmitContext ec)
2982 parameters.ResolveVariable ();
2984 // Avoid declaring an IL variable for this_variable since it is not accessed
2985 // from the generated IL
2986 if (this_variable != null)
2987 Variables.Remove ("this");
2988 base.EmitMeta (ec);
2991 protected override void EmitSymbolInfo (EmitContext ec)
2993 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2994 if ((ae != null) && (ae.Storey != null))
2995 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2997 base.EmitSymbolInfo (ec);
3001 public class SwitchLabel {
3002 Expression label;
3003 object converted;
3004 Location loc;
3006 Label il_label;
3007 bool il_label_set;
3008 Label il_label_code;
3009 bool il_label_code_set;
3011 public static readonly object NullStringCase = new object ();
3014 // if expr == null, then it is the default case.
3016 public SwitchLabel (Expression expr, Location l)
3018 label = expr;
3019 loc = l;
3022 public Expression Label {
3023 get {
3024 return label;
3028 public Location Location {
3029 get { return loc; }
3032 public object Converted {
3033 get {
3034 return converted;
3038 public Label GetILLabel (EmitContext ec)
3040 if (!il_label_set){
3041 il_label = ec.ig.DefineLabel ();
3042 il_label_set = true;
3044 return il_label;
3047 public Label GetILLabelCode (EmitContext ec)
3049 if (!il_label_code_set){
3050 il_label_code = ec.ig.DefineLabel ();
3051 il_label_code_set = true;
3053 return il_label_code;
3057 // Resolves the expression, reduces it to a literal if possible
3058 // and then converts it to the requested type.
3060 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3062 Expression e = label.Resolve (ec);
3064 if (e == null)
3065 return false;
3067 Constant c = e as Constant;
3068 if (c == null){
3069 ec.Report.Error (150, loc, "A constant value is expected");
3070 return false;
3073 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3074 converted = NullStringCase;
3075 return true;
3078 if (allow_nullable && c.GetValue () == null) {
3079 converted = NullStringCase;
3080 return true;
3083 c = c.ImplicitConversionRequired (ec, required_type, loc);
3084 if (c == null)
3085 return false;
3087 converted = c.GetValue ();
3088 return true;
3091 public void Error_AlreadyOccurs (ResolveContext ec, Type switch_type, SwitchLabel collision_with)
3093 string label;
3094 if (converted == null)
3095 label = "default";
3096 else if (converted == NullStringCase)
3097 label = "null";
3098 else
3099 label = converted.ToString ();
3101 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3102 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3105 public SwitchLabel Clone (CloneContext clonectx)
3107 return new SwitchLabel (label.Clone (clonectx), loc);
3111 public class SwitchSection {
3112 // An array of SwitchLabels.
3113 public readonly List<SwitchLabel> Labels;
3114 public readonly Block Block;
3116 public SwitchSection (List<SwitchLabel> labels, Block block)
3118 Labels = labels;
3119 Block = block;
3122 public SwitchSection Clone (CloneContext clonectx)
3124 var cloned_labels = new List<SwitchLabel> ();
3126 foreach (SwitchLabel sl in cloned_labels)
3127 cloned_labels.Add (sl.Clone (clonectx));
3129 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3133 public class Switch : Statement {
3134 public List<SwitchSection> Sections;
3135 public Expression Expr;
3137 /// <summary>
3138 /// Maps constants whose type type SwitchType to their SwitchLabels.
3139 /// </summary>
3140 public IDictionary<object, SwitchLabel> Elements;
3142 /// <summary>
3143 /// The governing switch type
3144 /// </summary>
3145 public Type SwitchType;
3148 // Computed
3150 Label default_target;
3151 Label null_target;
3152 Expression new_expr;
3153 bool is_constant;
3154 bool has_null_case;
3155 SwitchSection constant_section;
3156 SwitchSection default_section;
3158 ExpressionStatement string_dictionary;
3159 FieldExpr switch_cache_field;
3160 static int unique_counter;
3163 // Nullable Types support
3165 Nullable.Unwrap unwrap;
3167 protected bool HaveUnwrap {
3168 get { return unwrap != null; }
3172 // The types allowed to be implicitly cast from
3173 // on the governing type
3175 static Type [] allowed_types;
3177 public Switch (Expression e, List<SwitchSection> sects, Location l)
3179 Expr = e;
3180 Sections = sects;
3181 loc = l;
3184 public bool GotDefault {
3185 get {
3186 return default_section != null;
3190 public Label DefaultTarget {
3191 get {
3192 return default_target;
3197 // Determines the governing type for a switch. The returned
3198 // expression might be the expression from the switch, or an
3199 // expression that includes any potential conversions to the
3200 // integral types or to string.
3202 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3204 Type t = expr.Type;
3206 if (t == TypeManager.byte_type ||
3207 t == TypeManager.sbyte_type ||
3208 t == TypeManager.ushort_type ||
3209 t == TypeManager.short_type ||
3210 t == TypeManager.uint32_type ||
3211 t == TypeManager.int32_type ||
3212 t == TypeManager.uint64_type ||
3213 t == TypeManager.int64_type ||
3214 t == TypeManager.char_type ||
3215 t == TypeManager.string_type ||
3216 t == TypeManager.bool_type ||
3217 TypeManager.IsEnumType (t))
3218 return expr;
3220 if (allowed_types == null){
3221 allowed_types = new Type [] {
3222 TypeManager.sbyte_type,
3223 TypeManager.byte_type,
3224 TypeManager.short_type,
3225 TypeManager.ushort_type,
3226 TypeManager.int32_type,
3227 TypeManager.uint32_type,
3228 TypeManager.int64_type,
3229 TypeManager.uint64_type,
3230 TypeManager.char_type,
3231 TypeManager.string_type
3236 // Try to find a *user* defined implicit conversion.
3238 // If there is no implicit conversion, or if there are multiple
3239 // conversions, we have to report an error
3241 Expression converted = null;
3242 foreach (Type tt in allowed_types){
3243 Expression e;
3245 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3246 if (e == null)
3247 continue;
3250 // Ignore over-worked ImplicitUserConversions that do
3251 // an implicit conversion in addition to the user conversion.
3253 if (!(e is UserCast))
3254 continue;
3256 if (converted != null){
3257 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3258 return null;
3261 converted = e;
3263 return converted;
3267 // Performs the basic sanity checks on the switch statement
3268 // (looks for duplicate keys and non-constant expressions).
3270 // It also returns a hashtable with the keys that we will later
3271 // use to compute the switch tables
3273 bool CheckSwitch (ResolveContext ec)
3275 bool error = false;
3276 Elements = new Dictionary<object, SwitchLabel> ();
3278 foreach (SwitchSection ss in Sections){
3279 foreach (SwitchLabel sl in ss.Labels){
3280 if (sl.Label == null){
3281 if (default_section != null){
3282 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3283 error = true;
3285 default_section = ss;
3286 continue;
3289 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3290 error = true;
3291 continue;
3294 object key = sl.Converted;
3295 if (key == SwitchLabel.NullStringCase)
3296 has_null_case = true;
3298 try {
3299 Elements.Add (key, sl);
3300 } catch (ArgumentException) {
3301 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3302 error = true;
3306 return !error;
3309 void EmitObjectInteger (ILGenerator ig, object k)
3311 if (k is int)
3312 IntConstant.EmitInt (ig, (int) k);
3313 else if (k is Constant) {
3314 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3316 else if (k is uint)
3317 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3318 else if (k is long)
3320 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3322 IntConstant.EmitInt (ig, (int) (long) k);
3323 ig.Emit (OpCodes.Conv_I8);
3325 else
3326 LongConstant.EmitLong (ig, (long) k);
3328 else if (k is ulong)
3330 ulong ul = (ulong) k;
3331 if (ul < (1L<<32))
3333 IntConstant.EmitInt (ig, unchecked ((int) ul));
3334 ig.Emit (OpCodes.Conv_U8);
3336 else
3338 LongConstant.EmitLong (ig, unchecked ((long) ul));
3341 else if (k is char)
3342 IntConstant.EmitInt (ig, (int) ((char) k));
3343 else if (k is sbyte)
3344 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3345 else if (k is byte)
3346 IntConstant.EmitInt (ig, (int) ((byte) k));
3347 else if (k is short)
3348 IntConstant.EmitInt (ig, (int) ((short) k));
3349 else if (k is ushort)
3350 IntConstant.EmitInt (ig, (int) ((ushort) k));
3351 else if (k is bool)
3352 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3353 else
3354 throw new Exception ("Unhandled case");
3357 // structure used to hold blocks of keys while calculating table switch
3358 class KeyBlock : IComparable
3360 public KeyBlock (long _first)
3362 first = last = _first;
3364 public long first;
3365 public long last;
3366 public List<object> element_keys;
3367 // how many items are in the bucket
3368 public int Size = 1;
3369 public int Length
3371 get { return (int) (last - first + 1); }
3373 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3375 return kb_last.last - kb_first.first + 1;
3377 public int CompareTo (object obj)
3379 KeyBlock kb = (KeyBlock) obj;
3380 int nLength = Length;
3381 int nLengthOther = kb.Length;
3382 if (nLengthOther == nLength)
3383 return (int) (kb.first - first);
3384 return nLength - nLengthOther;
3388 /// <summary>
3389 /// This method emits code for a lookup-based switch statement (non-string)
3390 /// Basically it groups the cases into blocks that are at least half full,
3391 /// and then spits out individual lookup opcodes for each block.
3392 /// It emits the longest blocks first, and short blocks are just
3393 /// handled with direct compares.
3394 /// </summary>
3395 /// <param name="ec"></param>
3396 /// <param name="val"></param>
3397 /// <returns></returns>
3398 void TableSwitchEmit (EmitContext ec, Expression val)
3400 int element_count = Elements.Count;
3401 object [] element_keys = new object [element_count];
3402 Elements.Keys.CopyTo (element_keys, 0);
3403 Array.Sort (element_keys);
3405 // initialize the block list with one element per key
3406 var key_blocks = new List<KeyBlock> (element_count);
3407 foreach (object key in element_keys)
3408 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3410 KeyBlock current_kb;
3411 // iteratively merge the blocks while they are at least half full
3412 // there's probably a really cool way to do this with a tree...
3413 while (key_blocks.Count > 1)
3415 var key_blocks_new = new List<KeyBlock> ();
3416 current_kb = (KeyBlock) key_blocks [0];
3417 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3419 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3420 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3422 // merge blocks
3423 current_kb.last = kb.last;
3424 current_kb.Size += kb.Size;
3426 else
3428 // start a new block
3429 key_blocks_new.Add (current_kb);
3430 current_kb = kb;
3433 key_blocks_new.Add (current_kb);
3434 if (key_blocks.Count == key_blocks_new.Count)
3435 break;
3436 key_blocks = key_blocks_new;
3439 // initialize the key lists
3440 foreach (KeyBlock kb in key_blocks)
3441 kb.element_keys = new List<object> ();
3443 // fill the key lists
3444 int iBlockCurr = 0;
3445 if (key_blocks.Count > 0) {
3446 current_kb = (KeyBlock) key_blocks [0];
3447 foreach (object key in element_keys)
3449 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3450 System.Convert.ToInt64 (key) > current_kb.last;
3451 if (next_block)
3452 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3453 current_kb.element_keys.Add (key);
3457 // sort the blocks so we can tackle the largest ones first
3458 key_blocks.Sort ();
3460 // okay now we can start...
3461 ILGenerator ig = ec.ig;
3462 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3463 Label lbl_default = default_target;
3465 Type type_keys = null;
3466 if (element_keys.Length > 0)
3467 type_keys = element_keys [0].GetType (); // used for conversions
3469 Type compare_type;
3471 if (TypeManager.IsEnumType (SwitchType))
3472 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3473 else
3474 compare_type = SwitchType;
3476 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3478 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3479 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3480 if (kb.Length <= 2)
3482 foreach (object key in kb.element_keys) {
3483 SwitchLabel sl = (SwitchLabel) Elements [key];
3484 if (key is int && (int) key == 0) {
3485 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3486 } else {
3487 val.Emit (ec);
3488 EmitObjectInteger (ig, key);
3489 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3493 else
3495 // TODO: if all the keys in the block are the same and there are
3496 // no gaps/defaults then just use a range-check.
3497 if (compare_type == TypeManager.int64_type ||
3498 compare_type == TypeManager.uint64_type)
3500 // TODO: optimize constant/I4 cases
3502 // check block range (could be > 2^31)
3503 val.Emit (ec);
3504 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3505 ig.Emit (OpCodes.Blt, lbl_default);
3506 val.Emit (ec);
3507 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3508 ig.Emit (OpCodes.Bgt, lbl_default);
3510 // normalize range
3511 val.Emit (ec);
3512 if (kb.first != 0)
3514 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3515 ig.Emit (OpCodes.Sub);
3517 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3519 else
3521 // normalize range
3522 val.Emit (ec);
3523 int first = (int) kb.first;
3524 if (first > 0)
3526 IntConstant.EmitInt (ig, first);
3527 ig.Emit (OpCodes.Sub);
3529 else if (first < 0)
3531 IntConstant.EmitInt (ig, -first);
3532 ig.Emit (OpCodes.Add);
3536 // first, build the list of labels for the switch
3537 int iKey = 0;
3538 int cJumps = kb.Length;
3539 Label [] switch_labels = new Label [cJumps];
3540 for (int iJump = 0; iJump < cJumps; iJump++)
3542 object key = kb.element_keys [iKey];
3543 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3545 SwitchLabel sl = (SwitchLabel) Elements [key];
3546 switch_labels [iJump] = sl.GetILLabel (ec);
3547 iKey++;
3549 else
3550 switch_labels [iJump] = lbl_default;
3552 // emit the switch opcode
3553 ig.Emit (OpCodes.Switch, switch_labels);
3556 // mark the default for this block
3557 if (iBlock != 0)
3558 ig.MarkLabel (lbl_default);
3561 // TODO: find the default case and emit it here,
3562 // to prevent having to do the following jump.
3563 // make sure to mark other labels in the default section
3565 // the last default just goes to the end
3566 if (element_keys.Length > 0)
3567 ig.Emit (OpCodes.Br, lbl_default);
3569 // now emit the code for the sections
3570 bool found_default = false;
3572 foreach (SwitchSection ss in Sections) {
3573 foreach (SwitchLabel sl in ss.Labels) {
3574 if (sl.Converted == SwitchLabel.NullStringCase) {
3575 ig.MarkLabel (null_target);
3576 } else if (sl.Label == null) {
3577 ig.MarkLabel (lbl_default);
3578 found_default = true;
3579 if (!has_null_case)
3580 ig.MarkLabel (null_target);
3582 ig.MarkLabel (sl.GetILLabel (ec));
3583 ig.MarkLabel (sl.GetILLabelCode (ec));
3585 ss.Block.Emit (ec);
3588 if (!found_default) {
3589 ig.MarkLabel (lbl_default);
3590 if (!has_null_case) {
3591 ig.MarkLabel (null_target);
3595 ig.MarkLabel (lbl_end);
3598 SwitchSection FindSection (SwitchLabel label)
3600 foreach (SwitchSection ss in Sections){
3601 foreach (SwitchLabel sl in ss.Labels){
3602 if (label == sl)
3603 return ss;
3607 return null;
3610 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3612 foreach (SwitchSection ss in Sections)
3613 ss.Block.MutateHoistedGenericType (storey);
3616 public static void Reset ()
3618 unique_counter = 0;
3619 allowed_types = null;
3622 public override bool Resolve (BlockContext ec)
3624 Expr = Expr.Resolve (ec);
3625 if (Expr == null)
3626 return false;
3628 new_expr = SwitchGoverningType (ec, Expr);
3630 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3631 unwrap = Nullable.Unwrap.Create (Expr, false);
3632 if (unwrap == null)
3633 return false;
3635 new_expr = SwitchGoverningType (ec, unwrap);
3638 if (new_expr == null){
3639 ec.Report.Error (151, loc,
3640 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3641 TypeManager.CSharpName (Expr.Type));
3642 return false;
3645 // Validate switch.
3646 SwitchType = new_expr.Type;
3648 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3649 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3650 return false;
3653 if (!CheckSwitch (ec))
3654 return false;
3656 if (HaveUnwrap)
3657 Elements.Remove (SwitchLabel.NullStringCase);
3659 Switch old_switch = ec.Switch;
3660 ec.Switch = this;
3661 ec.Switch.SwitchType = SwitchType;
3663 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3664 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3666 var constant = new_expr as Constant;
3667 if (constant != null) {
3668 is_constant = true;
3669 object key = constant.GetValue ();
3670 SwitchLabel label;
3671 if (Elements.TryGetValue (key, out label))
3672 constant_section = FindSection (label);
3674 if (constant_section == null)
3675 constant_section = default_section;
3678 bool first = true;
3679 bool ok = true;
3680 foreach (SwitchSection ss in Sections){
3681 if (!first)
3682 ec.CurrentBranching.CreateSibling (
3683 null, FlowBranching.SiblingType.SwitchSection);
3684 else
3685 first = false;
3687 if (is_constant && (ss != constant_section)) {
3688 // If we're a constant switch, we're only emitting
3689 // one single section - mark all the others as
3690 // unreachable.
3691 ec.CurrentBranching.CurrentUsageVector.Goto ();
3692 if (!ss.Block.ResolveUnreachable (ec, true)) {
3693 ok = false;
3695 } else {
3696 if (!ss.Block.Resolve (ec))
3697 ok = false;
3701 if (default_section == null)
3702 ec.CurrentBranching.CreateSibling (
3703 null, FlowBranching.SiblingType.SwitchSection);
3705 ec.EndFlowBranching ();
3706 ec.Switch = old_switch;
3708 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3710 if (!ok)
3711 return false;
3713 if (SwitchType == TypeManager.string_type && !is_constant) {
3714 // TODO: Optimize single case, and single+default case
3715 ResolveStringSwitchMap (ec);
3718 return true;
3721 void ResolveStringSwitchMap (ResolveContext ec)
3723 FullNamedExpression string_dictionary_type;
3724 if (TypeManager.generic_ienumerable_type != null) {
3725 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3726 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3728 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3729 new TypeArguments (
3730 new TypeExpression (TypeManager.string_type, loc),
3731 new TypeExpression (TypeManager.int32_type, loc)), loc);
3732 } else {
3733 MemberAccess system_collections_generic = new MemberAccess (
3734 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3736 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3739 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3740 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3741 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3742 if (!field.Define ())
3743 return;
3744 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3746 var init = new List<Expression> ();
3747 int counter = 0;
3748 Elements.Clear ();
3749 string value = null;
3750 foreach (SwitchSection section in Sections) {
3751 int last_count = init.Count;
3752 foreach (SwitchLabel sl in section.Labels) {
3753 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3754 continue;
3756 value = (string) sl.Converted;
3757 var init_args = new List<Expression> (2);
3758 init_args.Add (new StringLiteral (value, sl.Location));
3759 init_args.Add (new IntConstant (counter, loc));
3760 init.Add (new CollectionElementInitializer (init_args, loc));
3764 // Don't add empty sections
3766 if (last_count == init.Count)
3767 continue;
3769 Elements.Add (counter, section.Labels [0]);
3770 ++counter;
3773 Arguments args = new Arguments (1);
3774 args.Add (new Argument (new IntConstant (init.Count, loc)));
3775 Expression initializer = new NewInitialize (string_dictionary_type, args,
3776 new CollectionOrObjectInitializers (init, loc), loc);
3778 switch_cache_field = new FieldExpr (field, loc);
3779 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3782 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3784 ILGenerator ig = ec.ig;
3785 Label l_initialized = ig.DefineLabel ();
3788 // Skip initialization when value is null
3790 value.EmitBranchable (ec, null_target, false);
3793 // Check if string dictionary is initialized and initialize
3795 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3796 string_dictionary.EmitStatement (ec);
3797 ig.MarkLabel (l_initialized);
3799 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3801 ResolveContext rc = new ResolveContext (ec.MemberContext);
3803 if (TypeManager.generic_ienumerable_type != null) {
3804 Arguments get_value_args = new Arguments (2);
3805 get_value_args.Add (new Argument (value));
3806 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3807 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3808 if (get_item == null)
3809 return;
3812 // A value was not found, go to default case
3814 get_item.EmitBranchable (ec, default_target, false);
3815 } else {
3816 Arguments get_value_args = new Arguments (1);
3817 get_value_args.Add (new Argument (value));
3819 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3820 if (get_item == null)
3821 return;
3823 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3824 get_item_object.EmitAssign (ec, get_item, true, false);
3825 ec.ig.Emit (OpCodes.Brfalse, default_target);
3827 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3828 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3830 get_item_int.EmitStatement (ec);
3831 get_item_object.Release (ec);
3834 TableSwitchEmit (ec, string_switch_variable);
3835 string_switch_variable.Release (ec);
3838 protected override void DoEmit (EmitContext ec)
3840 ILGenerator ig = ec.ig;
3842 default_target = ig.DefineLabel ();
3843 null_target = ig.DefineLabel ();
3845 // Store variable for comparission purposes
3846 // TODO: Don't duplicate non-captured VariableReference
3847 LocalTemporary value;
3848 if (HaveUnwrap) {
3849 value = new LocalTemporary (SwitchType);
3850 unwrap.EmitCheck (ec);
3851 ig.Emit (OpCodes.Brfalse, null_target);
3852 new_expr.Emit (ec);
3853 value.Store (ec);
3854 } else if (!is_constant) {
3855 value = new LocalTemporary (SwitchType);
3856 new_expr.Emit (ec);
3857 value.Store (ec);
3858 } else
3859 value = null;
3862 // Setup the codegen context
3864 Label old_end = ec.LoopEnd;
3865 Switch old_switch = ec.Switch;
3867 ec.LoopEnd = ig.DefineLabel ();
3868 ec.Switch = this;
3870 // Emit Code.
3871 if (is_constant) {
3872 if (constant_section != null)
3873 constant_section.Block.Emit (ec);
3874 } else if (string_dictionary != null) {
3875 DoEmitStringSwitch (value, ec);
3876 } else {
3877 TableSwitchEmit (ec, value);
3880 if (value != null)
3881 value.Release (ec);
3883 // Restore context state.
3884 ig.MarkLabel (ec.LoopEnd);
3887 // Restore the previous context
3889 ec.LoopEnd = old_end;
3890 ec.Switch = old_switch;
3893 protected override void CloneTo (CloneContext clonectx, Statement t)
3895 Switch target = (Switch) t;
3897 target.Expr = Expr.Clone (clonectx);
3898 target.Sections = new List<SwitchSection> ();
3899 foreach (SwitchSection ss in Sections){
3900 target.Sections.Add (ss.Clone (clonectx));
3905 // A place where execution can restart in an iterator
3906 public abstract class ResumableStatement : Statement
3908 bool prepared;
3909 protected Label resume_point;
3911 public Label PrepareForEmit (EmitContext ec)
3913 if (!prepared) {
3914 prepared = true;
3915 resume_point = ec.ig.DefineLabel ();
3917 return resume_point;
3920 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3922 return end;
3924 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3929 // Base class for statements that are implemented in terms of try...finally
3930 public abstract class ExceptionStatement : ResumableStatement
3932 bool code_follows;
3933 Iterator iter;
3934 List<ResumableStatement> resume_points;
3935 int first_resume_pc;
3937 protected abstract void EmitPreTryBody (EmitContext ec);
3938 protected abstract void EmitTryBody (EmitContext ec);
3939 protected abstract void EmitFinallyBody (EmitContext ec);
3941 protected sealed override void DoEmit (EmitContext ec)
3943 ILGenerator ig = ec.ig;
3945 EmitPreTryBody (ec);
3947 if (resume_points != null) {
3948 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3949 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3952 ig.BeginExceptionBlock ();
3954 if (resume_points != null) {
3955 ig.MarkLabel (resume_point);
3957 // For normal control flow, we want to fall-through the Switch
3958 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3959 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3960 IntConstant.EmitInt (ig, first_resume_pc);
3961 ig.Emit (OpCodes.Sub);
3963 Label [] labels = new Label [resume_points.Count];
3964 for (int i = 0; i < resume_points.Count; ++i)
3965 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3966 ig.Emit (OpCodes.Switch, labels);
3969 EmitTryBody (ec);
3971 ig.BeginFinallyBlock ();
3973 Label start_finally = ec.ig.DefineLabel ();
3974 if (resume_points != null) {
3975 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3976 ig.Emit (OpCodes.Brfalse_S, start_finally);
3977 ig.Emit (OpCodes.Endfinally);
3980 ig.MarkLabel (start_finally);
3981 EmitFinallyBody (ec);
3983 ig.EndExceptionBlock ();
3986 public void SomeCodeFollows ()
3988 code_follows = true;
3991 public override bool Resolve (BlockContext ec)
3993 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3994 // So, ensure there's some IL code after this statement.
3995 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3996 ec.NeedReturnLabel ();
3998 iter = ec.CurrentIterator;
3999 return true;
4002 public void AddResumePoint (ResumableStatement stmt, int pc)
4004 if (resume_points == null) {
4005 resume_points = new List<ResumableStatement> ();
4006 first_resume_pc = pc;
4009 if (pc != first_resume_pc + resume_points.Count)
4010 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4012 resume_points.Add (stmt);
4015 Label dispose_try_block;
4016 bool prepared_for_dispose, emitted_dispose;
4017 public override Label PrepareForDispose (EmitContext ec, Label end)
4019 if (!prepared_for_dispose) {
4020 prepared_for_dispose = true;
4021 dispose_try_block = ec.ig.DefineLabel ();
4023 return dispose_try_block;
4026 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4028 if (emitted_dispose)
4029 return;
4031 emitted_dispose = true;
4033 ILGenerator ig = ec.ig;
4035 Label end_of_try = ig.DefineLabel ();
4037 // Ensure that the only way we can get into this code is through a dispatcher
4038 if (have_dispatcher)
4039 ig.Emit (OpCodes.Br, end);
4041 ig.BeginExceptionBlock ();
4043 ig.MarkLabel (dispose_try_block);
4045 Label [] labels = null;
4046 for (int i = 0; i < resume_points.Count; ++i) {
4047 ResumableStatement s = (ResumableStatement) resume_points [i];
4048 Label ret = s.PrepareForDispose (ec, end_of_try);
4049 if (ret.Equals (end_of_try) && labels == null)
4050 continue;
4051 if (labels == null) {
4052 labels = new Label [resume_points.Count];
4053 for (int j = 0; j < i; ++j)
4054 labels [j] = end_of_try;
4056 labels [i] = ret;
4059 if (labels != null) {
4060 int j;
4061 for (j = 1; j < labels.Length; ++j)
4062 if (!labels [0].Equals (labels [j]))
4063 break;
4064 bool emit_dispatcher = j < labels.Length;
4066 if (emit_dispatcher) {
4067 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4068 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4069 IntConstant.EmitInt (ig, first_resume_pc);
4070 ig.Emit (OpCodes.Sub);
4071 ig.Emit (OpCodes.Switch, labels);
4072 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4075 foreach (ResumableStatement s in resume_points)
4076 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4079 ig.MarkLabel (end_of_try);
4081 ig.BeginFinallyBlock ();
4083 EmitFinallyBody (ec);
4085 ig.EndExceptionBlock ();
4089 public class Lock : ExceptionStatement {
4090 Expression expr;
4091 public Statement Statement;
4092 TemporaryVariable temp;
4094 public Lock (Expression expr, Statement stmt, Location l)
4096 this.expr = expr;
4097 Statement = stmt;
4098 loc = l;
4101 public override bool Resolve (BlockContext ec)
4103 expr = expr.Resolve (ec);
4104 if (expr == null)
4105 return false;
4107 if (!TypeManager.IsReferenceType (expr.Type)){
4108 ec.Report.Error (185, loc,
4109 "`{0}' is not a reference type as required by the lock statement",
4110 TypeManager.CSharpName (expr.Type));
4111 return false;
4114 ec.StartFlowBranching (this);
4115 bool ok = Statement.Resolve (ec);
4116 ec.EndFlowBranching ();
4118 ok &= base.Resolve (ec);
4120 // Avoid creating libraries that reference the internal
4121 // mcs NullType:
4122 Type t = expr.Type;
4123 if (t == TypeManager.null_type)
4124 t = TypeManager.object_type;
4126 temp = new TemporaryVariable (t, loc);
4127 temp.Resolve (ec);
4129 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4130 Type monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4131 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4132 monitor_type, "Enter", loc, TypeManager.object_type);
4133 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4134 monitor_type, "Exit", loc, TypeManager.object_type);
4137 return ok;
4140 protected override void EmitPreTryBody (EmitContext ec)
4142 ILGenerator ig = ec.ig;
4144 temp.EmitAssign (ec, expr);
4145 temp.Emit (ec);
4146 ig.Emit (OpCodes.Call, (MethodInfo) TypeManager.void_monitor_enter_object.MetaInfo);
4149 protected override void EmitTryBody (EmitContext ec)
4151 Statement.Emit (ec);
4154 protected override void EmitFinallyBody (EmitContext ec)
4156 temp.Emit (ec);
4157 ec.ig.Emit (OpCodes.Call, (MethodInfo) TypeManager.void_monitor_exit_object.MetaInfo);
4160 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4162 expr.MutateHoistedGenericType (storey);
4163 temp.MutateHoistedGenericType (storey);
4164 Statement.MutateHoistedGenericType (storey);
4167 protected override void CloneTo (CloneContext clonectx, Statement t)
4169 Lock target = (Lock) t;
4171 target.expr = expr.Clone (clonectx);
4172 target.Statement = Statement.Clone (clonectx);
4176 public class Unchecked : Statement {
4177 public Block Block;
4179 public Unchecked (Block b)
4181 Block = b;
4182 b.Unchecked = true;
4185 public override bool Resolve (BlockContext ec)
4187 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4188 return Block.Resolve (ec);
4191 protected override void DoEmit (EmitContext ec)
4193 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4194 Block.Emit (ec);
4197 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4199 Block.MutateHoistedGenericType (storey);
4202 protected override void CloneTo (CloneContext clonectx, Statement t)
4204 Unchecked target = (Unchecked) t;
4206 target.Block = clonectx.LookupBlock (Block);
4210 public class Checked : Statement {
4211 public Block Block;
4213 public Checked (Block b)
4215 Block = b;
4216 b.Unchecked = false;
4219 public override bool Resolve (BlockContext ec)
4221 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4222 return Block.Resolve (ec);
4225 protected override void DoEmit (EmitContext ec)
4227 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4228 Block.Emit (ec);
4231 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4233 Block.MutateHoistedGenericType (storey);
4236 protected override void CloneTo (CloneContext clonectx, Statement t)
4238 Checked target = (Checked) t;
4240 target.Block = clonectx.LookupBlock (Block);
4244 public class Unsafe : Statement {
4245 public Block Block;
4247 public Unsafe (Block b)
4249 Block = b;
4250 Block.Unsafe = true;
4251 loc = b.StartLocation;
4254 public override bool Resolve (BlockContext ec)
4256 if (ec.CurrentIterator != null)
4257 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4259 using (ec.Set (ResolveContext.Options.UnsafeScope))
4260 return Block.Resolve (ec);
4263 protected override void DoEmit (EmitContext ec)
4265 Block.Emit (ec);
4268 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4270 Block.MutateHoistedGenericType (storey);
4273 protected override void CloneTo (CloneContext clonectx, Statement t)
4275 Unsafe target = (Unsafe) t;
4277 target.Block = clonectx.LookupBlock (Block);
4282 // Fixed statement
4284 class Fixed : Statement {
4285 Expression type;
4286 List<KeyValuePair<LocalInfo, Expression>> declarators;
4287 Statement statement;
4288 Type expr_type;
4289 Emitter[] data;
4290 bool has_ret;
4292 abstract class Emitter
4294 protected LocalInfo vi;
4295 protected Expression converted;
4297 protected Emitter (Expression expr, LocalInfo li)
4299 converted = expr;
4300 vi = li;
4303 public abstract void Emit (EmitContext ec);
4304 public abstract void EmitExit (EmitContext ec);
4307 class ExpressionEmitter : Emitter {
4308 public ExpressionEmitter (Expression converted, LocalInfo li) :
4309 base (converted, li)
4313 public override void Emit (EmitContext ec) {
4315 // Store pointer in pinned location
4317 converted.Emit (ec);
4318 vi.EmitAssign (ec);
4321 public override void EmitExit (EmitContext ec)
4323 ec.ig.Emit (OpCodes.Ldc_I4_0);
4324 ec.ig.Emit (OpCodes.Conv_U);
4325 vi.EmitAssign (ec);
4329 class StringEmitter : Emitter
4331 LocalInfo pinned_string;
4333 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4334 base (expr, li)
4336 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4337 pinned_string.Pinned = true;
4340 public StringEmitter Resolve (ResolveContext rc)
4342 pinned_string.Resolve (rc);
4344 if (TypeManager.int_get_offset_to_string_data == null) {
4345 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4346 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4349 return this;
4352 public override void Emit (EmitContext ec)
4354 pinned_string.ResolveVariable (ec);
4356 converted.Emit (ec);
4357 pinned_string.EmitAssign (ec);
4359 // TODO: Should use Binary::Add
4360 pinned_string.Emit (ec);
4361 ec.ig.Emit (OpCodes.Conv_I);
4363 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4364 //pe.InstanceExpression = pinned_string;
4365 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4367 ec.ig.Emit (OpCodes.Add);
4368 vi.EmitAssign (ec);
4371 public override void EmitExit (EmitContext ec)
4373 ec.ig.Emit (OpCodes.Ldnull);
4374 pinned_string.EmitAssign (ec);
4378 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4380 this.type = type;
4381 declarators = decls;
4382 statement = stmt;
4383 loc = l;
4386 public Statement Statement {
4387 get { return statement; }
4390 public override bool Resolve (BlockContext ec)
4392 if (!ec.IsUnsafe){
4393 Expression.UnsafeError (ec, loc);
4394 return false;
4397 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4398 if (texpr == null) {
4399 if (type is VarExpr)
4400 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4402 return false;
4405 expr_type = texpr.Type;
4407 data = new Emitter [declarators.Count];
4409 if (!expr_type.IsPointer){
4410 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4411 return false;
4414 int i = 0;
4415 foreach (var p in declarators){
4416 LocalInfo vi = p.Key;
4417 Expression e = p.Value;
4419 vi.VariableInfo.SetAssigned (ec);
4420 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4423 // The rules for the possible declarators are pretty wise,
4424 // but the production on the grammar is more concise.
4426 // So we have to enforce these rules here.
4428 // We do not resolve before doing the case 1 test,
4429 // because the grammar is explicit in that the token &
4430 // is present, so we need to test for this particular case.
4433 if (e is Cast){
4434 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4435 return false;
4438 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4439 e = e.Resolve (ec);
4442 if (e == null)
4443 return false;
4446 // Case 2: Array
4448 if (e.Type.IsArray){
4449 Type array_type = TypeManager.GetElementType (e.Type);
4452 // Provided that array_type is unmanaged,
4454 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4455 return false;
4458 // and T* is implicitly convertible to the
4459 // pointer type given in the fixed statement.
4461 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4463 Expression converted = Convert.ImplicitConversionRequired (
4464 ec, array_ptr, vi.VariableType, loc);
4465 if (converted == null)
4466 return false;
4469 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4471 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4472 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4473 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4474 new NullPointer (loc),
4475 converted);
4477 converted = converted.Resolve (ec);
4479 data [i] = new ExpressionEmitter (converted, vi);
4480 i++;
4482 continue;
4486 // Case 3: string
4488 if (e.Type == TypeManager.string_type){
4489 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4490 i++;
4491 continue;
4494 // Case 4: fixed buffer
4495 if (e is FixedBufferPtr) {
4496 data [i++] = new ExpressionEmitter (e, vi);
4497 continue;
4501 // Case 1: & object.
4503 Unary u = e as Unary;
4504 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4505 IVariableReference vr = u.Expr as IVariableReference;
4506 if (vr == null || !vr.IsFixed) {
4507 data [i] = new ExpressionEmitter (e, vi);
4511 if (data [i++] == null)
4512 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4514 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4517 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4518 bool ok = statement.Resolve (ec);
4519 bool flow_unreachable = ec.EndFlowBranching ();
4520 has_ret = flow_unreachable;
4522 return ok;
4525 protected override void DoEmit (EmitContext ec)
4527 for (int i = 0; i < data.Length; i++) {
4528 data [i].Emit (ec);
4531 statement.Emit (ec);
4533 if (has_ret)
4534 return;
4537 // Clear the pinned variable
4539 for (int i = 0; i < data.Length; i++) {
4540 data [i].EmitExit (ec);
4544 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4546 // Fixed statement cannot be used inside anonymous methods or lambdas
4547 throw new NotSupportedException ();
4550 protected override void CloneTo (CloneContext clonectx, Statement t)
4552 Fixed target = (Fixed) t;
4554 target.type = type.Clone (clonectx);
4555 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4556 foreach (var p in declarators) {
4557 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4558 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4561 target.statement = statement.Clone (clonectx);
4565 public class Catch : Statement {
4566 public readonly string Name;
4567 public Block Block;
4568 public Block VarBlock;
4570 Expression type_expr;
4571 Type type;
4573 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4575 type_expr = type;
4576 Name = name;
4577 Block = block;
4578 VarBlock = var_block;
4579 loc = l;
4582 public Type CatchType {
4583 get {
4584 return type;
4588 public bool IsGeneral {
4589 get {
4590 return type_expr == null;
4594 protected override void DoEmit (EmitContext ec)
4596 ILGenerator ig = ec.ig;
4598 if (CatchType != null)
4599 ig.BeginCatchBlock (CatchType);
4600 else
4601 ig.BeginCatchBlock (TypeManager.object_type);
4603 if (VarBlock != null)
4604 VarBlock.Emit (ec);
4606 if (Name != null) {
4607 // TODO: Move to resolve
4608 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4609 lvr.Resolve (new ResolveContext (ec.MemberContext));
4611 // Only to make verifier happy
4612 if (TypeManager.IsGenericParameter (lvr.Type))
4613 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4615 Expression source;
4616 if (lvr.IsHoisted) {
4617 LocalTemporary lt = new LocalTemporary (lvr.Type);
4618 lt.Store (ec);
4619 source = lt;
4620 } else {
4621 // Variable is at the top of the stack
4622 source = EmptyExpression.Null;
4625 lvr.EmitAssign (ec, source, false, false);
4626 } else
4627 ig.Emit (OpCodes.Pop);
4629 Block.Emit (ec);
4632 public override bool Resolve (BlockContext ec)
4634 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4635 if (type_expr != null) {
4636 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4637 if (te == null)
4638 return false;
4640 type = te.Type;
4642 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4643 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4644 return false;
4646 } else
4647 type = null;
4649 if (!Block.Resolve (ec))
4650 return false;
4652 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4653 // emit the "unused variable" warnings.
4654 if (VarBlock != null)
4655 return VarBlock.Resolve (ec);
4657 return true;
4661 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4663 if (type != null)
4664 type = storey.MutateType (type);
4665 if (VarBlock != null)
4666 VarBlock.MutateHoistedGenericType (storey);
4667 Block.MutateHoistedGenericType (storey);
4670 protected override void CloneTo (CloneContext clonectx, Statement t)
4672 Catch target = (Catch) t;
4674 if (type_expr != null)
4675 target.type_expr = type_expr.Clone (clonectx);
4676 if (VarBlock != null)
4677 target.VarBlock = clonectx.LookupBlock (VarBlock);
4678 target.Block = clonectx.LookupBlock (Block);
4682 public class TryFinally : ExceptionStatement {
4683 Statement stmt;
4684 Block fini;
4686 public TryFinally (Statement stmt, Block fini, Location l)
4688 this.stmt = stmt;
4689 this.fini = fini;
4690 loc = l;
4693 public override bool Resolve (BlockContext ec)
4695 bool ok = true;
4697 ec.StartFlowBranching (this);
4699 if (!stmt.Resolve (ec))
4700 ok = false;
4702 if (ok)
4703 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4704 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4705 if (!fini.Resolve (ec))
4706 ok = false;
4709 ec.EndFlowBranching ();
4711 ok &= base.Resolve (ec);
4713 return ok;
4716 protected override void EmitPreTryBody (EmitContext ec)
4720 protected override void EmitTryBody (EmitContext ec)
4722 stmt.Emit (ec);
4725 protected override void EmitFinallyBody (EmitContext ec)
4727 fini.Emit (ec);
4730 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4732 stmt.MutateHoistedGenericType (storey);
4733 fini.MutateHoistedGenericType (storey);
4736 protected override void CloneTo (CloneContext clonectx, Statement t)
4738 TryFinally target = (TryFinally) t;
4740 target.stmt = (Statement) stmt.Clone (clonectx);
4741 if (fini != null)
4742 target.fini = clonectx.LookupBlock (fini);
4746 public class TryCatch : Statement {
4747 public Block Block;
4748 public List<Catch> Specific;
4749 public Catch General;
4750 bool inside_try_finally, code_follows;
4752 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4754 this.Block = block;
4755 this.Specific = catch_clauses;
4756 this.inside_try_finally = inside_try_finally;
4758 Catch c = catch_clauses [0];
4759 if (c.IsGeneral) {
4760 this.General = c;
4761 catch_clauses.RemoveAt (0);
4764 loc = l;
4767 public override bool Resolve (BlockContext ec)
4769 bool ok = true;
4771 ec.StartFlowBranching (this);
4773 if (!Block.Resolve (ec))
4774 ok = false;
4776 Type[] prev_catches = new Type [Specific.Count];
4777 int last_index = 0;
4778 foreach (Catch c in Specific){
4779 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4781 if (c.Name != null) {
4782 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4783 if (vi == null)
4784 throw new Exception ();
4786 vi.VariableInfo = null;
4789 if (!c.Resolve (ec)) {
4790 ok = false;
4791 continue;
4794 Type resolved_type = c.CatchType;
4795 for (int ii = 0; ii < last_index; ++ii) {
4796 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4797 ec.Report.Error (160, c.loc,
4798 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4799 TypeManager.CSharpName (prev_catches [ii]));
4800 ok = false;
4804 prev_catches [last_index++] = resolved_type;
4807 if (General != null) {
4808 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4809 foreach (Catch c in Specific){
4810 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4811 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'");
4816 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4818 if (!General.Resolve (ec))
4819 ok = false;
4822 ec.EndFlowBranching ();
4824 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4825 // So, ensure there's some IL code after this statement
4826 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4827 ec.NeedReturnLabel ();
4829 return ok;
4832 public void SomeCodeFollows ()
4834 code_follows = true;
4837 protected override void DoEmit (EmitContext ec)
4839 ILGenerator ig = ec.ig;
4841 if (!inside_try_finally)
4842 ig.BeginExceptionBlock ();
4844 Block.Emit (ec);
4846 foreach (Catch c in Specific)
4847 c.Emit (ec);
4849 if (General != null)
4850 General.Emit (ec);
4852 if (!inside_try_finally)
4853 ig.EndExceptionBlock ();
4856 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4858 Block.MutateHoistedGenericType (storey);
4860 if (General != null)
4861 General.MutateHoistedGenericType (storey);
4862 if (Specific != null) {
4863 foreach (Catch c in Specific)
4864 c.MutateHoistedGenericType (storey);
4868 protected override void CloneTo (CloneContext clonectx, Statement t)
4870 TryCatch target = (TryCatch) t;
4872 target.Block = clonectx.LookupBlock (Block);
4873 if (General != null)
4874 target.General = (Catch) General.Clone (clonectx);
4875 if (Specific != null){
4876 target.Specific = new List<Catch> ();
4877 foreach (Catch c in Specific)
4878 target.Specific.Add ((Catch) c.Clone (clonectx));
4883 // FIXME: Why is it almost exact copy of Using ??
4884 public class UsingTemporary : ExceptionStatement {
4885 TemporaryVariable local_copy;
4886 public Statement Statement;
4887 Expression expr;
4888 Type expr_type;
4890 public UsingTemporary (Expression expr, Statement stmt, Location l)
4892 this.expr = expr;
4893 Statement = stmt;
4894 loc = l;
4897 public override bool Resolve (BlockContext ec)
4899 expr = expr.Resolve (ec);
4900 if (expr == null)
4901 return false;
4903 expr_type = expr.Type;
4905 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type) &&
4906 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4907 if (!TypeManager.IsDynamicType (expr_type)) {
4908 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4909 return false;
4912 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4913 expr_type = expr.Type;
4916 local_copy = new TemporaryVariable (expr_type, loc);
4917 local_copy.Resolve (ec);
4919 ec.StartFlowBranching (this);
4921 bool ok = Statement.Resolve (ec);
4923 ec.EndFlowBranching ();
4925 ok &= base.Resolve (ec);
4927 if (TypeManager.void_dispose_void == null) {
4928 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4929 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4932 return ok;
4935 protected override void EmitPreTryBody (EmitContext ec)
4937 local_copy.EmitAssign (ec, expr);
4940 protected override void EmitTryBody (EmitContext ec)
4942 Statement.Emit (ec);
4945 protected override void EmitFinallyBody (EmitContext ec)
4947 ILGenerator ig = ec.ig;
4948 if (!TypeManager.IsStruct (expr_type)) {
4949 Label skip = ig.DefineLabel ();
4950 local_copy.Emit (ec);
4951 ig.Emit (OpCodes.Brfalse, skip);
4952 local_copy.Emit (ec);
4953 ig.Emit (OpCodes.Callvirt, (MethodInfo) TypeManager.void_dispose_void.MetaInfo);
4954 ig.MarkLabel (skip);
4955 return;
4958 Expression ml = Expression.MemberLookup (RootContext.ToplevelTypes.Compiler,
4959 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4960 "Dispose", Location.Null);
4962 if (!(ml is MethodGroupExpr)) {
4963 local_copy.Emit (ec);
4964 ig.Emit (OpCodes.Box, expr_type);
4965 ig.Emit (OpCodes.Callvirt, (MethodInfo) TypeManager.void_dispose_void.MetaInfo);
4966 return;
4969 MethodSpec mi = null;
4971 foreach (var mk in ((MethodGroupExpr) ml).Methods) {
4972 if (mk.Parameters.IsEmpty) {
4973 mi = mk;
4974 break;
4978 if (mi == null) {
4979 ec.Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4980 return;
4983 local_copy.AddressOf (ec, AddressOp.Load);
4984 ig.Emit (OpCodes.Call, (MethodInfo) mi.MetaInfo);
4987 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4989 expr_type = storey.MutateType (expr_type);
4990 local_copy.MutateHoistedGenericType (storey);
4991 Statement.MutateHoistedGenericType (storey);
4994 protected override void CloneTo (CloneContext clonectx, Statement t)
4996 UsingTemporary target = (UsingTemporary) t;
4998 target.expr = expr.Clone (clonectx);
4999 target.Statement = Statement.Clone (clonectx);
5003 public class Using : ExceptionStatement {
5004 Statement stmt;
5005 public Statement EmbeddedStatement {
5006 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5009 Expression var;
5010 Expression init;
5012 ExpressionStatement assign;
5014 public Using (Expression var, Expression init, Statement stmt, Location l)
5016 this.var = var;
5017 this.init = init;
5018 this.stmt = stmt;
5019 loc = l;
5022 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
5024 ec.Report.SymbolRelatedToPreviousError (expr.Type);
5025 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5026 TypeManager.CSharpName (expr.Type));
5029 protected override void EmitPreTryBody (EmitContext ec)
5031 assign.EmitStatement (ec);
5034 protected override void EmitTryBody (EmitContext ec)
5036 stmt.Emit (ec);
5039 protected override void EmitFinallyBody (EmitContext ec)
5041 ILGenerator ig = ec.ig;
5042 Label skip = ig.DefineLabel ();
5044 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5045 if (emit_null_check) {
5046 var.Emit (ec);
5047 ig.Emit (OpCodes.Brfalse, skip);
5050 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5052 if (emit_null_check)
5053 ig.MarkLabel (skip);
5056 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5058 assign.MutateHoistedGenericType (storey);
5059 var.MutateHoistedGenericType (storey);
5060 stmt.MutateHoistedGenericType (storey);
5063 public override bool Resolve (BlockContext ec)
5065 if (!ResolveVariable (ec))
5066 return false;
5068 ec.StartFlowBranching (this);
5070 bool ok = stmt.Resolve (ec);
5072 ec.EndFlowBranching ();
5074 ok &= base.Resolve (ec);
5076 if (TypeManager.void_dispose_void == null) {
5077 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5078 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5081 return ok;
5084 bool ResolveVariable (BlockContext ec)
5086 assign = new SimpleAssign (var, init, loc);
5087 assign = assign.ResolveStatement (ec);
5088 if (assign == null)
5089 return false;
5091 if (assign.Type == TypeManager.idisposable_type ||
5092 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5093 return true;
5096 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5097 if (e == null) {
5098 if (TypeManager.IsDynamicType (assign.Type)) {
5099 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
5100 var = new TemporaryVariable (e.Type, loc);
5101 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
5102 return true;
5105 Error_IsNotConvertibleToIDisposable (ec, var);
5106 return false;
5109 throw new NotImplementedException ("covariance?");
5112 protected override void CloneTo (CloneContext clonectx, Statement t)
5114 Using target = (Using) t;
5116 target.var = var.Clone (clonectx);
5117 target.init = init.Clone (clonectx);
5118 target.stmt = stmt.Clone (clonectx);
5122 /// <summary>
5123 /// Implementation of the foreach C# statement
5124 /// </summary>
5125 public class Foreach : Statement {
5127 sealed class ArrayForeach : Statement
5129 class ArrayCounter : TemporaryVariable
5131 StatementExpression increment;
5133 public ArrayCounter (Location loc)
5134 : base (TypeManager.int32_type, loc)
5138 public void ResolveIncrement (BlockContext ec)
5140 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5141 increment.Resolve (ec);
5144 public void EmitIncrement (EmitContext ec)
5146 increment.Emit (ec);
5150 readonly Foreach for_each;
5151 readonly Statement statement;
5153 Expression conv;
5154 TemporaryVariable[] lengths;
5155 Expression [] length_exprs;
5156 ArrayCounter[] counter;
5158 TemporaryVariable copy;
5159 Expression access;
5161 public ArrayForeach (Foreach @foreach, int rank)
5163 for_each = @foreach;
5164 statement = for_each.statement;
5165 loc = @foreach.loc;
5167 counter = new ArrayCounter [rank];
5168 length_exprs = new Expression [rank];
5171 // Only use temporary length variables when dealing with
5172 // multi-dimensional arrays
5174 if (rank > 1)
5175 lengths = new TemporaryVariable [rank];
5178 protected override void CloneTo (CloneContext clonectx, Statement target)
5180 throw new NotImplementedException ();
5183 public override bool Resolve (BlockContext ec)
5185 copy = new TemporaryVariable (for_each.expr.Type, loc);
5186 copy.Resolve (ec);
5188 int rank = length_exprs.Length;
5189 Arguments list = new Arguments (rank);
5190 for (int i = 0; i < rank; i++) {
5191 counter [i] = new ArrayCounter (loc);
5192 counter [i].ResolveIncrement (ec);
5194 if (rank == 1) {
5195 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5196 } else {
5197 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5198 lengths [i].Resolve (ec);
5200 Arguments args = new Arguments (1);
5201 args.Add (new Argument (new IntConstant (i, loc)));
5202 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5205 list.Add (new Argument (counter [i]));
5208 access = new ElementAccess (copy, list).Resolve (ec);
5209 if (access == null)
5210 return false;
5212 Expression var_type = for_each.type;
5213 VarExpr ve = var_type as VarExpr;
5214 if (ve != null) {
5215 // Infer implicitly typed local variable from foreach array type
5216 var_type = new TypeExpression (access.Type, ve.Location);
5219 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5220 if (var_type == null)
5221 return false;
5223 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5224 if (conv == null)
5225 return false;
5227 bool ok = true;
5229 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5230 ec.CurrentBranching.CreateSibling ();
5232 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5233 if (for_each.variable == null)
5234 ok = false;
5236 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5237 if (!statement.Resolve (ec))
5238 ok = false;
5239 ec.EndFlowBranching ();
5241 // There's no direct control flow from the end of the embedded statement to the end of the loop
5242 ec.CurrentBranching.CurrentUsageVector.Goto ();
5244 ec.EndFlowBranching ();
5246 return ok;
5249 protected override void DoEmit (EmitContext ec)
5251 ILGenerator ig = ec.ig;
5253 copy.EmitAssign (ec, for_each.expr);
5255 int rank = length_exprs.Length;
5256 Label[] test = new Label [rank];
5257 Label[] loop = new Label [rank];
5259 for (int i = 0; i < rank; i++) {
5260 test [i] = ig.DefineLabel ();
5261 loop [i] = ig.DefineLabel ();
5263 if (lengths != null)
5264 lengths [i].EmitAssign (ec, length_exprs [i]);
5267 IntConstant zero = new IntConstant (0, loc);
5268 for (int i = 0; i < rank; i++) {
5269 counter [i].EmitAssign (ec, zero);
5271 ig.Emit (OpCodes.Br, test [i]);
5272 ig.MarkLabel (loop [i]);
5275 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5277 statement.Emit (ec);
5279 ig.MarkLabel (ec.LoopBegin);
5281 for (int i = rank - 1; i >= 0; i--){
5282 counter [i].EmitIncrement (ec);
5284 ig.MarkLabel (test [i]);
5285 counter [i].Emit (ec);
5287 if (lengths != null)
5288 lengths [i].Emit (ec);
5289 else
5290 length_exprs [i].Emit (ec);
5292 ig.Emit (OpCodes.Blt, loop [i]);
5295 ig.MarkLabel (ec.LoopEnd);
5298 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5300 for_each.expr.MutateHoistedGenericType (storey);
5302 copy.MutateHoistedGenericType (storey);
5303 conv.MutateHoistedGenericType (storey);
5304 statement.MutateHoistedGenericType (storey);
5306 for (int i = 0; i < counter.Length; i++) {
5307 counter [i].MutateHoistedGenericType (storey);
5308 if (lengths != null)
5309 lengths [i].MutateHoistedGenericType (storey);
5314 sealed class CollectionForeach : Statement
5316 class CollectionForeachStatement : Statement
5318 Type type;
5319 Expression variable, current, conv;
5320 Statement statement;
5321 Assign assign;
5323 public CollectionForeachStatement (Type type, Expression variable,
5324 Expression current, Statement statement,
5325 Location loc)
5327 this.type = type;
5328 this.variable = variable;
5329 this.current = current;
5330 this.statement = statement;
5331 this.loc = loc;
5334 protected override void CloneTo (CloneContext clonectx, Statement target)
5336 throw new NotImplementedException ();
5339 public override bool Resolve (BlockContext ec)
5341 current = current.Resolve (ec);
5342 if (current == null)
5343 return false;
5345 conv = Convert.ExplicitConversion (ec, current, type, loc);
5346 if (conv == null)
5347 return false;
5349 assign = new SimpleAssign (variable, conv, loc);
5350 if (assign.Resolve (ec) == null)
5351 return false;
5353 if (!statement.Resolve (ec))
5354 return false;
5356 return true;
5359 protected override void DoEmit (EmitContext ec)
5361 assign.EmitStatement (ec);
5362 statement.Emit (ec);
5365 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5367 assign.MutateHoistedGenericType (storey);
5368 statement.MutateHoistedGenericType (storey);
5372 Expression variable, expr;
5373 Statement statement;
5375 TemporaryVariable enumerator;
5376 Expression init;
5377 Statement loop;
5378 Statement wrapper;
5380 MethodGroupExpr get_enumerator;
5381 PropertyExpr get_current;
5382 MethodSpec move_next;
5383 Expression var_type;
5384 Type enumerator_type;
5385 bool enumerator_found;
5387 public CollectionForeach (Expression var_type, Expression var,
5388 Expression expr, Statement stmt, Location l)
5390 this.var_type = var_type;
5391 this.variable = var;
5392 this.expr = expr;
5393 statement = stmt;
5394 loc = l;
5397 protected override void CloneTo (CloneContext clonectx, Statement target)
5399 throw new NotImplementedException ();
5402 bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi)
5404 Type return_type = mi.ReturnType;
5407 // Ok, we can access it, now make sure that we can do something
5408 // with this `GetEnumerator'
5411 if (return_type == TypeManager.ienumerator_type ||
5412 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5414 // If it is not an interface, lets try to find the methods ourselves.
5415 // For example, if we have:
5416 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5417 // We can avoid the iface call. This is a runtime perf boost.
5418 // even bigger if we have a ValueType, because we avoid the cost
5419 // of boxing.
5421 // We have to make sure that both methods exist for us to take
5422 // this path. If one of the methods does not exist, we will just
5423 // use the interface. Sadly, this complex if statement is the only
5424 // way I could do this without a goto
5427 if (TypeManager.bool_movenext_void == null) {
5428 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5429 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5432 if (TypeManager.ienumerator_getcurrent == null) {
5433 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5434 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5438 // Prefer a generic enumerator over a non-generic one.
5440 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5441 enumerator_type = return_type;
5442 if (!FetchGetCurrent (ec, return_type))
5443 get_current = new PropertyExpr (
5444 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5445 if (!FetchMoveNext (return_type))
5446 move_next = TypeManager.bool_movenext_void;
5447 return true;
5450 if (return_type.IsInterface ||
5451 !FetchMoveNext (return_type) ||
5452 !FetchGetCurrent (ec, return_type)) {
5453 enumerator_type = return_type;
5454 move_next = TypeManager.bool_movenext_void;
5455 get_current = new PropertyExpr (
5456 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5457 return true;
5459 } else {
5461 // Ok, so they dont return an IEnumerable, we will have to
5462 // find if they support the GetEnumerator pattern.
5465 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5466 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",
5467 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5468 return false;
5472 enumerator_type = return_type;
5474 return true;
5478 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5480 bool FetchMoveNext (Type t)
5482 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5483 MemberTypes.Method,
5484 BindingFlags.Public | BindingFlags.Instance,
5485 "MoveNext", null);
5487 if (move_next_list == null)
5488 return false;
5490 foreach (MemberInfo m in move_next_list){
5491 MethodInfo mi = (MethodInfo) m;
5493 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5494 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5495 move_next = Import.CreateMethod (mi);
5496 return true;
5500 return false;
5504 // Retrieves a `public T get_Current ()' method from the Type `t'
5506 bool FetchGetCurrent (ResolveContext ec, Type t)
5508 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5509 ec.CurrentType, t, "Current", MemberTypes.Property,
5510 Expression.AllBindingFlags, loc) as PropertyExpr;
5511 if (pe == null)
5512 return false;
5514 get_current = pe;
5515 return true;
5518 void Error_Enumerator (BlockContext ec)
5520 if (enumerator_found) {
5521 return;
5524 ec.Report.Error (1579, loc,
5525 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5526 TypeManager.CSharpName (expr.Type));
5529 bool IsOverride (MethodSpec ms)
5531 MethodInfo m = (MethodInfo) ms.MetaInfo;
5532 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5534 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5535 return false;
5536 if (m is MethodBuilder)
5537 return true;
5539 MethodInfo base_method = m.GetBaseDefinition ();
5540 return base_method != m;
5543 bool TryType (ResolveContext ec, Type t)
5545 MethodGroupExpr mg = Expression.MemberLookup (ec.Compiler,
5546 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5547 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5548 if (mg == null)
5549 return false;
5551 MethodSpec result = null;
5552 MethodSpec tmp_move_next = null;
5553 PropertyExpr tmp_get_cur = null;
5554 Type tmp_enumerator_type = enumerator_type;
5555 foreach (var mi in mg.Methods) {
5556 if (!mi.Parameters.IsEmpty)
5557 continue;
5559 // Check whether GetEnumerator is public
5560 if ((mi.MetaInfo.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5561 continue;
5563 if (IsOverride (mi))
5564 continue;
5566 enumerator_found = true;
5568 if (!GetEnumeratorFilter (ec, mi))
5569 continue;
5571 if (result != null) {
5572 if (TypeManager.IsGenericType (result.ReturnType)) {
5573 if (!TypeManager.IsGenericType (mi.ReturnType))
5574 continue;
5576 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5577 ec.Report.SymbolRelatedToPreviousError (t);
5578 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5579 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5580 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5581 return false;
5584 // Always prefer generics enumerators
5585 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5586 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5587 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5588 continue;
5590 ec.Report.SymbolRelatedToPreviousError (result.MetaInfo);
5591 ec.Report.SymbolRelatedToPreviousError (mi.MetaInfo);
5592 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5593 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result.MetaInfo), TypeManager.CSharpSignature (mi.MetaInfo));
5594 return false;
5597 result = mi;
5598 tmp_move_next = move_next;
5599 tmp_get_cur = get_current;
5600 tmp_enumerator_type = enumerator_type;
5601 if (mi.DeclaringType == t)
5602 break;
5605 if (result != null) {
5606 move_next = tmp_move_next;
5607 get_current = tmp_get_cur;
5608 enumerator_type = tmp_enumerator_type;
5609 var mi = new [] { result };
5610 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5612 if (t != expr.Type) {
5613 expr = Convert.ExplicitConversion (
5614 ec, expr, t, loc);
5615 if (expr == null)
5616 throw new InternalErrorException ();
5619 get_enumerator.InstanceExpression = expr;
5620 get_enumerator.IsBase = t != expr.Type;
5622 return true;
5625 return false;
5628 bool ProbeCollectionType (ResolveContext ec, Type t)
5630 int errors = ec.Report.Errors;
5631 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5632 if (TryType (ec, tt))
5633 return true;
5634 tt = tt.BaseType;
5637 if (ec.Report.Errors > errors)
5638 return false;
5641 // Now try to find the method in the interfaces
5643 Type [] ifaces = TypeManager.GetInterfaces (t);
5644 foreach (Type i in ifaces){
5645 if (TryType (ec, i))
5646 return true;
5649 return false;
5652 public override bool Resolve (BlockContext ec)
5654 enumerator_type = TypeManager.ienumerator_type;
5656 bool is_dynamic = TypeManager.IsDynamicType (expr.Type);
5657 if (is_dynamic)
5658 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5660 if (!ProbeCollectionType (ec, expr.Type)) {
5661 Error_Enumerator (ec);
5662 return false;
5665 VarExpr ve = var_type as VarExpr;
5666 if (ve != null) {
5667 // Infer implicitly typed local variable from foreach enumerable type
5668 var_type = new TypeExpression (
5669 is_dynamic ? InternalType.Dynamic : get_current.PropertyInfo.PropertyType,
5670 var_type.Location);
5673 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5674 if (var_type == null)
5675 return false;
5677 enumerator = new TemporaryVariable (enumerator_type, loc);
5678 enumerator.Resolve (ec);
5680 init = new Invocation (get_enumerator, null);
5681 init = init.Resolve (ec);
5682 if (init == null)
5683 return false;
5685 Expression move_next_expr;
5687 var mi = new [] { move_next };
5688 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5689 mg.InstanceExpression = enumerator;
5691 move_next_expr = new Invocation (mg, null);
5694 get_current.InstanceExpression = enumerator;
5696 Statement block = new CollectionForeachStatement (
5697 var_type.Type, variable, get_current, statement, loc);
5699 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5702 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5703 if (implements_idisposable || !enumerator_type.IsSealed) {
5704 wrapper = new DisposableWrapper (this, implements_idisposable);
5705 } else {
5706 wrapper = new NonDisposableWrapper (this);
5709 return wrapper.Resolve (ec);
5712 protected override void DoEmit (EmitContext ec)
5714 wrapper.Emit (ec);
5717 class NonDisposableWrapper : Statement {
5718 CollectionForeach parent;
5720 internal NonDisposableWrapper (CollectionForeach parent)
5722 this.parent = parent;
5725 protected override void CloneTo (CloneContext clonectx, Statement target)
5727 throw new NotSupportedException ();
5730 public override bool Resolve (BlockContext ec)
5732 return parent.ResolveLoop (ec);
5735 protected override void DoEmit (EmitContext ec)
5737 parent.EmitLoopInit (ec);
5738 parent.EmitLoopBody (ec);
5741 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5743 throw new NotSupportedException ();
5747 sealed class DisposableWrapper : ExceptionStatement
5749 CollectionForeach parent;
5750 bool implements_idisposable;
5752 internal DisposableWrapper (CollectionForeach parent, bool implements)
5754 this.parent = parent;
5755 this.implements_idisposable = implements;
5758 protected override void CloneTo (CloneContext clonectx, Statement target)
5760 throw new NotSupportedException ();
5763 public override bool Resolve (BlockContext ec)
5765 bool ok = true;
5767 ec.StartFlowBranching (this);
5769 if (!parent.ResolveLoop (ec))
5770 ok = false;
5772 ec.EndFlowBranching ();
5774 ok &= base.Resolve (ec);
5776 if (TypeManager.void_dispose_void == null) {
5777 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5778 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5780 return ok;
5783 protected override void EmitPreTryBody (EmitContext ec)
5785 parent.EmitLoopInit (ec);
5788 protected override void EmitTryBody (EmitContext ec)
5790 parent.EmitLoopBody (ec);
5793 protected override void EmitFinallyBody (EmitContext ec)
5795 Expression instance = parent.enumerator;
5796 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5797 ILGenerator ig = ec.ig;
5799 parent.enumerator.Emit (ec);
5801 Label call_dispose = ig.DefineLabel ();
5803 if (!implements_idisposable) {
5804 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5805 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5806 temp.Store (ec);
5807 temp.Emit (ec);
5808 instance = temp;
5811 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5813 // using 'endfinally' to empty the evaluation stack
5814 ig.Emit (OpCodes.Endfinally);
5815 ig.MarkLabel (call_dispose);
5818 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5821 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5823 throw new NotSupportedException ();
5827 bool ResolveLoop (BlockContext ec)
5829 return loop.Resolve (ec);
5832 void EmitLoopInit (EmitContext ec)
5834 enumerator.EmitAssign (ec, init);
5837 void EmitLoopBody (EmitContext ec)
5839 loop.Emit (ec);
5842 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5844 enumerator_type = storey.MutateType (enumerator_type);
5845 init.MutateHoistedGenericType (storey);
5846 loop.MutateHoistedGenericType (storey);
5850 Expression type;
5851 Expression variable;
5852 Expression expr;
5853 Statement statement;
5855 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5856 Statement stmt, Location l)
5858 this.type = type;
5859 this.variable = var;
5860 this.expr = expr;
5861 statement = stmt;
5862 loc = l;
5865 public Statement Statement {
5866 get { return statement; }
5869 public override bool Resolve (BlockContext ec)
5871 expr = expr.Resolve (ec);
5872 if (expr == null)
5873 return false;
5875 if (expr.IsNull) {
5876 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5877 return false;
5880 if (expr.Type == TypeManager.string_type) {
5881 statement = new ArrayForeach (this, 1);
5882 } else if (expr.Type.IsArray) {
5883 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5884 } else {
5885 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5886 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5887 expr.ExprClassName);
5888 return false;
5891 statement = new CollectionForeach (type, variable, expr, statement, loc);
5894 return statement.Resolve (ec);
5897 protected override void DoEmit (EmitContext ec)
5899 ILGenerator ig = ec.ig;
5901 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5902 ec.LoopBegin = ig.DefineLabel ();
5903 ec.LoopEnd = ig.DefineLabel ();
5905 statement.Emit (ec);
5907 ec.LoopBegin = old_begin;
5908 ec.LoopEnd = old_end;
5911 protected override void CloneTo (CloneContext clonectx, Statement t)
5913 Foreach target = (Foreach) t;
5915 target.type = type.Clone (clonectx);
5916 target.variable = variable.Clone (clonectx);
5917 target.expr = expr.Clone (clonectx);
5918 target.statement = statement.Clone (clonectx);
5921 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5923 statement.MutateHoistedGenericType (storey);