2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
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 {
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (BlockContext ec)
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
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.
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 ();
60 /// Return value indicates whether all code paths emitted return.
62 protected abstract void DoEmit (EmitContext ec);
64 public virtual void Emit (EmitContext ec)
71 // This routine must be overrided in derived classes and make copies
72 // of all the data that might be modified if resolved
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);
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");
89 public Statement PerformClone ()
91 CloneContext clonectx = new CloneContext ();
93 return Clone (clonectx);
96 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
99 public sealed class EmptyStatement : Statement {
101 private EmptyStatement () {}
103 public static readonly EmptyStatement Value = new EmptyStatement ();
105 public override bool Resolve (BlockContext ec)
110 public override bool ResolveUnreachable (BlockContext ec, bool warn)
115 protected override void DoEmit (EmitContext ec)
119 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
123 protected override void CloneTo (CloneContext clonectx, Statement target)
129 public class If : Statement {
131 public Statement TrueStatement;
132 public Statement FalseStatement;
136 public If (Expression bool_expr, Statement true_statement, Location l)
137 : this (bool_expr, true_statement, null, l)
141 public If (Expression bool_expr,
142 Statement true_statement,
143 Statement false_statement,
146 this.expr = bool_expr;
147 TrueStatement = true_statement;
148 FalseStatement = false_statement;
152 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
154 expr.MutateHoistedGenericType (storey);
155 TrueStatement.MutateHoistedGenericType (storey);
156 if (FalseStatement != null)
157 FalseStatement.MutateHoistedGenericType (storey);
160 public override bool Resolve (BlockContext ec)
164 Report.Debug (1, "START IF BLOCK", loc);
166 expr = expr.Resolve (ec);
171 // Dead code elimination
173 if (expr is Constant) {
174 bool take = !((Constant) expr).IsDefaultValue;
177 if (!TrueStatement.Resolve (ec))
180 if ((FalseStatement != null) &&
181 !FalseStatement.ResolveUnreachable (ec, true))
183 FalseStatement = null;
185 if (!TrueStatement.ResolveUnreachable (ec, true))
187 TrueStatement = null;
189 if ((FalseStatement != null) &&
190 !FalseStatement.Resolve (ec))
198 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
200 ok &= TrueStatement.Resolve (ec);
202 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
204 ec.CurrentBranching.CreateSibling ();
206 if (FalseStatement != null)
207 ok &= FalseStatement.Resolve (ec);
209 ec.EndFlowBranching ();
211 Report.Debug (1, "END IF BLOCK", loc);
216 protected override void DoEmit (EmitContext ec)
218 ILGenerator ig = ec.ig;
219 Label false_target = ig.DefineLabel ();
223 // If we're a boolean constant, Resolve() already
224 // eliminated dead code for us.
226 Constant c = expr as Constant;
228 c.EmitSideEffect (ec);
230 if (!c.IsDefaultValue)
231 TrueStatement.Emit (ec);
232 else if (FalseStatement != null)
233 FalseStatement.Emit (ec);
238 expr.EmitBranchable (ec, false_target, false);
240 TrueStatement.Emit (ec);
242 if (FalseStatement != null){
243 bool branch_emitted = false;
245 end = ig.DefineLabel ();
247 ig.Emit (OpCodes.Br, end);
248 branch_emitted = true;
251 ig.MarkLabel (false_target);
252 FalseStatement.Emit (ec);
257 ig.MarkLabel (false_target);
261 protected override void CloneTo (CloneContext clonectx, Statement t)
265 target.expr = expr.Clone (clonectx);
266 target.TrueStatement = TrueStatement.Clone (clonectx);
267 if (FalseStatement != null)
268 target.FalseStatement = FalseStatement.Clone (clonectx);
272 public class Do : Statement {
273 public Expression expr;
274 public Statement EmbeddedStatement;
276 public Do (Statement statement, BooleanExpression bool_expr, Location l)
279 EmbeddedStatement = statement;
283 public override bool Resolve (BlockContext ec)
287 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
289 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
291 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
292 if (!EmbeddedStatement.Resolve (ec))
294 ec.EndFlowBranching ();
296 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
297 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
299 expr = expr.Resolve (ec);
302 else if (expr is Constant){
303 bool infinite = !((Constant) expr).IsDefaultValue;
305 ec.CurrentBranching.CurrentUsageVector.Goto ();
308 ec.EndFlowBranching ();
313 protected override void DoEmit (EmitContext ec)
315 ILGenerator ig = ec.ig;
316 Label loop = ig.DefineLabel ();
317 Label old_begin = ec.LoopBegin;
318 Label old_end = ec.LoopEnd;
320 ec.LoopBegin = ig.DefineLabel ();
321 ec.LoopEnd = ig.DefineLabel ();
324 EmbeddedStatement.Emit (ec);
325 ig.MarkLabel (ec.LoopBegin);
328 // Dead code elimination
330 if (expr is Constant){
331 bool res = !((Constant) expr).IsDefaultValue;
333 expr.EmitSideEffect (ec);
335 ec.ig.Emit (OpCodes.Br, loop);
337 expr.EmitBranchable (ec, loop, true);
339 ig.MarkLabel (ec.LoopEnd);
341 ec.LoopBegin = old_begin;
342 ec.LoopEnd = old_end;
345 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
347 expr.MutateHoistedGenericType (storey);
348 EmbeddedStatement.MutateHoistedGenericType (storey);
351 protected override void CloneTo (CloneContext clonectx, Statement t)
355 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
356 target.expr = expr.Clone (clonectx);
360 public class While : Statement {
361 public Expression expr;
362 public Statement Statement;
363 bool infinite, empty;
365 public While (BooleanExpression bool_expr, Statement statement, Location l)
367 this.expr = bool_expr;
368 Statement = statement;
372 public override bool Resolve (BlockContext ec)
376 expr = expr.Resolve (ec);
381 // Inform whether we are infinite or not
383 if (expr is Constant){
384 bool value = !((Constant) expr).IsDefaultValue;
387 if (!Statement.ResolveUnreachable (ec, true))
395 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
397 ec.CurrentBranching.CreateSibling ();
399 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
400 if (!Statement.Resolve (ec))
402 ec.EndFlowBranching ();
404 // There's no direct control flow from the end of the embedded statement to the end of the loop
405 ec.CurrentBranching.CurrentUsageVector.Goto ();
407 ec.EndFlowBranching ();
412 protected override void DoEmit (EmitContext ec)
415 expr.EmitSideEffect (ec);
419 ILGenerator ig = ec.ig;
420 Label old_begin = ec.LoopBegin;
421 Label old_end = ec.LoopEnd;
423 ec.LoopBegin = ig.DefineLabel ();
424 ec.LoopEnd = ig.DefineLabel ();
427 // Inform whether we are infinite or not
429 if (expr is Constant){
430 // expr is 'true', since the 'empty' case above handles the 'false' case
431 ig.MarkLabel (ec.LoopBegin);
432 expr.EmitSideEffect (ec);
434 ig.Emit (OpCodes.Br, ec.LoopBegin);
437 // Inform that we are infinite (ie, `we return'), only
438 // if we do not `break' inside the code.
440 ig.MarkLabel (ec.LoopEnd);
442 Label while_loop = ig.DefineLabel ();
444 ig.Emit (OpCodes.Br, ec.LoopBegin);
445 ig.MarkLabel (while_loop);
449 ig.MarkLabel (ec.LoopBegin);
452 expr.EmitBranchable (ec, while_loop, true);
454 ig.MarkLabel (ec.LoopEnd);
457 ec.LoopBegin = old_begin;
458 ec.LoopEnd = old_end;
461 public override void Emit (EmitContext ec)
466 protected override void CloneTo (CloneContext clonectx, Statement t)
468 While target = (While) t;
470 target.expr = expr.Clone (clonectx);
471 target.Statement = Statement.Clone (clonectx);
474 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
476 expr.MutateHoistedGenericType (storey);
477 Statement.MutateHoistedGenericType (storey);
481 public class For : Statement {
483 Statement InitStatement;
485 public Statement Statement;
486 bool infinite, empty;
488 public For (Statement init_statement,
489 BooleanExpression test,
494 InitStatement = init_statement;
496 Increment = increment;
497 Statement = statement;
501 public override bool Resolve (BlockContext ec)
505 if (InitStatement != null){
506 if (!InitStatement.Resolve (ec))
511 Test = Test.Resolve (ec);
514 else if (Test is Constant){
515 bool value = !((Constant) Test).IsDefaultValue;
518 if (!Statement.ResolveUnreachable (ec, true))
520 if ((Increment != null) &&
521 !Increment.ResolveUnreachable (ec, false))
531 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
533 ec.CurrentBranching.CreateSibling ();
535 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
537 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
538 if (!Statement.Resolve (ec))
540 ec.EndFlowBranching ();
542 if (Increment != null){
543 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
544 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
547 if (!Increment.Resolve (ec))
552 // There's no direct control flow from the end of the embedded statement to the end of the loop
553 ec.CurrentBranching.CurrentUsageVector.Goto ();
555 ec.EndFlowBranching ();
560 protected override void DoEmit (EmitContext ec)
562 if (InitStatement != null && InitStatement != EmptyStatement.Value)
563 InitStatement.Emit (ec);
566 Test.EmitSideEffect (ec);
570 ILGenerator ig = ec.ig;
571 Label old_begin = ec.LoopBegin;
572 Label old_end = ec.LoopEnd;
573 Label loop = ig.DefineLabel ();
574 Label test = ig.DefineLabel ();
576 ec.LoopBegin = ig.DefineLabel ();
577 ec.LoopEnd = ig.DefineLabel ();
579 ig.Emit (OpCodes.Br, test);
583 ig.MarkLabel (ec.LoopBegin);
584 if (Increment != EmptyStatement.Value)
589 // If test is null, there is no test, and we are just
594 // The Resolve code already catches the case for
595 // Test == Constant (false) so we know that
598 if (Test is Constant) {
599 Test.EmitSideEffect (ec);
600 ig.Emit (OpCodes.Br, loop);
602 Test.EmitBranchable (ec, loop, true);
606 ig.Emit (OpCodes.Br, loop);
607 ig.MarkLabel (ec.LoopEnd);
609 ec.LoopBegin = old_begin;
610 ec.LoopEnd = old_end;
613 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
615 if (InitStatement != null)
616 InitStatement.MutateHoistedGenericType (storey);
618 Test.MutateHoistedGenericType (storey);
619 if (Increment != null)
620 Increment.MutateHoistedGenericType (storey);
622 Statement.MutateHoistedGenericType (storey);
625 protected override void CloneTo (CloneContext clonectx, Statement t)
627 For target = (For) t;
629 if (InitStatement != null)
630 target.InitStatement = InitStatement.Clone (clonectx);
632 target.Test = Test.Clone (clonectx);
633 if (Increment != null)
634 target.Increment = Increment.Clone (clonectx);
635 target.Statement = Statement.Clone (clonectx);
639 public class StatementExpression : Statement {
640 ExpressionStatement expr;
642 public StatementExpression (ExpressionStatement expr)
648 public override bool Resolve (BlockContext ec)
650 expr = expr.ResolveStatement (ec);
654 protected override void DoEmit (EmitContext ec)
656 expr.EmitStatement (ec);
659 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
661 expr.MutateHoistedGenericType (storey);
664 public override string ToString ()
666 return "StatementExpression (" + expr + ")";
669 protected override void CloneTo (CloneContext clonectx, Statement t)
671 StatementExpression target = (StatementExpression) t;
673 target.expr = (ExpressionStatement) expr.Clone (clonectx);
677 // A 'return' or a 'yield break'
678 public abstract class ExitStatement : Statement
680 protected bool unwind_protect;
681 protected abstract bool DoResolve (BlockContext ec);
683 public virtual void Error_FinallyClause (Report Report)
685 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
688 public sealed override bool Resolve (BlockContext ec)
693 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
695 ec.NeedReturnLabel ();
696 ec.CurrentBranching.CurrentUsageVector.Goto ();
702 /// Implements the return statement
704 public class Return : ExitStatement {
705 protected Expression Expr;
706 public Return (Expression expr, Location l)
712 protected override bool DoResolve (BlockContext ec)
715 if (ec.ReturnType == TypeManager.void_type)
718 ec.Report.Error (126, loc,
719 "An object of a type convertible to `{0}' is required for the return statement",
720 TypeManager.CSharpName (ec.ReturnType));
724 if (ec.CurrentBlock.Toplevel.IsIterator) {
725 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
726 "statement to return a value, or yield break to end the iteration");
729 AnonymousExpression am = ec.CurrentAnonymousMethod;
730 if (am == null && ec.ReturnType == TypeManager.void_type) {
731 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
732 ec.GetSignatureForError ());
735 Expr = Expr.Resolve (ec);
739 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
740 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
744 if (Expr.Type != ec.ReturnType) {
745 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
749 ec.Report.Error (1662, loc,
750 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
751 am.ContainerType, am.GetSignatureForError ());
760 protected override void DoEmit (EmitContext ec)
766 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
770 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
772 ec.ig.Emit (OpCodes.Ret);
775 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
778 Expr.MutateHoistedGenericType (storey);
781 protected override void CloneTo (CloneContext clonectx, Statement t)
783 Return target = (Return) t;
784 // It's null for simple return;
786 target.Expr = Expr.Clone (clonectx);
790 public class Goto : Statement {
792 LabeledStatement label;
795 public override bool Resolve (BlockContext ec)
797 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
798 ec.CurrentBranching.CurrentUsageVector.Goto ();
802 public Goto (string label, Location l)
808 public string Target {
809 get { return target; }
812 public void SetResolvedTarget (LabeledStatement label)
815 label.AddReference ();
818 protected override void CloneTo (CloneContext clonectx, Statement target)
823 protected override void DoEmit (EmitContext ec)
826 throw new InternalErrorException ("goto emitted before target resolved");
827 Label l = label.LabelTarget (ec);
828 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
831 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
836 public class LabeledStatement : Statement {
843 FlowBranching.UsageVector vectors;
845 public LabeledStatement (string name, Location l)
851 public Label LabelTarget (EmitContext ec)
856 label = ec.ig.DefineLabel ();
866 public bool IsDefined {
867 get { return defined; }
870 public bool HasBeenReferenced {
871 get { return referenced; }
874 public FlowBranching.UsageVector JumpOrigins {
875 get { return vectors; }
878 public void AddUsageVector (FlowBranching.UsageVector vector)
880 vector = vector.Clone ();
881 vector.Next = vectors;
885 protected override void CloneTo (CloneContext clonectx, Statement target)
890 public override bool Resolve (BlockContext ec)
892 // this flow-branching will be terminated when the surrounding block ends
893 ec.StartFlowBranching (this);
897 protected override void DoEmit (EmitContext ec)
899 if (ig != null && ig != ec.ig)
900 throw new InternalErrorException ("cannot happen");
902 ec.ig.MarkLabel (label);
905 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
909 public void AddReference ()
917 /// `goto default' statement
919 public class GotoDefault : Statement {
921 public GotoDefault (Location l)
926 protected override void CloneTo (CloneContext clonectx, Statement target)
931 public override bool Resolve (BlockContext ec)
933 ec.CurrentBranching.CurrentUsageVector.Goto ();
935 if (ec.Switch == null) {
936 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
940 if (!ec.Switch.GotDefault) {
941 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
948 protected override void DoEmit (EmitContext ec)
950 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
953 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
959 /// `goto case' statement
961 public class GotoCase : Statement {
965 public GotoCase (Expression e, Location l)
971 public override bool Resolve (BlockContext ec)
973 if (ec.Switch == null){
974 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
978 ec.CurrentBranching.CurrentUsageVector.Goto ();
980 expr = expr.Resolve (ec);
984 Constant c = expr as Constant;
986 ec.Report.Error (150, expr.Location, "A constant value is expected");
990 Type type = ec.Switch.SwitchType;
991 Constant res = c.TryReduce (ec, type, c.Location);
993 c.Error_ValueCannotBeConverted (ec, loc, type, true);
997 if (!Convert.ImplicitStandardConversionExists (c, type))
998 ec.Report.Warning (469, 2, loc,
999 "The `goto case' value is not implicitly convertible to type `{0}'",
1000 TypeManager.CSharpName (type));
1002 object val = res.GetValue ();
1004 val = SwitchLabel.NullStringCase;
1006 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1007 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1008 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1015 protected override void DoEmit (EmitContext ec)
1017 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1020 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1022 expr.MutateHoistedGenericType (storey);
1025 protected override void CloneTo (CloneContext clonectx, Statement t)
1027 GotoCase target = (GotoCase) t;
1029 target.expr = expr.Clone (clonectx);
1033 public class Throw : Statement {
1036 public Throw (Expression expr, Location l)
1042 public override bool Resolve (BlockContext ec)
1045 ec.CurrentBranching.CurrentUsageVector.Goto ();
1046 return ec.CurrentBranching.CheckRethrow (loc);
1049 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1050 ec.CurrentBranching.CurrentUsageVector.Goto ();
1055 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1056 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1058 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1063 protected override void DoEmit (EmitContext ec)
1066 ec.ig.Emit (OpCodes.Rethrow);
1070 ec.ig.Emit (OpCodes.Throw);
1074 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1077 expr.MutateHoistedGenericType (storey);
1080 protected override void CloneTo (CloneContext clonectx, Statement t)
1082 Throw target = (Throw) t;
1085 target.expr = expr.Clone (clonectx);
1089 public class Break : Statement {
1091 public Break (Location l)
1096 bool unwind_protect;
1098 public override bool Resolve (BlockContext ec)
1100 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1101 ec.CurrentBranching.CurrentUsageVector.Goto ();
1105 protected override void DoEmit (EmitContext ec)
1107 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1110 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1114 protected override void CloneTo (CloneContext clonectx, Statement t)
1120 public class Continue : Statement {
1122 public Continue (Location l)
1127 bool unwind_protect;
1129 public override bool Resolve (BlockContext ec)
1131 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1132 ec.CurrentBranching.CurrentUsageVector.Goto ();
1136 protected override void DoEmit (EmitContext ec)
1138 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1141 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1145 protected override void CloneTo (CloneContext clonectx, Statement t)
1151 public interface ILocalVariable
1153 void Emit (EmitContext ec);
1154 void EmitAssign (EmitContext ec);
1155 void EmitAddressOf (EmitContext ec);
1158 public interface IKnownVariable {
1159 Block Block { get; }
1160 Location Location { get; }
1164 // The information about a user-perceived local variable
1166 public class LocalInfo : IKnownVariable, ILocalVariable {
1167 public readonly FullNamedExpression Type;
1169 public Type VariableType;
1170 public readonly string Name;
1171 public readonly Location Location;
1172 public readonly Block Block;
1174 public VariableInfo VariableInfo;
1175 HoistedVariable hoisted_variant;
1184 CompilerGenerated = 64,
1188 public enum ReadOnlyContext: byte {
1195 ReadOnlyContext ro_context;
1196 LocalBuilder builder;
1198 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1206 public LocalInfo (DeclSpace ds, Block block, Location l)
1208 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1213 public void ResolveVariable (EmitContext ec)
1215 if (HoistedVariant != null)
1218 if (builder == null) {
1219 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType), Pinned);
1223 public void Emit (EmitContext ec)
1225 ec.ig.Emit (OpCodes.Ldloc, builder);
1228 public void EmitAssign (EmitContext ec)
1230 ec.ig.Emit (OpCodes.Stloc, builder);
1233 public void EmitAddressOf (EmitContext ec)
1235 ec.ig.Emit (OpCodes.Ldloca, builder);
1238 public void EmitSymbolInfo (EmitContext ec)
1240 if (builder != null)
1241 ec.DefineLocalVariable (Name, builder);
1245 // Hoisted local variable variant
1247 public HoistedVariable HoistedVariant {
1249 return hoisted_variant;
1252 hoisted_variant = value;
1256 public bool IsThisAssigned (BlockContext ec, Block block)
1258 if (VariableInfo == null)
1259 throw new Exception ();
1261 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1264 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1267 public bool IsAssigned (BlockContext ec)
1269 if (VariableInfo == null)
1270 throw new Exception ();
1272 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1275 public bool Resolve (ResolveContext ec)
1277 if (VariableType != null)
1280 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1284 VariableType = texpr.Type;
1286 if (TypeManager.IsGenericParameter (VariableType))
1289 if (VariableType.IsAbstract && VariableType.IsSealed) {
1290 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1294 if (VariableType.IsPointer && !ec.IsUnsafe)
1295 Expression.UnsafeError (ec, Location);
1300 public bool IsConstant {
1301 get { return (flags & Flags.IsConstant) != 0; }
1302 set { flags |= Flags.IsConstant; }
1305 public bool AddressTaken {
1306 get { return (flags & Flags.AddressTaken) != 0; }
1307 set { flags |= Flags.AddressTaken; }
1310 public bool CompilerGenerated {
1311 get { return (flags & Flags.CompilerGenerated) != 0; }
1312 set { flags |= Flags.CompilerGenerated; }
1315 public override string ToString ()
1317 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1318 Name, Type, VariableInfo, Location);
1322 get { return (flags & Flags.Used) != 0; }
1323 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1326 public bool ReadOnly {
1327 get { return (flags & Flags.ReadOnly) != 0; }
1330 public void SetReadOnlyContext (ReadOnlyContext context)
1332 flags |= Flags.ReadOnly;
1333 ro_context = context;
1336 public string GetReadOnlyContext ()
1339 throw new InternalErrorException ("Variable is not readonly");
1341 switch (ro_context) {
1342 case ReadOnlyContext.Fixed:
1343 return "fixed variable";
1344 case ReadOnlyContext.Foreach:
1345 return "foreach iteration variable";
1346 case ReadOnlyContext.Using:
1347 return "using variable";
1349 throw new NotImplementedException ();
1353 // Whether the variable is pinned, if Pinned the variable has been
1354 // allocated in a pinned slot with DeclareLocal.
1356 public bool Pinned {
1357 get { return (flags & Flags.Pinned) != 0; }
1358 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1361 public bool IsThis {
1362 get { return (flags & Flags.IsThis) != 0; }
1363 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1366 Block IKnownVariable.Block {
1367 get { return Block; }
1370 Location IKnownVariable.Location {
1371 get { return Location; }
1374 public LocalInfo Clone (CloneContext clonectx)
1377 // Variables in anonymous block are not resolved yet
1379 if (VariableType == null)
1380 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1383 // Variables in method block are resolved
1385 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1386 li.VariableType = VariableType;
1392 /// Block represents a C# block.
1396 /// This class is used in a number of places: either to represent
1397 /// explicit blocks that the programmer places or implicit blocks.
1399 /// Implicit blocks are used as labels or to introduce variable
1402 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1403 /// they contain extra information that is not necessary on normal blocks.
1405 public class Block : Statement {
1406 public Block Parent;
1407 public Location StartLocation;
1408 public Location EndLocation = Location.Null;
1410 public ExplicitBlock Explicit;
1411 public ToplevelBlock Toplevel; // TODO: Use Explicit
1418 VariablesInitialized = 4,
1422 HasCapturedVariable = 64,
1423 HasCapturedThis = 1 << 7,
1424 IsExpressionTree = 1 << 8
1427 protected Flags flags;
1429 public bool Unchecked {
1430 get { return (flags & Flags.Unchecked) != 0; }
1431 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1434 public bool Unsafe {
1435 get { return (flags & Flags.Unsafe) != 0; }
1436 set { flags |= Flags.Unsafe; }
1440 // The statements in this block
1442 protected List<Statement> statements;
1445 // An array of Blocks. We keep track of children just
1446 // to generate the local variable declarations.
1448 // Statements and child statements are handled through the
1451 List<Block> children;
1454 // Labels. (label, block) pairs.
1456 protected Dictionary<string, LabeledStatement> labels;
1459 // Keeps track of (name, type) pairs
1461 Dictionary<string, LocalInfo> variables;
1464 // Keeps track of constants
1465 Dictionary<string, Expression> constants;
1468 // Temporary variables.
1470 List<LocalInfo> temporary_variables;
1473 // If this is a switch section, the enclosing switch block.
1477 protected List<Statement> scope_initializers;
1479 List<ToplevelBlock> anonymous_children;
1481 protected static int id;
1485 int assignable_slots;
1486 bool unreachable_shown;
1489 public Block (Block parent)
1490 : this (parent, (Flags) 0, Location.Null, Location.Null)
1493 public Block (Block parent, Flags flags)
1494 : this (parent, flags, Location.Null, Location.Null)
1497 public Block (Block parent, Location start, Location end)
1498 : this (parent, (Flags) 0, start, end)
1502 // Useful when TopLevel block is downgraded to normal block
1504 public Block (ToplevelBlock parent, ToplevelBlock source)
1505 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1507 statements = source.statements;
1508 children = source.children;
1509 labels = source.labels;
1510 variables = source.variables;
1511 constants = source.constants;
1512 switch_block = source.switch_block;
1515 public Block (Block parent, Flags flags, Location start, Location end)
1517 if (parent != null) {
1518 parent.AddChild (this);
1520 // the appropriate constructors will fixup these fields
1521 Toplevel = parent.Toplevel;
1522 Explicit = parent.Explicit;
1525 this.Parent = parent;
1527 this.StartLocation = start;
1528 this.EndLocation = end;
1531 statements = new List<Statement> (4);
1534 public Block CreateSwitchBlock (Location start)
1536 // FIXME: should this be implicit?
1537 Block new_block = new ExplicitBlock (this, start, start);
1538 new_block.switch_block = this;
1543 get { return this_id; }
1546 public IDictionary<string, LocalInfo> Variables {
1548 if (variables == null)
1549 variables = new Dictionary<string, LocalInfo> ();
1554 void AddChild (Block b)
1556 if (children == null)
1557 children = new List<Block> (1);
1562 public void SetEndLocation (Location loc)
1567 protected void Error_158 (string name, Location loc)
1569 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1570 "by the same name in a contained scope", name);
1574 /// Adds a label to the current block.
1578 /// false if the name already exists in this block. true
1582 public bool AddLabel (LabeledStatement target)
1584 if (switch_block != null)
1585 return switch_block.AddLabel (target);
1587 string name = target.Name;
1590 while (cur != null) {
1591 LabeledStatement s = cur.DoLookupLabel (name);
1593 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1594 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1598 if (this == Explicit)
1604 while (cur != null) {
1605 if (cur.DoLookupLabel (name) != null) {
1606 Error_158 (name, target.loc);
1610 if (children != null) {
1611 foreach (Block b in children) {
1612 LabeledStatement s = b.DoLookupLabel (name);
1616 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1617 Error_158 (name, target.loc);
1625 Toplevel.CheckError158 (name, target.loc);
1628 labels = new Dictionary<string, LabeledStatement> ();
1630 labels.Add (name, target);
1634 public LabeledStatement LookupLabel (string name)
1636 LabeledStatement s = DoLookupLabel (name);
1640 if (children == null)
1643 foreach (Block child in children) {
1644 if (Explicit != child.Explicit)
1647 s = child.LookupLabel (name);
1655 LabeledStatement DoLookupLabel (string name)
1657 if (switch_block != null)
1658 return switch_block.LookupLabel (name);
1661 if (labels.ContainsKey (name))
1662 return labels [name];
1667 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1670 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1671 while (kvi == null) {
1672 b = b.Explicit.Parent;
1675 kvi = b.Explicit.GetKnownVariable (name);
1681 // Is kvi.Block nested inside 'b'
1682 if (b.Explicit != kvi.Block.Explicit) {
1684 // If a variable by the same name it defined in a nested block of this
1685 // block, we violate the invariant meaning in a block.
1688 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1689 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1694 // It's ok if the definition is in a nested subblock of b, but not
1695 // nested inside this block -- a definition in a sibling block
1696 // should not affect us.
1702 // Block 'b' and kvi.Block are the same textual block.
1703 // However, different variables are extant.
1705 // Check if the variable is in scope in both blocks. We use
1706 // an indirect check that depends on AddVariable doing its
1707 // part in maintaining the invariant-meaning-in-block property.
1709 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1712 if (this is ToplevelBlock) {
1713 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1714 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1719 // Even though we detected the error when the name is used, we
1720 // treat it as if the variable declaration was in error.
1722 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1723 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1727 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1729 LocalInfo vi = GetLocalInfo (name);
1731 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1732 if (Explicit == vi.Block.Explicit) {
1733 Error_AlreadyDeclared (l, name, null);
1735 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1736 "parent or current" : "parent");
1741 if (block != null) {
1742 Expression e = block.GetParameterReference (name, Location.Null);
1744 ParameterReference pr = e as ParameterReference;
1745 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1746 Error_AlreadyDeclared (loc, name);
1748 Error_AlreadyDeclared (loc, name, "parent or current");
1756 public LocalInfo AddVariable (Expression type, string name, Location l)
1758 if (!CheckParentConflictName (Toplevel, name, l))
1761 if (Toplevel.GenericMethod != null) {
1762 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1763 if (tp.Name == name) {
1764 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1765 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1771 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1773 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1774 Error_AlreadyDeclared (l, name, "child");
1778 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1781 if ((flags & Flags.VariablesInitialized) != 0)
1782 throw new InternalErrorException ("block has already been resolved");
1787 protected virtual void AddVariable (LocalInfo li)
1789 Variables.Add (li.Name, li);
1790 Explicit.AddKnownVariable (li.Name, li);
1793 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1795 if (reason == null) {
1796 Error_AlreadyDeclared (loc, var);
1800 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1801 "in this scope because it would give a different meaning " +
1802 "to `{0}', which is already used in a `{1}' scope " +
1803 "to denote something else", var, reason);
1806 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1808 Toplevel.Report.Error (128, loc,
1809 "A local variable named `{0}' is already defined in this scope", name);
1812 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1814 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1818 public bool AddConstant (Expression type, string name, Expression value, Location l)
1820 if (AddVariable (type, name, l) == null)
1823 if (constants == null)
1824 constants = new Dictionary<string, Expression> ();
1826 constants.Add (name, value);
1828 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1833 static int next_temp_id = 0;
1835 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1837 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1839 if (temporary_variables == null)
1840 temporary_variables = new List<LocalInfo> ();
1842 int id = ++next_temp_id;
1843 string name = "$s_" + id.ToString ();
1845 LocalInfo li = new LocalInfo (te, name, this, loc);
1846 li.CompilerGenerated = true;
1847 temporary_variables.Add (li);
1851 public LocalInfo GetLocalInfo (string name)
1854 for (Block b = this; b != null; b = b.Parent) {
1855 if (b.variables != null) {
1856 if (b.variables.TryGetValue (name, out ret))
1864 public Expression GetVariableType (string name)
1866 LocalInfo vi = GetLocalInfo (name);
1867 return vi == null ? null : vi.Type;
1870 public Expression GetConstantExpression (string name)
1873 for (Block b = this; b != null; b = b.Parent) {
1874 if (b.constants != null) {
1875 if (b.constants.TryGetValue (name, out ret))
1883 // It should be used by expressions which require to
1884 // register a statement during resolve process.
1886 public void AddScopeStatement (Statement s)
1888 if (scope_initializers == null)
1889 scope_initializers = new List<Statement> ();
1891 scope_initializers.Add (s);
1894 public void AddStatement (Statement s)
1897 flags |= Flags.BlockUsed;
1901 get { return (flags & Flags.BlockUsed) != 0; }
1906 flags |= Flags.BlockUsed;
1909 public bool HasRet {
1910 get { return (flags & Flags.HasRet) != 0; }
1913 public int AssignableSlots {
1916 // if ((flags & Flags.VariablesInitialized) == 0)
1917 // throw new Exception ("Variables have not been initialized yet");
1918 return assignable_slots;
1922 public IList<ToplevelBlock> AnonymousChildren {
1923 get { return anonymous_children; }
1926 public void AddAnonymousChild (ToplevelBlock b)
1928 if (anonymous_children == null)
1929 anonymous_children = new List<ToplevelBlock> ();
1931 anonymous_children.Add (b);
1934 void DoResolveConstants (BlockContext ec)
1936 if (constants == null)
1939 if (variables == null)
1940 throw new InternalErrorException ("cannot happen");
1942 foreach (var de in variables) {
1943 string name = de.Key;
1944 LocalInfo vi = de.Value;
1945 Type variable_type = vi.VariableType;
1947 if (variable_type == null) {
1948 if (vi.Type is VarExpr)
1949 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1955 if (!constants.TryGetValue (name, out cv))
1958 // Don't let 'const int Foo = Foo;' succeed.
1959 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1960 // which in turn causes the 'must be constant' error to be triggered.
1961 constants.Remove (name);
1963 if (!Const.IsConstantTypeValid (variable_type)) {
1964 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1968 ec.CurrentBlock = this;
1970 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1971 e = cv.Resolve (ec);
1976 Constant ce = e as Constant;
1978 Const.Error_ExpressionMustBeConstant (vi.Location, name, ec.Report);
1982 e = ce.ConvertImplicitly (ec, variable_type);
1984 if (TypeManager.IsReferenceType (variable_type))
1985 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name, ec.Report);
1987 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1991 constants.Add (name, e);
1992 vi.IsConstant = true;
1996 protected void ResolveMeta (BlockContext ec, int offset)
1998 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2000 // If some parent block was unsafe, we remain unsafe even if this block
2001 // isn't explicitly marked as such.
2002 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2003 flags |= Flags.VariablesInitialized;
2005 if (variables != null) {
2006 foreach (LocalInfo li in variables.Values) {
2007 if (!li.Resolve (ec))
2009 li.VariableInfo = new VariableInfo (li, offset);
2010 offset += li.VariableInfo.Length;
2013 assignable_slots = offset;
2015 DoResolveConstants (ec);
2017 if (children == null)
2019 foreach (Block b in children)
2020 b.ResolveMeta (ec, offset);
2025 // Emits the local variable declarations for a block
2027 public virtual void EmitMeta (EmitContext ec)
2029 if (variables != null){
2030 foreach (LocalInfo vi in variables.Values)
2031 vi.ResolveVariable (ec);
2034 if (temporary_variables != null) {
2035 for (int i = 0; i < temporary_variables.Count; i++)
2036 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2039 if (children != null) {
2040 for (int i = 0; i < children.Count; i++)
2041 ((Block)children[i]).EmitMeta(ec);
2045 void UsageWarning (BlockContext ec)
2047 if (variables == null || ec.Report.WarningLevel < 3)
2050 foreach (var de in variables) {
2051 LocalInfo vi = de.Value;
2054 string name = de.Key;
2056 // vi.VariableInfo can be null for 'catch' variables
2057 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2058 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2060 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2065 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2069 // Some statements are wrapped by a Block. Since
2070 // others' internal could be changed, here I treat
2071 // them as possibly wrapped by Block equally.
2072 Block b = s as Block;
2073 if (b != null && b.statements.Count == 1)
2074 s = (Statement) b.statements [0];
2077 body = ((Lock) s).Statement;
2079 body = ((For) s).Statement;
2080 else if (s is Foreach)
2081 body = ((Foreach) s).Statement;
2082 else if (s is While)
2083 body = ((While) s).Statement;
2084 else if (s is Fixed)
2085 body = ((Fixed) s).Statement;
2086 else if (s is Using)
2087 body = ((Using) s).EmbeddedStatement;
2088 else if (s is UsingTemporary)
2089 body = ((UsingTemporary) s).Statement;
2093 if (body == null || body is EmptyStatement)
2094 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2097 public override bool Resolve (BlockContext ec)
2099 Block prev_block = ec.CurrentBlock;
2102 int errors = ec.Report.Errors;
2104 ec.CurrentBlock = this;
2105 ec.StartFlowBranching (this);
2107 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2110 // Compiler generated scope statements
2112 if (scope_initializers != null) {
2113 foreach (Statement s in scope_initializers)
2118 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2119 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2120 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2121 // responsible for handling the situation.
2123 int statement_count = statements.Count;
2124 for (int ix = 0; ix < statement_count; ix++){
2125 Statement s = statements [ix];
2126 // Check possible empty statement (CS0642)
2127 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2128 statements [ix + 1] is ExplicitBlock)
2129 CheckPossibleMistakenEmptyStatement (ec, s);
2132 // Warn if we detect unreachable code.
2135 if (s is EmptyStatement)
2138 if (!unreachable_shown && !(s is LabeledStatement)) {
2139 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2140 unreachable_shown = true;
2143 Block c_block = s as Block;
2144 if (c_block != null)
2145 c_block.unreachable = c_block.unreachable_shown = true;
2149 // Note that we're not using ResolveUnreachable() for unreachable
2150 // statements here. ResolveUnreachable() creates a temporary
2151 // flow branching and kills it afterwards. This leads to problems
2152 // if you have two unreachable statements where the first one
2153 // assigns a variable and the second one tries to access it.
2156 if (!s.Resolve (ec)) {
2158 if (ec.IsInProbingMode)
2161 statements [ix] = EmptyStatement.Value;
2165 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2166 statements [ix] = EmptyStatement.Value;
2168 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2169 if (unreachable && s is LabeledStatement)
2170 throw new InternalErrorException ("should not happen");
2173 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2174 ec.CurrentBranching, statement_count);
2176 while (ec.CurrentBranching is FlowBranchingLabeled)
2177 ec.EndFlowBranching ();
2179 bool flow_unreachable = ec.EndFlowBranching ();
2181 ec.CurrentBlock = prev_block;
2183 if (flow_unreachable)
2184 flags |= Flags.HasRet;
2186 // If we're a non-static `struct' constructor which doesn't have an
2187 // initializer, then we must initialize all of the struct's fields.
2188 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2191 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2192 foreach (LabeledStatement label in labels.Values)
2193 if (!label.HasBeenReferenced)
2194 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2197 if (ok && errors == ec.Report.Errors)
2203 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2205 unreachable_shown = true;
2209 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2211 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2212 bool ok = Resolve (ec);
2213 ec.KillFlowBranching ();
2218 protected override void DoEmit (EmitContext ec)
2220 for (int ix = 0; ix < statements.Count; ix++){
2221 Statement s = (Statement) statements [ix];
2226 public override void Emit (EmitContext ec)
2228 if (scope_initializers != null)
2229 EmitScopeInitializers (ec);
2231 ec.Mark (StartLocation);
2234 if (SymbolWriter.HasSymbolWriter)
2235 EmitSymbolInfo (ec);
2238 protected void EmitScopeInitializers (EmitContext ec)
2240 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2242 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2243 foreach (Statement s in scope_initializers)
2247 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2250 protected virtual void EmitSymbolInfo (EmitContext ec)
2252 if (variables != null) {
2253 foreach (LocalInfo vi in variables.Values) {
2254 vi.EmitSymbolInfo (ec);
2259 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2261 MutateVariables (storey);
2263 if (scope_initializers != null) {
2264 foreach (Statement s in scope_initializers)
2265 s.MutateHoistedGenericType (storey);
2268 foreach (Statement s in statements)
2269 s.MutateHoistedGenericType (storey);
2272 void MutateVariables (AnonymousMethodStorey storey)
2274 if (variables != null) {
2275 foreach (LocalInfo vi in variables.Values) {
2276 vi.VariableType = storey.MutateType (vi.VariableType);
2280 if (temporary_variables != null) {
2281 foreach (LocalInfo vi in temporary_variables)
2282 vi.VariableType = storey.MutateType (vi.VariableType);
2286 public override string ToString ()
2288 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2291 protected override void CloneTo (CloneContext clonectx, Statement t)
2293 Block target = (Block) t;
2295 clonectx.AddBlockMap (this, target);
2297 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2298 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2300 target.Parent = clonectx.RemapBlockCopy (Parent);
2302 if (variables != null){
2303 target.variables = new Dictionary<string, LocalInfo> ();
2305 foreach (var de in variables){
2306 LocalInfo newlocal = de.Value.Clone (clonectx);
2307 target.variables [de.Key] = newlocal;
2308 clonectx.AddVariableMap (de.Value, newlocal);
2312 target.statements = new List<Statement> (statements.Count);
2313 foreach (Statement s in statements)
2314 target.statements.Add (s.Clone (clonectx));
2316 if (target.children != null){
2317 target.children = new List<Block> (children.Count);
2318 foreach (Block b in children){
2319 target.children.Add (clonectx.LookupBlock (b));
2324 // TODO: labels, switch_block, constants (?), anonymous_children
2329 public class ExplicitBlock : Block
2331 Dictionary<string, IKnownVariable> known_variables;
2332 protected AnonymousMethodStorey am_storey;
2334 public ExplicitBlock (Block parent, Location start, Location end)
2335 : this (parent, (Flags) 0, start, end)
2339 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2340 : base (parent, flags, start, end)
2342 this.Explicit = this;
2346 // Marks a variable with name @name as being used in this or a child block.
2347 // If a variable name has been used in a child block, it's illegal to
2348 // declare a variable with the same name in the current block.
2350 internal void AddKnownVariable (string name, IKnownVariable info)
2352 if (known_variables == null)
2353 known_variables = new Dictionary<string, IKnownVariable> ();
2355 known_variables [name] = info;
2358 Parent.Explicit.AddKnownVariable (name, info);
2361 public AnonymousMethodStorey AnonymousMethodStorey {
2362 get { return am_storey; }
2366 // Creates anonymous method storey in current block
2368 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2371 // When referencing a variable in iterator storey from children anonymous method
2373 if (Toplevel.am_storey is IteratorStorey) {
2374 return Toplevel.am_storey;
2378 // An iterator has only 1 storey block
2380 if (ec.CurrentIterator != null)
2381 return ec.CurrentIterator.Storey;
2383 if (am_storey == null) {
2384 MemberBase mc = ec.MemberContext as MemberBase;
2385 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2388 // Creates anonymous method storey for this block
2390 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2396 public override void Emit (EmitContext ec)
2398 if (am_storey != null)
2399 am_storey.EmitStoreyInstantiation (ec);
2401 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2402 if (emit_debug_info)
2407 if (emit_debug_info)
2411 public override void EmitMeta (EmitContext ec)
2414 // Creates anonymous method storey
2416 if (am_storey != null) {
2417 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2419 // Creates parent storey reference when hoisted this is accessible
2421 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2422 ExplicitBlock parent = Toplevel.Parent.Explicit;
2425 // Hoisted this exists in top-level parent storey only
2427 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2428 parent = parent.Parent.Explicit;
2430 am_storey.AddParentStoreyReference (parent.am_storey);
2433 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2436 am_storey.DefineType ();
2437 am_storey.ResolveType ();
2438 am_storey.Define ();
2439 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2441 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2442 if (ref_blocks != null) {
2443 foreach (ExplicitBlock ref_block in ref_blocks) {
2444 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2445 if (b.am_storey != null) {
2446 b.am_storey.AddParentStoreyReference (am_storey);
2448 // Stop propagation inside same top block
2449 if (b.Toplevel == Toplevel)
2454 b.HasCapturedVariable = true;
2463 internal IKnownVariable GetKnownVariable (string name)
2465 if (known_variables == null)
2469 if (!known_variables.TryGetValue (name, out kw))
2475 public bool HasCapturedThis
2477 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2478 get { return (flags & Flags.HasCapturedThis) != 0; }
2481 public bool HasCapturedVariable
2483 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2484 get { return (flags & Flags.HasCapturedVariable) != 0; }
2487 protected override void CloneTo (CloneContext clonectx, Statement t)
2489 ExplicitBlock target = (ExplicitBlock) t;
2490 target.known_variables = null;
2491 base.CloneTo (clonectx, t);
2495 public class ToplevelParameterInfo : IKnownVariable {
2496 public readonly ToplevelBlock Block;
2497 public readonly int Index;
2498 public VariableInfo VariableInfo;
2500 Block IKnownVariable.Block {
2501 get { return Block; }
2503 public Parameter Parameter {
2504 get { return Block.Parameters [Index]; }
2507 public Type ParameterType {
2508 get { return Block.Parameters.Types [Index]; }
2511 public Location Location {
2512 get { return Parameter.Location; }
2515 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2523 // A toplevel block contains extra information, the split is done
2524 // only to separate information that would otherwise bloat the more
2525 // lightweight Block.
2527 // In particular, this was introduced when the support for Anonymous
2528 // Methods was implemented.
2530 public class ToplevelBlock : ExplicitBlock
2533 // Block is converted to an expression
2535 sealed class BlockScopeExpression : Expression
2538 readonly ToplevelBlock block;
2540 public BlockScopeExpression (Expression child, ToplevelBlock block)
2546 public override Expression CreateExpressionTree (ResolveContext ec)
2548 throw new NotSupportedException ();
2551 protected override Expression DoResolve (ResolveContext ec)
2556 child = child.Resolve (ec);
2560 eclass = child.eclass;
2565 public override void Emit (EmitContext ec)
2567 block.EmitMeta (ec);
2568 block.EmitScopeInitializers (ec);
2572 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2574 type = storey.MutateType (type);
2575 child.MutateHoistedGenericType (storey);
2576 block.MutateHoistedGenericType (storey);
2580 GenericMethod generic;
2581 protected ParametersCompiled parameters;
2582 ToplevelParameterInfo[] parameter_info;
2583 LocalInfo this_variable;
2586 CompilerContext compiler;
2588 public HoistedVariable HoistedThisVariable;
2590 public bool Resolved {
2597 // The parameters for the block.
2599 public ParametersCompiled Parameters {
2600 get { return parameters; }
2603 public Report Report {
2604 get { return compiler.Report; }
2607 public GenericMethod GenericMethod {
2608 get { return generic; }
2611 public ToplevelBlock Container {
2612 get { return Parent == null ? null : Parent.Toplevel; }
2615 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2616 this (ctx, parent, (Flags) 0, parameters, start)
2620 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2621 this (ctx, parent, parameters, start)
2623 this.generic = generic;
2626 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2627 this (ctx, null, (Flags) 0, parameters, start)
2631 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2632 this (ctx, null, flags, parameters, start)
2636 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2637 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2638 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2639 base (null, flags, start, Location.Null)
2641 this.compiler = ctx;
2642 this.Toplevel = this;
2644 this.parameters = parameters;
2645 this.Parent = parent;
2647 parent.AddAnonymousChild (this);
2649 if (!this.parameters.IsEmpty)
2650 ProcessParameters ();
2653 public ToplevelBlock (CompilerContext ctx, Location loc)
2654 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2658 protected override void CloneTo (CloneContext clonectx, Statement t)
2660 ToplevelBlock target = (ToplevelBlock) t;
2661 base.CloneTo (clonectx, t);
2663 if (parameters.Count != 0)
2664 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2665 for (int i = 0; i < parameters.Count; ++i)
2666 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2669 public bool CheckError158 (string name, Location loc)
2671 if (AnonymousChildren != null) {
2672 foreach (ToplevelBlock child in AnonymousChildren) {
2673 if (!child.CheckError158 (name, loc))
2678 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2679 if (!c.DoCheckError158 (name, loc))
2686 void ProcessParameters ()
2688 int n = parameters.Count;
2689 parameter_info = new ToplevelParameterInfo [n];
2690 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2691 for (int i = 0; i < n; ++i) {
2692 parameter_info [i] = new ToplevelParameterInfo (this, i);
2694 Parameter p = parameters [i];
2698 string name = p.Name;
2699 if (CheckParentConflictName (top_parent, name, loc))
2700 AddKnownVariable (name, parameter_info [i]);
2703 // mark this block as "used" so that we create local declarations in a sub-block
2704 // FIXME: This appears to uncover a lot of bugs
2708 bool DoCheckError158 (string name, Location loc)
2710 LabeledStatement s = LookupLabel (name);
2712 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2713 Error_158 (name, loc);
2720 public override Expression CreateExpressionTree (ResolveContext ec)
2722 if (statements.Count == 1) {
2723 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2724 if (scope_initializers != null)
2725 expr = new BlockScopeExpression (expr, this);
2730 return base.CreateExpressionTree (ec);
2734 // Reformats this block to be top-level iterator block
2736 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2740 // Creates block with original statements
2741 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2743 source.statements = new List<Statement> (1);
2744 source.AddStatement (new Return (iterator, iterator.Location));
2745 source.IsIterator = false;
2747 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2748 source.am_storey = iterator_storey;
2749 return iterator_storey;
2753 // Returns a parameter reference expression for the given name,
2754 // or null if there is no such parameter
2756 public Expression GetParameterReference (string name, Location loc)
2758 for (ToplevelBlock t = this; t != null; t = t.Container) {
2759 Expression expr = t.GetParameterReferenceExpression (name, loc);
2767 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2769 int idx = parameters.GetParameterIndexByName (name);
2771 null : new ParameterReference (parameter_info [idx], loc);
2775 // Returns the "this" instance variable of this block.
2776 // See AddThisVariable() for more information.
2778 public LocalInfo ThisVariable {
2779 get { return this_variable; }
2783 // This is used by non-static `struct' constructors which do not have an
2784 // initializer - in this case, the constructor must initialize all of the
2785 // struct's fields. To do this, we add a "this" variable and use the flow
2786 // analysis code to ensure that it's been fully initialized before control
2787 // leaves the constructor.
2789 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2791 if (this_variable == null) {
2792 this_variable = new LocalInfo (ds, this, l);
2793 this_variable.Used = true;
2794 this_variable.IsThis = true;
2796 Variables.Add ("this", this_variable);
2799 return this_variable;
2802 public bool IsIterator {
2803 get { return (flags & Flags.IsIterator) != 0; }
2804 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2808 // Block has been converted to expression tree
2810 public bool IsExpressionTree {
2811 get { return (flags & Flags.IsExpressionTree) != 0; }
2814 public bool IsThisAssigned (BlockContext ec)
2816 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2819 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2826 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2827 flags |= Flags.IsExpressionTree;
2830 if (!ResolveMeta (rc, ip))
2833 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2834 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2839 unreachable = top_level.End ();
2841 } catch (Exception) {
2843 if (rc.CurrentBlock != null) {
2844 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2846 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2852 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2853 if (rc.CurrentAnonymousMethod == null) {
2854 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2856 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2857 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2858 rc.CurrentAnonymousMethod.GetSignatureForError ());
2866 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2868 int errors = ec.Report.Errors;
2869 int orig_count = parameters.Count;
2874 // Assert: orig_count != parameter.Count => orig_count == 0
2875 if (orig_count != 0 && orig_count != parameters.Count)
2876 throw new InternalErrorException ("parameter information mismatch");
2878 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2880 for (int i = 0; i < orig_count; ++i) {
2881 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2883 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2886 VariableInfo vi = new VariableInfo (ip, i, offset);
2887 parameter_info [i].VariableInfo = vi;
2888 offset += vi.Length;
2891 ResolveMeta (ec, offset);
2893 return ec.Report.Errors == errors;
2897 // Check whether all `out' parameters have been assigned.
2899 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2901 if (vector.IsUnreachable)
2904 int n = parameter_info == null ? 0 : parameter_info.Length;
2906 for (int i = 0; i < n; i++) {
2907 VariableInfo var = parameter_info [i].VariableInfo;
2912 if (vector.IsAssigned (var, false))
2915 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2920 public override void Emit (EmitContext ec)
2922 if (Report.Errors > 0)
2930 if (ec.HasReturnLabel)
2931 ec.ReturnLabel = ec.ig.DefineLabel ();
2935 ec.Mark (EndLocation);
2937 if (ec.HasReturnLabel)
2938 ec.ig.MarkLabel (ec.ReturnLabel);
2940 if (ec.return_value != null) {
2941 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
2942 ec.ig.Emit (OpCodes.Ret);
2945 // If `HasReturnLabel' is set, then we already emitted a
2946 // jump to the end of the method, so we must emit a `ret'
2949 // Unfortunately, System.Reflection.Emit automatically emits
2950 // a leave to the end of a finally block. This is a problem
2951 // if no code is following the try/finally block since we may
2952 // jump to a point after the end of the method.
2953 // As a workaround, we're always creating a return label in
2957 if (ec.HasReturnLabel || !unreachable) {
2958 if (ec.ReturnType != TypeManager.void_type)
2959 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2960 ec.ig.Emit (OpCodes.Ret);
2965 } catch (Exception e){
2966 Console.WriteLine ("Exception caught by the compiler while emitting:");
2967 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2969 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2975 public override void EmitMeta (EmitContext ec)
2977 parameters.ResolveVariable ();
2979 // Avoid declaring an IL variable for this_variable since it is not accessed
2980 // from the generated IL
2981 if (this_variable != null)
2982 Variables.Remove ("this");
2986 protected override void EmitSymbolInfo (EmitContext ec)
2988 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2989 if ((ae != null) && (ae.Storey != null))
2990 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2992 base.EmitSymbolInfo (ec);
2996 public class SwitchLabel {
3003 Label il_label_code;
3004 bool il_label_code_set;
3006 public static readonly object NullStringCase = new object ();
3009 // if expr == null, then it is the default case.
3011 public SwitchLabel (Expression expr, Location l)
3017 public Expression Label {
3023 public Location Location {
3027 public object Converted {
3033 public Label GetILLabel (EmitContext ec)
3036 il_label = ec.ig.DefineLabel ();
3037 il_label_set = true;
3042 public Label GetILLabelCode (EmitContext ec)
3044 if (!il_label_code_set){
3045 il_label_code = ec.ig.DefineLabel ();
3046 il_label_code_set = true;
3048 return il_label_code;
3052 // Resolves the expression, reduces it to a literal if possible
3053 // and then converts it to the requested type.
3055 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3057 Expression e = label.Resolve (ec);
3062 Constant c = e as Constant;
3064 ec.Report.Error (150, loc, "A constant value is expected");
3068 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3069 converted = NullStringCase;
3073 if (allow_nullable && c.GetValue () == null) {
3074 converted = NullStringCase;
3078 c = c.ImplicitConversionRequired (ec, required_type, loc);
3082 converted = c.GetValue ();
3086 public void Error_AlreadyOccurs (ResolveContext ec, Type switch_type, SwitchLabel collision_with)
3089 if (converted == null)
3091 else if (converted == NullStringCase)
3094 label = converted.ToString ();
3096 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3097 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3100 public SwitchLabel Clone (CloneContext clonectx)
3102 return new SwitchLabel (label.Clone (clonectx), loc);
3106 public class SwitchSection {
3107 // An array of SwitchLabels.
3108 public readonly List<SwitchLabel> Labels;
3109 public readonly Block Block;
3111 public SwitchSection (List<SwitchLabel> labels, Block block)
3117 public SwitchSection Clone (CloneContext clonectx)
3119 var cloned_labels = new List<SwitchLabel> ();
3121 foreach (SwitchLabel sl in cloned_labels)
3122 cloned_labels.Add (sl.Clone (clonectx));
3124 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3128 public class Switch : Statement {
3129 public List<SwitchSection> Sections;
3130 public Expression Expr;
3133 /// Maps constants whose type type SwitchType to their SwitchLabels.
3135 public IDictionary<object, SwitchLabel> Elements;
3138 /// The governing switch type
3140 public Type SwitchType;
3145 Label default_target;
3147 Expression new_expr;
3150 SwitchSection constant_section;
3151 SwitchSection default_section;
3153 ExpressionStatement string_dictionary;
3154 FieldExpr switch_cache_field;
3155 static int unique_counter;
3158 // Nullable Types support
3160 Nullable.Unwrap unwrap;
3162 protected bool HaveUnwrap {
3163 get { return unwrap != null; }
3167 // The types allowed to be implicitly cast from
3168 // on the governing type
3170 static Type [] allowed_types;
3172 public Switch (Expression e, List<SwitchSection> sects, Location l)
3179 public bool GotDefault {
3181 return default_section != null;
3185 public Label DefaultTarget {
3187 return default_target;
3192 // Determines the governing type for a switch. The returned
3193 // expression might be the expression from the switch, or an
3194 // expression that includes any potential conversions to the
3195 // integral types or to string.
3197 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3201 if (t == TypeManager.byte_type ||
3202 t == TypeManager.sbyte_type ||
3203 t == TypeManager.ushort_type ||
3204 t == TypeManager.short_type ||
3205 t == TypeManager.uint32_type ||
3206 t == TypeManager.int32_type ||
3207 t == TypeManager.uint64_type ||
3208 t == TypeManager.int64_type ||
3209 t == TypeManager.char_type ||
3210 t == TypeManager.string_type ||
3211 t == TypeManager.bool_type ||
3212 TypeManager.IsEnumType (t))
3215 if (allowed_types == null){
3216 allowed_types = new Type [] {
3217 TypeManager.sbyte_type,
3218 TypeManager.byte_type,
3219 TypeManager.short_type,
3220 TypeManager.ushort_type,
3221 TypeManager.int32_type,
3222 TypeManager.uint32_type,
3223 TypeManager.int64_type,
3224 TypeManager.uint64_type,
3225 TypeManager.char_type,
3226 TypeManager.string_type
3231 // Try to find a *user* defined implicit conversion.
3233 // If there is no implicit conversion, or if there are multiple
3234 // conversions, we have to report an error
3236 Expression converted = null;
3237 foreach (Type tt in allowed_types){
3240 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3245 // Ignore over-worked ImplicitUserConversions that do
3246 // an implicit conversion in addition to the user conversion.
3248 if (!(e is UserCast))
3251 if (converted != null){
3252 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3262 // Performs the basic sanity checks on the switch statement
3263 // (looks for duplicate keys and non-constant expressions).
3265 // It also returns a hashtable with the keys that we will later
3266 // use to compute the switch tables
3268 bool CheckSwitch (ResolveContext ec)
3271 Elements = new Dictionary<object, SwitchLabel> ();
3273 foreach (SwitchSection ss in Sections){
3274 foreach (SwitchLabel sl in ss.Labels){
3275 if (sl.Label == null){
3276 if (default_section != null){
3277 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3280 default_section = ss;
3284 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3289 object key = sl.Converted;
3290 if (key == SwitchLabel.NullStringCase)
3291 has_null_case = true;
3294 Elements.Add (key, sl);
3295 } catch (ArgumentException) {
3296 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3304 void EmitObjectInteger (ILGenerator ig, object k)
3307 IntConstant.EmitInt (ig, (int) k);
3308 else if (k is Constant) {
3309 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3312 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3315 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3317 IntConstant.EmitInt (ig, (int) (long) k);
3318 ig.Emit (OpCodes.Conv_I8);
3321 LongConstant.EmitLong (ig, (long) k);
3323 else if (k is ulong)
3325 ulong ul = (ulong) k;
3328 IntConstant.EmitInt (ig, unchecked ((int) ul));
3329 ig.Emit (OpCodes.Conv_U8);
3333 LongConstant.EmitLong (ig, unchecked ((long) ul));
3337 IntConstant.EmitInt (ig, (int) ((char) k));
3338 else if (k is sbyte)
3339 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3341 IntConstant.EmitInt (ig, (int) ((byte) k));
3342 else if (k is short)
3343 IntConstant.EmitInt (ig, (int) ((short) k));
3344 else if (k is ushort)
3345 IntConstant.EmitInt (ig, (int) ((ushort) k));
3347 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3349 throw new Exception ("Unhandled case");
3352 // structure used to hold blocks of keys while calculating table switch
3353 class KeyBlock : IComparable
3355 public KeyBlock (long _first)
3357 first = last = _first;
3361 public List<object> element_keys;
3362 // how many items are in the bucket
3363 public int Size = 1;
3366 get { return (int) (last - first + 1); }
3368 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3370 return kb_last.last - kb_first.first + 1;
3372 public int CompareTo (object obj)
3374 KeyBlock kb = (KeyBlock) obj;
3375 int nLength = Length;
3376 int nLengthOther = kb.Length;
3377 if (nLengthOther == nLength)
3378 return (int) (kb.first - first);
3379 return nLength - nLengthOther;
3384 /// This method emits code for a lookup-based switch statement (non-string)
3385 /// Basically it groups the cases into blocks that are at least half full,
3386 /// and then spits out individual lookup opcodes for each block.
3387 /// It emits the longest blocks first, and short blocks are just
3388 /// handled with direct compares.
3390 /// <param name="ec"></param>
3391 /// <param name="val"></param>
3392 /// <returns></returns>
3393 void TableSwitchEmit (EmitContext ec, Expression val)
3395 int element_count = Elements.Count;
3396 object [] element_keys = new object [element_count];
3397 Elements.Keys.CopyTo (element_keys, 0);
3398 Array.Sort (element_keys);
3400 // initialize the block list with one element per key
3401 var key_blocks = new List<KeyBlock> (element_count);
3402 foreach (object key in element_keys)
3403 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3405 KeyBlock current_kb;
3406 // iteratively merge the blocks while they are at least half full
3407 // there's probably a really cool way to do this with a tree...
3408 while (key_blocks.Count > 1)
3410 var key_blocks_new = new List<KeyBlock> ();
3411 current_kb = (KeyBlock) key_blocks [0];
3412 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3414 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3415 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3418 current_kb.last = kb.last;
3419 current_kb.Size += kb.Size;
3423 // start a new block
3424 key_blocks_new.Add (current_kb);
3428 key_blocks_new.Add (current_kb);
3429 if (key_blocks.Count == key_blocks_new.Count)
3431 key_blocks = key_blocks_new;
3434 // initialize the key lists
3435 foreach (KeyBlock kb in key_blocks)
3436 kb.element_keys = new List<object> ();
3438 // fill the key lists
3440 if (key_blocks.Count > 0) {
3441 current_kb = (KeyBlock) key_blocks [0];
3442 foreach (object key in element_keys)
3444 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3445 System.Convert.ToInt64 (key) > current_kb.last;
3447 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3448 current_kb.element_keys.Add (key);
3452 // sort the blocks so we can tackle the largest ones first
3455 // okay now we can start...
3456 ILGenerator ig = ec.ig;
3457 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3458 Label lbl_default = default_target;
3460 Type type_keys = null;
3461 if (element_keys.Length > 0)
3462 type_keys = element_keys [0].GetType (); // used for conversions
3466 if (TypeManager.IsEnumType (SwitchType))
3467 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3469 compare_type = SwitchType;
3471 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3473 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3474 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3477 foreach (object key in kb.element_keys) {
3478 SwitchLabel sl = (SwitchLabel) Elements [key];
3479 if (key is int && (int) key == 0) {
3480 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3483 EmitObjectInteger (ig, key);
3484 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3490 // TODO: if all the keys in the block are the same and there are
3491 // no gaps/defaults then just use a range-check.
3492 if (compare_type == TypeManager.int64_type ||
3493 compare_type == TypeManager.uint64_type)
3495 // TODO: optimize constant/I4 cases
3497 // check block range (could be > 2^31)
3499 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3500 ig.Emit (OpCodes.Blt, lbl_default);
3502 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3503 ig.Emit (OpCodes.Bgt, lbl_default);
3509 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3510 ig.Emit (OpCodes.Sub);
3512 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3518 int first = (int) kb.first;
3521 IntConstant.EmitInt (ig, first);
3522 ig.Emit (OpCodes.Sub);
3526 IntConstant.EmitInt (ig, -first);
3527 ig.Emit (OpCodes.Add);
3531 // first, build the list of labels for the switch
3533 int cJumps = kb.Length;
3534 Label [] switch_labels = new Label [cJumps];
3535 for (int iJump = 0; iJump < cJumps; iJump++)
3537 object key = kb.element_keys [iKey];
3538 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3540 SwitchLabel sl = (SwitchLabel) Elements [key];
3541 switch_labels [iJump] = sl.GetILLabel (ec);
3545 switch_labels [iJump] = lbl_default;
3547 // emit the switch opcode
3548 ig.Emit (OpCodes.Switch, switch_labels);
3551 // mark the default for this block
3553 ig.MarkLabel (lbl_default);
3556 // TODO: find the default case and emit it here,
3557 // to prevent having to do the following jump.
3558 // make sure to mark other labels in the default section
3560 // the last default just goes to the end
3561 if (element_keys.Length > 0)
3562 ig.Emit (OpCodes.Br, lbl_default);
3564 // now emit the code for the sections
3565 bool found_default = false;
3567 foreach (SwitchSection ss in Sections) {
3568 foreach (SwitchLabel sl in ss.Labels) {
3569 if (sl.Converted == SwitchLabel.NullStringCase) {
3570 ig.MarkLabel (null_target);
3571 } else if (sl.Label == null) {
3572 ig.MarkLabel (lbl_default);
3573 found_default = true;
3575 ig.MarkLabel (null_target);
3577 ig.MarkLabel (sl.GetILLabel (ec));
3578 ig.MarkLabel (sl.GetILLabelCode (ec));
3583 if (!found_default) {
3584 ig.MarkLabel (lbl_default);
3585 if (!has_null_case) {
3586 ig.MarkLabel (null_target);
3590 ig.MarkLabel (lbl_end);
3593 SwitchSection FindSection (SwitchLabel label)
3595 foreach (SwitchSection ss in Sections){
3596 foreach (SwitchLabel sl in ss.Labels){
3605 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3607 foreach (SwitchSection ss in Sections)
3608 ss.Block.MutateHoistedGenericType (storey);
3611 public static void Reset ()
3614 allowed_types = null;
3617 public override bool Resolve (BlockContext ec)
3619 Expr = Expr.Resolve (ec);
3623 new_expr = SwitchGoverningType (ec, Expr);
3625 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3626 unwrap = Nullable.Unwrap.Create (Expr, false);
3630 new_expr = SwitchGoverningType (ec, unwrap);
3633 if (new_expr == null){
3634 ec.Report.Error (151, loc,
3635 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3636 TypeManager.CSharpName (Expr.Type));
3641 SwitchType = new_expr.Type;
3643 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3644 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3648 if (!CheckSwitch (ec))
3652 Elements.Remove (SwitchLabel.NullStringCase);
3654 Switch old_switch = ec.Switch;
3656 ec.Switch.SwitchType = SwitchType;
3658 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3659 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3661 var constant = new_expr as Constant;
3662 if (constant != null) {
3664 object key = constant.GetValue ();
3666 if (Elements.TryGetValue (key, out label))
3667 constant_section = FindSection (label);
3669 if (constant_section == null)
3670 constant_section = default_section;
3675 foreach (SwitchSection ss in Sections){
3677 ec.CurrentBranching.CreateSibling (
3678 null, FlowBranching.SiblingType.SwitchSection);
3682 if (is_constant && (ss != constant_section)) {
3683 // If we're a constant switch, we're only emitting
3684 // one single section - mark all the others as
3686 ec.CurrentBranching.CurrentUsageVector.Goto ();
3687 if (!ss.Block.ResolveUnreachable (ec, true)) {
3691 if (!ss.Block.Resolve (ec))
3696 if (default_section == null)
3697 ec.CurrentBranching.CreateSibling (
3698 null, FlowBranching.SiblingType.SwitchSection);
3700 ec.EndFlowBranching ();
3701 ec.Switch = old_switch;
3703 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3708 if (SwitchType == TypeManager.string_type && !is_constant) {
3709 // TODO: Optimize single case, and single+default case
3710 ResolveStringSwitchMap (ec);
3716 void ResolveStringSwitchMap (ResolveContext ec)
3718 FullNamedExpression string_dictionary_type;
3719 if (TypeManager.generic_ienumerable_type != null) {
3720 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3721 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3723 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3725 new TypeExpression (TypeManager.string_type, loc),
3726 new TypeExpression (TypeManager.int32_type, loc)), loc);
3728 MemberAccess system_collections_generic = new MemberAccess (
3729 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3731 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3734 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3735 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3736 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3737 if (!field.Define ())
3739 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3741 var init = new List<Expression> ();
3744 string value = null;
3745 foreach (SwitchSection section in Sections) {
3746 int last_count = init.Count;
3747 foreach (SwitchLabel sl in section.Labels) {
3748 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3751 value = (string) sl.Converted;
3752 var init_args = new List<Expression> (2);
3753 init_args.Add (new StringLiteral (value, sl.Location));
3754 init_args.Add (new IntConstant (counter, loc));
3755 init.Add (new CollectionElementInitializer (init_args, loc));
3759 // Don't add empty sections
3761 if (last_count == init.Count)
3764 Elements.Add (counter, section.Labels [0]);
3768 Arguments args = new Arguments (1);
3769 args.Add (new Argument (new IntConstant (init.Count, loc)));
3770 Expression initializer = new NewInitialize (string_dictionary_type, args,
3771 new CollectionOrObjectInitializers (init, loc), loc);
3773 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3774 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3777 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3779 ILGenerator ig = ec.ig;
3780 Label l_initialized = ig.DefineLabel ();
3783 // Skip initialization when value is null
3785 value.EmitBranchable (ec, null_target, false);
3788 // Check if string dictionary is initialized and initialize
3790 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3791 string_dictionary.EmitStatement (ec);
3792 ig.MarkLabel (l_initialized);
3794 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3796 ResolveContext rc = new ResolveContext (ec.MemberContext);
3798 if (TypeManager.generic_ienumerable_type != null) {
3799 Arguments get_value_args = new Arguments (2);
3800 get_value_args.Add (new Argument (value));
3801 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3802 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3803 if (get_item == null)
3807 // A value was not found, go to default case
3809 get_item.EmitBranchable (ec, default_target, false);
3811 Arguments get_value_args = new Arguments (1);
3812 get_value_args.Add (new Argument (value));
3814 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3815 if (get_item == null)
3818 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3819 get_item_object.EmitAssign (ec, get_item, true, false);
3820 ec.ig.Emit (OpCodes.Brfalse, default_target);
3822 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3823 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3825 get_item_int.EmitStatement (ec);
3826 get_item_object.Release (ec);
3829 TableSwitchEmit (ec, string_switch_variable);
3830 string_switch_variable.Release (ec);
3833 protected override void DoEmit (EmitContext ec)
3835 ILGenerator ig = ec.ig;
3837 default_target = ig.DefineLabel ();
3838 null_target = ig.DefineLabel ();
3840 // Store variable for comparission purposes
3841 // TODO: Don't duplicate non-captured VariableReference
3842 LocalTemporary value;
3844 value = new LocalTemporary (SwitchType);
3845 unwrap.EmitCheck (ec);
3846 ig.Emit (OpCodes.Brfalse, null_target);
3849 } else if (!is_constant) {
3850 value = new LocalTemporary (SwitchType);
3857 // Setup the codegen context
3859 Label old_end = ec.LoopEnd;
3860 Switch old_switch = ec.Switch;
3862 ec.LoopEnd = ig.DefineLabel ();
3867 if (constant_section != null)
3868 constant_section.Block.Emit (ec);
3869 } else if (string_dictionary != null) {
3870 DoEmitStringSwitch (value, ec);
3872 TableSwitchEmit (ec, value);
3878 // Restore context state.
3879 ig.MarkLabel (ec.LoopEnd);
3882 // Restore the previous context
3884 ec.LoopEnd = old_end;
3885 ec.Switch = old_switch;
3888 protected override void CloneTo (CloneContext clonectx, Statement t)
3890 Switch target = (Switch) t;
3892 target.Expr = Expr.Clone (clonectx);
3893 target.Sections = new List<SwitchSection> ();
3894 foreach (SwitchSection ss in Sections){
3895 target.Sections.Add (ss.Clone (clonectx));
3900 // A place where execution can restart in an iterator
3901 public abstract class ResumableStatement : Statement
3904 protected Label resume_point;
3906 public Label PrepareForEmit (EmitContext ec)
3910 resume_point = ec.ig.DefineLabel ();
3912 return resume_point;
3915 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3919 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3924 // Base class for statements that are implemented in terms of try...finally
3925 public abstract class ExceptionStatement : ResumableStatement
3929 List<ResumableStatement> resume_points;
3930 int first_resume_pc;
3932 protected abstract void EmitPreTryBody (EmitContext ec);
3933 protected abstract void EmitTryBody (EmitContext ec);
3934 protected abstract void EmitFinallyBody (EmitContext ec);
3936 protected sealed override void DoEmit (EmitContext ec)
3938 ILGenerator ig = ec.ig;
3940 EmitPreTryBody (ec);
3942 if (resume_points != null) {
3943 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3944 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
3947 ig.BeginExceptionBlock ();
3949 if (resume_points != null) {
3950 ig.MarkLabel (resume_point);
3952 // For normal control flow, we want to fall-through the Switch
3953 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3954 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
3955 IntConstant.EmitInt (ig, first_resume_pc);
3956 ig.Emit (OpCodes.Sub);
3958 Label [] labels = new Label [resume_points.Count];
3959 for (int i = 0; i < resume_points.Count; ++i)
3960 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3961 ig.Emit (OpCodes.Switch, labels);
3966 ig.BeginFinallyBlock ();
3968 Label start_finally = ec.ig.DefineLabel ();
3969 if (resume_points != null) {
3970 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
3971 ig.Emit (OpCodes.Brfalse_S, start_finally);
3972 ig.Emit (OpCodes.Endfinally);
3975 ig.MarkLabel (start_finally);
3976 EmitFinallyBody (ec);
3978 ig.EndExceptionBlock ();
3981 public void SomeCodeFollows ()
3983 code_follows = true;
3986 public override bool Resolve (BlockContext ec)
3988 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3989 // So, ensure there's some IL code after this statement.
3990 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3991 ec.NeedReturnLabel ();
3993 iter = ec.CurrentIterator;
3997 public void AddResumePoint (ResumableStatement stmt, int pc)
3999 if (resume_points == null) {
4000 resume_points = new List<ResumableStatement> ();
4001 first_resume_pc = pc;
4004 if (pc != first_resume_pc + resume_points.Count)
4005 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4007 resume_points.Add (stmt);
4010 Label dispose_try_block;
4011 bool prepared_for_dispose, emitted_dispose;
4012 public override Label PrepareForDispose (EmitContext ec, Label end)
4014 if (!prepared_for_dispose) {
4015 prepared_for_dispose = true;
4016 dispose_try_block = ec.ig.DefineLabel ();
4018 return dispose_try_block;
4021 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4023 if (emitted_dispose)
4026 emitted_dispose = true;
4028 ILGenerator ig = ec.ig;
4030 Label end_of_try = ig.DefineLabel ();
4032 // Ensure that the only way we can get into this code is through a dispatcher
4033 if (have_dispatcher)
4034 ig.Emit (OpCodes.Br, end);
4036 ig.BeginExceptionBlock ();
4038 ig.MarkLabel (dispose_try_block);
4040 Label [] labels = null;
4041 for (int i = 0; i < resume_points.Count; ++i) {
4042 ResumableStatement s = (ResumableStatement) resume_points [i];
4043 Label ret = s.PrepareForDispose (ec, end_of_try);
4044 if (ret.Equals (end_of_try) && labels == null)
4046 if (labels == null) {
4047 labels = new Label [resume_points.Count];
4048 for (int j = 0; j < i; ++j)
4049 labels [j] = end_of_try;
4054 if (labels != null) {
4056 for (j = 1; j < labels.Length; ++j)
4057 if (!labels [0].Equals (labels [j]))
4059 bool emit_dispatcher = j < labels.Length;
4061 if (emit_dispatcher) {
4062 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4063 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4064 IntConstant.EmitInt (ig, first_resume_pc);
4065 ig.Emit (OpCodes.Sub);
4066 ig.Emit (OpCodes.Switch, labels);
4067 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4070 foreach (ResumableStatement s in resume_points)
4071 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4074 ig.MarkLabel (end_of_try);
4076 ig.BeginFinallyBlock ();
4078 EmitFinallyBody (ec);
4080 ig.EndExceptionBlock ();
4084 public class Lock : ExceptionStatement {
4086 public Statement Statement;
4087 TemporaryVariable temp;
4089 public Lock (Expression expr, Statement stmt, Location l)
4096 public override bool Resolve (BlockContext ec)
4098 expr = expr.Resolve (ec);
4102 if (!TypeManager.IsReferenceType (expr.Type)){
4103 ec.Report.Error (185, loc,
4104 "`{0}' is not a reference type as required by the lock statement",
4105 TypeManager.CSharpName (expr.Type));
4109 ec.StartFlowBranching (this);
4110 bool ok = Statement.Resolve (ec);
4111 ec.EndFlowBranching ();
4113 ok &= base.Resolve (ec);
4115 // Avoid creating libraries that reference the internal
4118 if (t == TypeManager.null_type)
4119 t = TypeManager.object_type;
4121 temp = new TemporaryVariable (t, loc);
4124 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4125 Type monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", Kind.Class, true);
4126 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4127 monitor_type, "Enter", loc, TypeManager.object_type);
4128 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4129 monitor_type, "Exit", loc, TypeManager.object_type);
4135 protected override void EmitPreTryBody (EmitContext ec)
4137 ILGenerator ig = ec.ig;
4139 temp.EmitAssign (ec, expr);
4141 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4144 protected override void EmitTryBody (EmitContext ec)
4146 Statement.Emit (ec);
4149 protected override void EmitFinallyBody (EmitContext ec)
4152 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4155 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4157 expr.MutateHoistedGenericType (storey);
4158 temp.MutateHoistedGenericType (storey);
4159 Statement.MutateHoistedGenericType (storey);
4162 protected override void CloneTo (CloneContext clonectx, Statement t)
4164 Lock target = (Lock) t;
4166 target.expr = expr.Clone (clonectx);
4167 target.Statement = Statement.Clone (clonectx);
4171 public class Unchecked : Statement {
4174 public Unchecked (Block b)
4180 public override bool Resolve (BlockContext ec)
4182 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4183 return Block.Resolve (ec);
4186 protected override void DoEmit (EmitContext ec)
4188 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4192 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4194 Block.MutateHoistedGenericType (storey);
4197 protected override void CloneTo (CloneContext clonectx, Statement t)
4199 Unchecked target = (Unchecked) t;
4201 target.Block = clonectx.LookupBlock (Block);
4205 public class Checked : Statement {
4208 public Checked (Block b)
4211 b.Unchecked = false;
4214 public override bool Resolve (BlockContext ec)
4216 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4217 return Block.Resolve (ec);
4220 protected override void DoEmit (EmitContext ec)
4222 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4226 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4228 Block.MutateHoistedGenericType (storey);
4231 protected override void CloneTo (CloneContext clonectx, Statement t)
4233 Checked target = (Checked) t;
4235 target.Block = clonectx.LookupBlock (Block);
4239 public class Unsafe : Statement {
4242 public Unsafe (Block b)
4245 Block.Unsafe = true;
4246 loc = b.StartLocation;
4249 public override bool Resolve (BlockContext ec)
4251 if (ec.CurrentIterator != null)
4252 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4254 using (ec.Set (ResolveContext.Options.UnsafeScope))
4255 return Block.Resolve (ec);
4258 protected override void DoEmit (EmitContext ec)
4263 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4265 Block.MutateHoistedGenericType (storey);
4268 protected override void CloneTo (CloneContext clonectx, Statement t)
4270 Unsafe target = (Unsafe) t;
4272 target.Block = clonectx.LookupBlock (Block);
4279 class Fixed : Statement {
4281 List<KeyValuePair<LocalInfo, Expression>> declarators;
4282 Statement statement;
4287 abstract class Emitter
4289 protected LocalInfo vi;
4290 protected Expression converted;
4292 protected Emitter (Expression expr, LocalInfo li)
4298 public abstract void Emit (EmitContext ec);
4299 public abstract void EmitExit (EmitContext ec);
4302 class ExpressionEmitter : Emitter {
4303 public ExpressionEmitter (Expression converted, LocalInfo li) :
4304 base (converted, li)
4308 public override void Emit (EmitContext ec) {
4310 // Store pointer in pinned location
4312 converted.Emit (ec);
4316 public override void EmitExit (EmitContext ec)
4318 ec.ig.Emit (OpCodes.Ldc_I4_0);
4319 ec.ig.Emit (OpCodes.Conv_U);
4324 class StringEmitter : Emitter
4326 LocalInfo pinned_string;
4328 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4331 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4332 pinned_string.Pinned = true;
4335 public StringEmitter Resolve (ResolveContext rc)
4337 pinned_string.Resolve (rc);
4339 if (TypeManager.int_get_offset_to_string_data == null) {
4340 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4341 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4347 public override void Emit (EmitContext ec)
4349 pinned_string.ResolveVariable (ec);
4351 converted.Emit (ec);
4352 pinned_string.EmitAssign (ec);
4354 // TODO: Should use Binary::Add
4355 pinned_string.Emit (ec);
4356 ec.ig.Emit (OpCodes.Conv_I);
4358 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4359 //pe.InstanceExpression = pinned_string;
4360 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4362 ec.ig.Emit (OpCodes.Add);
4366 public override void EmitExit (EmitContext ec)
4368 ec.ig.Emit (OpCodes.Ldnull);
4369 pinned_string.EmitAssign (ec);
4373 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4376 declarators = decls;
4381 public Statement Statement {
4382 get { return statement; }
4385 public override bool Resolve (BlockContext ec)
4388 Expression.UnsafeError (ec, loc);
4392 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4393 if (texpr == null) {
4394 if (type is VarExpr)
4395 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4400 expr_type = texpr.Type;
4402 data = new Emitter [declarators.Count];
4404 if (!expr_type.IsPointer){
4405 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4410 foreach (var p in declarators){
4411 LocalInfo vi = p.Key;
4412 Expression e = p.Value;
4414 vi.VariableInfo.SetAssigned (ec);
4415 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4418 // The rules for the possible declarators are pretty wise,
4419 // but the production on the grammar is more concise.
4421 // So we have to enforce these rules here.
4423 // We do not resolve before doing the case 1 test,
4424 // because the grammar is explicit in that the token &
4425 // is present, so we need to test for this particular case.
4429 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4433 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4443 if (e.Type.IsArray){
4444 Type array_type = TypeManager.GetElementType (e.Type);
4447 // Provided that array_type is unmanaged,
4449 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4453 // and T* is implicitly convertible to the
4454 // pointer type given in the fixed statement.
4456 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4458 Expression converted = Convert.ImplicitConversionRequired (
4459 ec, array_ptr, vi.VariableType, loc);
4460 if (converted == null)
4464 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4466 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4467 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4468 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc)))),
4469 new NullPointer (loc),
4472 converted = converted.Resolve (ec);
4474 data [i] = new ExpressionEmitter (converted, vi);
4483 if (e.Type == TypeManager.string_type){
4484 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4489 // Case 4: fixed buffer
4490 if (e is FixedBufferPtr) {
4491 data [i++] = new ExpressionEmitter (e, vi);
4496 // Case 1: & object.
4498 Unary u = e as Unary;
4499 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4500 IVariableReference vr = u.Expr as IVariableReference;
4501 if (vr == null || !vr.IsFixed) {
4502 data [i] = new ExpressionEmitter (e, vi);
4506 if (data [i++] == null)
4507 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4509 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4512 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4513 bool ok = statement.Resolve (ec);
4514 bool flow_unreachable = ec.EndFlowBranching ();
4515 has_ret = flow_unreachable;
4520 protected override void DoEmit (EmitContext ec)
4522 for (int i = 0; i < data.Length; i++) {
4526 statement.Emit (ec);
4532 // Clear the pinned variable
4534 for (int i = 0; i < data.Length; i++) {
4535 data [i].EmitExit (ec);
4539 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4541 // Fixed statement cannot be used inside anonymous methods or lambdas
4542 throw new NotSupportedException ();
4545 protected override void CloneTo (CloneContext clonectx, Statement t)
4547 Fixed target = (Fixed) t;
4549 target.type = type.Clone (clonectx);
4550 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4551 foreach (var p in declarators) {
4552 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4553 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4556 target.statement = statement.Clone (clonectx);
4560 public class Catch : Statement {
4561 public readonly string Name;
4563 public Block VarBlock;
4565 Expression type_expr;
4568 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4573 VarBlock = var_block;
4577 public Type CatchType {
4583 public bool IsGeneral {
4585 return type_expr == null;
4589 protected override void DoEmit (EmitContext ec)
4591 ILGenerator ig = ec.ig;
4593 if (CatchType != null)
4594 ig.BeginCatchBlock (CatchType);
4596 ig.BeginCatchBlock (TypeManager.object_type);
4598 if (VarBlock != null)
4602 // TODO: Move to resolve
4603 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4604 lvr.Resolve (new ResolveContext (ec.MemberContext));
4606 // Only to make verifier happy
4607 if (TypeManager.IsGenericParameter (lvr.Type))
4608 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4611 if (lvr.IsHoisted) {
4612 LocalTemporary lt = new LocalTemporary (lvr.Type);
4616 // Variable is at the top of the stack
4617 source = EmptyExpression.Null;
4620 lvr.EmitAssign (ec, source, false, false);
4622 ig.Emit (OpCodes.Pop);
4627 public override bool Resolve (BlockContext ec)
4629 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4630 if (type_expr != null) {
4631 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4637 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4638 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4644 if (!Block.Resolve (ec))
4647 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4648 // emit the "unused variable" warnings.
4649 if (VarBlock != null)
4650 return VarBlock.Resolve (ec);
4656 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4659 type = storey.MutateType (type);
4660 if (VarBlock != null)
4661 VarBlock.MutateHoistedGenericType (storey);
4662 Block.MutateHoistedGenericType (storey);
4665 protected override void CloneTo (CloneContext clonectx, Statement t)
4667 Catch target = (Catch) t;
4669 if (type_expr != null)
4670 target.type_expr = type_expr.Clone (clonectx);
4671 if (VarBlock != null)
4672 target.VarBlock = clonectx.LookupBlock (VarBlock);
4673 target.Block = clonectx.LookupBlock (Block);
4677 public class TryFinally : ExceptionStatement {
4681 public TryFinally (Statement stmt, Block fini, Location l)
4688 public override bool Resolve (BlockContext ec)
4692 ec.StartFlowBranching (this);
4694 if (!stmt.Resolve (ec))
4698 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4699 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4700 if (!fini.Resolve (ec))
4704 ec.EndFlowBranching ();
4706 ok &= base.Resolve (ec);
4711 protected override void EmitPreTryBody (EmitContext ec)
4715 protected override void EmitTryBody (EmitContext ec)
4720 protected override void EmitFinallyBody (EmitContext ec)
4725 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4727 stmt.MutateHoistedGenericType (storey);
4728 fini.MutateHoistedGenericType (storey);
4731 protected override void CloneTo (CloneContext clonectx, Statement t)
4733 TryFinally target = (TryFinally) t;
4735 target.stmt = (Statement) stmt.Clone (clonectx);
4737 target.fini = clonectx.LookupBlock (fini);
4741 public class TryCatch : Statement {
4743 public List<Catch> Specific;
4744 public Catch General;
4745 bool inside_try_finally, code_follows;
4747 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4750 this.Specific = catch_clauses;
4751 this.inside_try_finally = inside_try_finally;
4753 Catch c = catch_clauses [0];
4756 catch_clauses.RemoveAt (0);
4762 public override bool Resolve (BlockContext ec)
4766 ec.StartFlowBranching (this);
4768 if (!Block.Resolve (ec))
4771 Type[] prev_catches = new Type [Specific.Count];
4773 foreach (Catch c in Specific){
4774 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4776 if (c.Name != null) {
4777 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4779 throw new Exception ();
4781 vi.VariableInfo = null;
4784 if (!c.Resolve (ec)) {
4789 Type resolved_type = c.CatchType;
4790 for (int ii = 0; ii < last_index; ++ii) {
4791 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4792 ec.Report.Error (160, c.loc,
4793 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4794 TypeManager.CSharpName (prev_catches [ii]));
4799 prev_catches [last_index++] = resolved_type;
4802 if (General != null) {
4803 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4804 foreach (Catch c in Specific){
4805 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4806 ec.Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4811 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4813 if (!General.Resolve (ec))
4817 ec.EndFlowBranching ();
4819 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4820 // So, ensure there's some IL code after this statement
4821 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4822 ec.NeedReturnLabel ();
4827 public void SomeCodeFollows ()
4829 code_follows = true;
4832 protected override void DoEmit (EmitContext ec)
4834 ILGenerator ig = ec.ig;
4836 if (!inside_try_finally)
4837 ig.BeginExceptionBlock ();
4841 foreach (Catch c in Specific)
4844 if (General != null)
4847 if (!inside_try_finally)
4848 ig.EndExceptionBlock ();
4851 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4853 Block.MutateHoistedGenericType (storey);
4855 if (General != null)
4856 General.MutateHoistedGenericType (storey);
4857 if (Specific != null) {
4858 foreach (Catch c in Specific)
4859 c.MutateHoistedGenericType (storey);
4863 protected override void CloneTo (CloneContext clonectx, Statement t)
4865 TryCatch target = (TryCatch) t;
4867 target.Block = clonectx.LookupBlock (Block);
4868 if (General != null)
4869 target.General = (Catch) General.Clone (clonectx);
4870 if (Specific != null){
4871 target.Specific = new List<Catch> ();
4872 foreach (Catch c in Specific)
4873 target.Specific.Add ((Catch) c.Clone (clonectx));
4878 // FIXME: Why is it almost exact copy of Using ??
4879 public class UsingTemporary : ExceptionStatement {
4880 TemporaryVariable local_copy;
4881 public Statement Statement;
4885 public UsingTemporary (Expression expr, Statement stmt, Location l)
4892 public override bool Resolve (BlockContext ec)
4894 expr = expr.Resolve (ec);
4898 expr_type = expr.Type;
4900 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type) &&
4901 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4902 if (!TypeManager.IsDynamicType (expr_type)) {
4903 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4907 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4908 expr_type = expr.Type;
4911 local_copy = new TemporaryVariable (expr_type, loc);
4912 local_copy.Resolve (ec);
4914 ec.StartFlowBranching (this);
4916 bool ok = Statement.Resolve (ec);
4918 ec.EndFlowBranching ();
4920 ok &= base.Resolve (ec);
4922 if (TypeManager.void_dispose_void == null) {
4923 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4924 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4930 protected override void EmitPreTryBody (EmitContext ec)
4932 local_copy.EmitAssign (ec, expr);
4935 protected override void EmitTryBody (EmitContext ec)
4937 Statement.Emit (ec);
4940 protected override void EmitFinallyBody (EmitContext ec)
4942 ILGenerator ig = ec.ig;
4943 if (!TypeManager.IsStruct (expr_type)) {
4944 Label skip = ig.DefineLabel ();
4945 local_copy.Emit (ec);
4946 ig.Emit (OpCodes.Brfalse, skip);
4947 local_copy.Emit (ec);
4948 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4949 ig.MarkLabel (skip);
4953 Expression ml = Expression.MemberLookup (RootContext.ToplevelTypes.Compiler,
4954 ec.CurrentType, TypeManager.idisposable_type, expr_type,
4955 "Dispose", Location.Null);
4957 if (!(ml is MethodGroupExpr)) {
4958 local_copy.Emit (ec);
4959 ig.Emit (OpCodes.Box, expr_type);
4960 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4964 MethodInfo mi = null;
4966 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4967 if (TypeManager.GetParameterData (mk).Count == 0) {
4974 ec.Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4978 local_copy.AddressOf (ec, AddressOp.Load);
4979 ig.Emit (OpCodes.Call, mi);
4982 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4984 expr_type = storey.MutateType (expr_type);
4985 local_copy.MutateHoistedGenericType (storey);
4986 Statement.MutateHoistedGenericType (storey);
4989 protected override void CloneTo (CloneContext clonectx, Statement t)
4991 UsingTemporary target = (UsingTemporary) t;
4993 target.expr = expr.Clone (clonectx);
4994 target.Statement = Statement.Clone (clonectx);
4998 public class Using : ExceptionStatement {
5000 public Statement EmbeddedStatement {
5001 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5007 ExpressionStatement assign;
5009 public Using (Expression var, Expression init, Statement stmt, Location l)
5017 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
5019 ec.Report.SymbolRelatedToPreviousError (expr.Type);
5020 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5021 TypeManager.CSharpName (expr.Type));
5024 protected override void EmitPreTryBody (EmitContext ec)
5026 assign.EmitStatement (ec);
5029 protected override void EmitTryBody (EmitContext ec)
5034 protected override void EmitFinallyBody (EmitContext ec)
5036 ILGenerator ig = ec.ig;
5037 Label skip = ig.DefineLabel ();
5039 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5040 if (emit_null_check) {
5042 ig.Emit (OpCodes.Brfalse, skip);
5045 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5047 if (emit_null_check)
5048 ig.MarkLabel (skip);
5051 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5053 assign.MutateHoistedGenericType (storey);
5054 var.MutateHoistedGenericType (storey);
5055 stmt.MutateHoistedGenericType (storey);
5058 public override bool Resolve (BlockContext ec)
5060 if (!ResolveVariable (ec))
5063 ec.StartFlowBranching (this);
5065 bool ok = stmt.Resolve (ec);
5067 ec.EndFlowBranching ();
5069 ok &= base.Resolve (ec);
5071 if (TypeManager.void_dispose_void == null) {
5072 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5073 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5079 bool ResolveVariable (BlockContext ec)
5081 assign = new SimpleAssign (var, init, loc);
5082 assign = assign.ResolveStatement (ec);
5086 if (assign.Type == TypeManager.idisposable_type ||
5087 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5091 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5093 if (TypeManager.IsDynamicType (assign.Type)) {
5094 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
5095 var = new TemporaryVariable (e.Type, loc);
5096 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
5100 Error_IsNotConvertibleToIDisposable (ec, var);
5104 throw new NotImplementedException ("covariance?");
5107 protected override void CloneTo (CloneContext clonectx, Statement t)
5109 Using target = (Using) t;
5111 target.var = var.Clone (clonectx);
5112 target.init = init.Clone (clonectx);
5113 target.stmt = stmt.Clone (clonectx);
5118 /// Implementation of the foreach C# statement
5120 public class Foreach : Statement {
5122 sealed class ArrayForeach : Statement
5124 class ArrayCounter : TemporaryVariable
5126 StatementExpression increment;
5128 public ArrayCounter (Location loc)
5129 : base (TypeManager.int32_type, loc)
5133 public void ResolveIncrement (BlockContext ec)
5135 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5136 increment.Resolve (ec);
5139 public void EmitIncrement (EmitContext ec)
5141 increment.Emit (ec);
5145 readonly Foreach for_each;
5146 readonly Statement statement;
5149 TemporaryVariable[] lengths;
5150 Expression [] length_exprs;
5151 ArrayCounter[] counter;
5153 TemporaryVariable copy;
5156 public ArrayForeach (Foreach @foreach, int rank)
5158 for_each = @foreach;
5159 statement = for_each.statement;
5162 counter = new ArrayCounter [rank];
5163 length_exprs = new Expression [rank];
5166 // Only use temporary length variables when dealing with
5167 // multi-dimensional arrays
5170 lengths = new TemporaryVariable [rank];
5173 protected override void CloneTo (CloneContext clonectx, Statement target)
5175 throw new NotImplementedException ();
5178 public override bool Resolve (BlockContext ec)
5180 copy = new TemporaryVariable (for_each.expr.Type, loc);
5183 int rank = length_exprs.Length;
5184 Arguments list = new Arguments (rank);
5185 for (int i = 0; i < rank; i++) {
5186 counter [i] = new ArrayCounter (loc);
5187 counter [i].ResolveIncrement (ec);
5190 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5192 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5193 lengths [i].Resolve (ec);
5195 Arguments args = new Arguments (1);
5196 args.Add (new Argument (new IntConstant (i, loc)));
5197 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5200 list.Add (new Argument (counter [i]));
5203 access = new ElementAccess (copy, list).Resolve (ec);
5207 Expression var_type = for_each.type;
5208 VarExpr ve = var_type as VarExpr;
5210 // Infer implicitly typed local variable from foreach array type
5211 var_type = new TypeExpression (access.Type, ve.Location);
5214 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5215 if (var_type == null)
5218 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5224 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5225 ec.CurrentBranching.CreateSibling ();
5227 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5228 if (for_each.variable == null)
5231 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5232 if (!statement.Resolve (ec))
5234 ec.EndFlowBranching ();
5236 // There's no direct control flow from the end of the embedded statement to the end of the loop
5237 ec.CurrentBranching.CurrentUsageVector.Goto ();
5239 ec.EndFlowBranching ();
5244 protected override void DoEmit (EmitContext ec)
5246 ILGenerator ig = ec.ig;
5248 copy.EmitAssign (ec, for_each.expr);
5250 int rank = length_exprs.Length;
5251 Label[] test = new Label [rank];
5252 Label[] loop = new Label [rank];
5254 for (int i = 0; i < rank; i++) {
5255 test [i] = ig.DefineLabel ();
5256 loop [i] = ig.DefineLabel ();
5258 if (lengths != null)
5259 lengths [i].EmitAssign (ec, length_exprs [i]);
5262 IntConstant zero = new IntConstant (0, loc);
5263 for (int i = 0; i < rank; i++) {
5264 counter [i].EmitAssign (ec, zero);
5266 ig.Emit (OpCodes.Br, test [i]);
5267 ig.MarkLabel (loop [i]);
5270 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5272 statement.Emit (ec);
5274 ig.MarkLabel (ec.LoopBegin);
5276 for (int i = rank - 1; i >= 0; i--){
5277 counter [i].EmitIncrement (ec);
5279 ig.MarkLabel (test [i]);
5280 counter [i].Emit (ec);
5282 if (lengths != null)
5283 lengths [i].Emit (ec);
5285 length_exprs [i].Emit (ec);
5287 ig.Emit (OpCodes.Blt, loop [i]);
5290 ig.MarkLabel (ec.LoopEnd);
5293 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5295 for_each.expr.MutateHoistedGenericType (storey);
5297 copy.MutateHoistedGenericType (storey);
5298 conv.MutateHoistedGenericType (storey);
5299 statement.MutateHoistedGenericType (storey);
5301 for (int i = 0; i < counter.Length; i++) {
5302 counter [i].MutateHoistedGenericType (storey);
5303 if (lengths != null)
5304 lengths [i].MutateHoistedGenericType (storey);
5309 sealed class CollectionForeach : Statement
5311 class CollectionForeachStatement : Statement
5314 Expression variable, current, conv;
5315 Statement statement;
5318 public CollectionForeachStatement (Type type, Expression variable,
5319 Expression current, Statement statement,
5323 this.variable = variable;
5324 this.current = current;
5325 this.statement = statement;
5329 protected override void CloneTo (CloneContext clonectx, Statement target)
5331 throw new NotImplementedException ();
5334 public override bool Resolve (BlockContext ec)
5336 current = current.Resolve (ec);
5337 if (current == null)
5340 conv = Convert.ExplicitConversion (ec, current, type, loc);
5344 assign = new SimpleAssign (variable, conv, loc);
5345 if (assign.Resolve (ec) == null)
5348 if (!statement.Resolve (ec))
5354 protected override void DoEmit (EmitContext ec)
5356 assign.EmitStatement (ec);
5357 statement.Emit (ec);
5360 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5362 assign.MutateHoistedGenericType (storey);
5363 statement.MutateHoistedGenericType (storey);
5367 Expression variable, expr;
5368 Statement statement;
5370 TemporaryVariable enumerator;
5375 MethodGroupExpr get_enumerator;
5376 PropertyExpr get_current;
5377 MethodInfo move_next;
5378 Expression var_type;
5379 Type enumerator_type;
5380 bool enumerator_found;
5382 public CollectionForeach (Expression var_type, Expression var,
5383 Expression expr, Statement stmt, Location l)
5385 this.var_type = var_type;
5386 this.variable = var;
5392 protected override void CloneTo (CloneContext clonectx, Statement target)
5394 throw new NotImplementedException ();
5397 bool GetEnumeratorFilter (ResolveContext ec, MethodInfo mi)
5399 Type return_type = mi.ReturnType;
5402 // Ok, we can access it, now make sure that we can do something
5403 // with this `GetEnumerator'
5406 if (return_type == TypeManager.ienumerator_type ||
5407 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5409 // If it is not an interface, lets try to find the methods ourselves.
5410 // For example, if we have:
5411 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5412 // We can avoid the iface call. This is a runtime perf boost.
5413 // even bigger if we have a ValueType, because we avoid the cost
5416 // We have to make sure that both methods exist for us to take
5417 // this path. If one of the methods does not exist, we will just
5418 // use the interface. Sadly, this complex if statement is the only
5419 // way I could do this without a goto
5422 if (TypeManager.bool_movenext_void == null) {
5423 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5424 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5427 if (TypeManager.ienumerator_getcurrent == null) {
5428 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5429 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5433 // Prefer a generic enumerator over a non-generic one.
5435 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5436 enumerator_type = return_type;
5437 if (!FetchGetCurrent (ec, return_type))
5438 get_current = new PropertyExpr (
5439 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5440 if (!FetchMoveNext (return_type))
5441 move_next = TypeManager.bool_movenext_void;
5445 if (return_type.IsInterface ||
5446 !FetchMoveNext (return_type) ||
5447 !FetchGetCurrent (ec, return_type)) {
5448 enumerator_type = return_type;
5449 move_next = TypeManager.bool_movenext_void;
5450 get_current = new PropertyExpr (
5451 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5456 // Ok, so they dont return an IEnumerable, we will have to
5457 // find if they support the GetEnumerator pattern.
5460 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5461 ec.Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5462 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5467 enumerator_type = return_type;
5473 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5475 bool FetchMoveNext (Type t)
5477 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5479 BindingFlags.Public | BindingFlags.Instance,
5482 if (move_next_list == null)
5485 foreach (MemberInfo m in move_next_list){
5486 MethodInfo mi = (MethodInfo) m;
5488 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5489 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5499 // Retrieves a `public T get_Current ()' method from the Type `t'
5501 bool FetchGetCurrent (ResolveContext ec, Type t)
5503 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5504 ec.CurrentType, t, "Current", MemberTypes.Property,
5505 Expression.AllBindingFlags, loc) as PropertyExpr;
5513 void Error_Enumerator (BlockContext ec)
5515 if (enumerator_found) {
5519 ec.Report.Error (1579, loc,
5520 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5521 TypeManager.CSharpName (expr.Type));
5524 bool IsOverride (MethodInfo m)
5526 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5528 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5530 if (m is MethodBuilder)
5533 MethodInfo base_method = m.GetBaseDefinition ();
5534 return base_method != m;
5537 bool TryType (ResolveContext ec, Type t)
5539 MethodGroupExpr mg = Expression.MemberLookup (ec.Compiler,
5540 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5541 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5545 MethodInfo result = null;
5546 MethodInfo tmp_move_next = null;
5547 PropertyExpr tmp_get_cur = null;
5548 Type tmp_enumerator_type = enumerator_type;
5549 foreach (MethodInfo mi in mg.Methods) {
5550 if (TypeManager.GetParameterData (mi).Count != 0)
5553 // Check whether GetEnumerator is public
5554 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5557 if (IsOverride (mi))
5560 enumerator_found = true;
5562 if (!GetEnumeratorFilter (ec, mi))
5565 if (result != null) {
5566 if (TypeManager.IsGenericType (result.ReturnType)) {
5567 if (!TypeManager.IsGenericType (mi.ReturnType))
5570 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5571 ec.Report.SymbolRelatedToPreviousError (t);
5572 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5573 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5574 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5578 // Always prefer generics enumerators
5579 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5580 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5581 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5584 ec.Report.SymbolRelatedToPreviousError (result);
5585 ec.Report.SymbolRelatedToPreviousError (mi);
5586 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5587 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5592 tmp_move_next = move_next;
5593 tmp_get_cur = get_current;
5594 tmp_enumerator_type = enumerator_type;
5595 if (mi.DeclaringType == t)
5599 if (result != null) {
5600 move_next = tmp_move_next;
5601 get_current = tmp_get_cur;
5602 enumerator_type = tmp_enumerator_type;
5603 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5604 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5606 if (t != expr.Type) {
5607 expr = Convert.ExplicitConversion (
5610 throw new InternalErrorException ();
5613 get_enumerator.InstanceExpression = expr;
5614 get_enumerator.IsBase = t != expr.Type;
5622 bool ProbeCollectionType (ResolveContext ec, Type t)
5624 int errors = ec.Report.Errors;
5625 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5626 if (TryType (ec, tt))
5631 if (ec.Report.Errors > errors)
5635 // Now try to find the method in the interfaces
5637 Type [] ifaces = TypeManager.GetInterfaces (t);
5638 foreach (Type i in ifaces){
5639 if (TryType (ec, i))
5646 public override bool Resolve (BlockContext ec)
5648 enumerator_type = TypeManager.ienumerator_type;
5650 bool is_dynamic = TypeManager.IsDynamicType (expr.Type);
5652 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5654 if (!ProbeCollectionType (ec, expr.Type)) {
5655 Error_Enumerator (ec);
5659 VarExpr ve = var_type as VarExpr;
5661 // Infer implicitly typed local variable from foreach enumerable type
5662 var_type = new TypeExpression (
5663 is_dynamic ? InternalType.Dynamic : get_current.PropertyInfo.PropertyType,
5667 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5668 if (var_type == null)
5671 enumerator = new TemporaryVariable (enumerator_type, loc);
5672 enumerator.Resolve (ec);
5674 init = new Invocation (get_enumerator, null);
5675 init = init.Resolve (ec);
5679 Expression move_next_expr;
5681 MemberInfo[] mi = new MemberInfo[] { move_next };
5682 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5683 mg.InstanceExpression = enumerator;
5685 move_next_expr = new Invocation (mg, null);
5688 get_current.InstanceExpression = enumerator;
5690 Statement block = new CollectionForeachStatement (
5691 var_type.Type, variable, get_current, statement, loc);
5693 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5696 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5697 if (implements_idisposable || !enumerator_type.IsSealed) {
5698 wrapper = new DisposableWrapper (this, implements_idisposable);
5700 wrapper = new NonDisposableWrapper (this);
5703 return wrapper.Resolve (ec);
5706 protected override void DoEmit (EmitContext ec)
5711 class NonDisposableWrapper : Statement {
5712 CollectionForeach parent;
5714 internal NonDisposableWrapper (CollectionForeach parent)
5716 this.parent = parent;
5719 protected override void CloneTo (CloneContext clonectx, Statement target)
5721 throw new NotSupportedException ();
5724 public override bool Resolve (BlockContext ec)
5726 return parent.ResolveLoop (ec);
5729 protected override void DoEmit (EmitContext ec)
5731 parent.EmitLoopInit (ec);
5732 parent.EmitLoopBody (ec);
5735 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5737 throw new NotSupportedException ();
5741 sealed class DisposableWrapper : ExceptionStatement
5743 CollectionForeach parent;
5744 bool implements_idisposable;
5746 internal DisposableWrapper (CollectionForeach parent, bool implements)
5748 this.parent = parent;
5749 this.implements_idisposable = implements;
5752 protected override void CloneTo (CloneContext clonectx, Statement target)
5754 throw new NotSupportedException ();
5757 public override bool Resolve (BlockContext ec)
5761 ec.StartFlowBranching (this);
5763 if (!parent.ResolveLoop (ec))
5766 ec.EndFlowBranching ();
5768 ok &= base.Resolve (ec);
5770 if (TypeManager.void_dispose_void == null) {
5771 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5772 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5777 protected override void EmitPreTryBody (EmitContext ec)
5779 parent.EmitLoopInit (ec);
5782 protected override void EmitTryBody (EmitContext ec)
5784 parent.EmitLoopBody (ec);
5787 protected override void EmitFinallyBody (EmitContext ec)
5789 Expression instance = parent.enumerator;
5790 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5791 ILGenerator ig = ec.ig;
5793 parent.enumerator.Emit (ec);
5795 Label call_dispose = ig.DefineLabel ();
5797 if (!implements_idisposable) {
5798 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5799 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5805 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5807 // using 'endfinally' to empty the evaluation stack
5808 ig.Emit (OpCodes.Endfinally);
5809 ig.MarkLabel (call_dispose);
5812 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5815 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5817 throw new NotSupportedException ();
5821 bool ResolveLoop (BlockContext ec)
5823 return loop.Resolve (ec);
5826 void EmitLoopInit (EmitContext ec)
5828 enumerator.EmitAssign (ec, init);
5831 void EmitLoopBody (EmitContext ec)
5836 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5838 enumerator_type = storey.MutateType (enumerator_type);
5839 init.MutateHoistedGenericType (storey);
5840 loop.MutateHoistedGenericType (storey);
5845 Expression variable;
5847 Statement statement;
5849 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5850 Statement stmt, Location l)
5853 this.variable = var;
5859 public Statement Statement {
5860 get { return statement; }
5863 public override bool Resolve (BlockContext ec)
5865 expr = expr.Resolve (ec);
5870 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5874 if (expr.Type == TypeManager.string_type) {
5875 statement = new ArrayForeach (this, 1);
5876 } else if (expr.Type.IsArray) {
5877 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5879 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5880 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5881 expr.ExprClassName);
5885 statement = new CollectionForeach (type, variable, expr, statement, loc);
5888 return statement.Resolve (ec);
5891 protected override void DoEmit (EmitContext ec)
5893 ILGenerator ig = ec.ig;
5895 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5896 ec.LoopBegin = ig.DefineLabel ();
5897 ec.LoopEnd = ig.DefineLabel ();
5899 statement.Emit (ec);
5901 ec.LoopBegin = old_begin;
5902 ec.LoopEnd = old_end;
5905 protected override void CloneTo (CloneContext clonectx, Statement t)
5907 Foreach target = (Foreach) t;
5909 target.type = type.Clone (clonectx);
5910 target.variable = variable.Clone (clonectx);
5911 target.expr = expr.Clone (clonectx);
5912 target.statement = statement.Clone (clonectx);
5915 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5917 statement.MutateHoistedGenericType (storey);