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);
97 public sealed class EmptyStatement : Statement
99 public EmptyStatement (Location loc)
104 public override bool Resolve (BlockContext ec)
109 public override bool ResolveUnreachable (BlockContext ec, bool warn)
114 public override void Emit (EmitContext ec)
118 protected override void DoEmit (EmitContext ec)
120 throw new NotSupportedException ();
123 protected override void CloneTo (CloneContext clonectx, Statement target)
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 bool Resolve (BlockContext ec)
156 Report.Debug (1, "START IF BLOCK", loc);
158 expr = expr.Resolve (ec);
163 // Dead code elimination
165 if (expr is Constant) {
166 bool take = !((Constant) expr).IsDefaultValue;
169 if (!TrueStatement.Resolve (ec))
172 if ((FalseStatement != null) &&
173 !FalseStatement.ResolveUnreachable (ec, true))
175 FalseStatement = null;
177 if (!TrueStatement.ResolveUnreachable (ec, true))
179 TrueStatement = null;
181 if ((FalseStatement != null) &&
182 !FalseStatement.Resolve (ec))
190 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
192 ok &= TrueStatement.Resolve (ec);
194 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
196 ec.CurrentBranching.CreateSibling ();
198 if (FalseStatement != null)
199 ok &= FalseStatement.Resolve (ec);
201 ec.EndFlowBranching ();
203 Report.Debug (1, "END IF BLOCK", loc);
208 protected override void DoEmit (EmitContext ec)
210 Label false_target = ec.DefineLabel ();
214 // If we're a boolean constant, Resolve() already
215 // eliminated dead code for us.
217 Constant c = expr as Constant;
219 c.EmitSideEffect (ec);
221 if (!c.IsDefaultValue)
222 TrueStatement.Emit (ec);
223 else if (FalseStatement != null)
224 FalseStatement.Emit (ec);
229 expr.EmitBranchable (ec, false_target, false);
231 TrueStatement.Emit (ec);
233 if (FalseStatement != null){
234 bool branch_emitted = false;
236 end = ec.DefineLabel ();
238 ec.Emit (OpCodes.Br, end);
239 branch_emitted = true;
242 ec.MarkLabel (false_target);
243 FalseStatement.Emit (ec);
248 ec.MarkLabel (false_target);
252 protected override void CloneTo (CloneContext clonectx, Statement t)
256 target.expr = expr.Clone (clonectx);
257 target.TrueStatement = TrueStatement.Clone (clonectx);
258 if (FalseStatement != null)
259 target.FalseStatement = FalseStatement.Clone (clonectx);
263 public class Do : Statement {
264 public Expression expr;
265 public Statement EmbeddedStatement;
267 public Do (Statement statement, BooleanExpression bool_expr, Location l)
270 EmbeddedStatement = statement;
274 public override bool Resolve (BlockContext ec)
278 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
280 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
282 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
283 if (!EmbeddedStatement.Resolve (ec))
285 ec.EndFlowBranching ();
287 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
288 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
290 expr = expr.Resolve (ec);
293 else if (expr is Constant){
294 bool infinite = !((Constant) expr).IsDefaultValue;
296 ec.CurrentBranching.CurrentUsageVector.Goto ();
299 ec.EndFlowBranching ();
304 protected override void DoEmit (EmitContext ec)
306 Label loop = ec.DefineLabel ();
307 Label old_begin = ec.LoopBegin;
308 Label old_end = ec.LoopEnd;
310 ec.LoopBegin = ec.DefineLabel ();
311 ec.LoopEnd = ec.DefineLabel ();
314 EmbeddedStatement.Emit (ec);
315 ec.MarkLabel (ec.LoopBegin);
318 // Dead code elimination
320 if (expr is Constant){
321 bool res = !((Constant) expr).IsDefaultValue;
323 expr.EmitSideEffect (ec);
325 ec.Emit (OpCodes.Br, loop);
327 expr.EmitBranchable (ec, loop, true);
329 ec.MarkLabel (ec.LoopEnd);
331 ec.LoopBegin = old_begin;
332 ec.LoopEnd = old_end;
335 protected override void CloneTo (CloneContext clonectx, Statement t)
339 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
340 target.expr = expr.Clone (clonectx);
344 public class While : Statement {
345 public Expression expr;
346 public Statement Statement;
347 bool infinite, empty;
349 public While (BooleanExpression bool_expr, Statement statement, Location l)
351 this.expr = bool_expr;
352 Statement = statement;
356 public override bool Resolve (BlockContext ec)
360 expr = expr.Resolve (ec);
365 // Inform whether we are infinite or not
367 if (expr is Constant){
368 bool value = !((Constant) expr).IsDefaultValue;
371 if (!Statement.ResolveUnreachable (ec, true))
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
381 ec.CurrentBranching.CreateSibling ();
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!Statement.Resolve (ec))
386 ec.EndFlowBranching ();
388 // There's no direct control flow from the end of the embedded statement to the end of the loop
389 ec.CurrentBranching.CurrentUsageVector.Goto ();
391 ec.EndFlowBranching ();
396 protected override void DoEmit (EmitContext ec)
399 expr.EmitSideEffect (ec);
403 Label old_begin = ec.LoopBegin;
404 Label old_end = ec.LoopEnd;
406 ec.LoopBegin = ec.DefineLabel ();
407 ec.LoopEnd = ec.DefineLabel ();
410 // Inform whether we are infinite or not
412 if (expr is Constant){
413 // expr is 'true', since the 'empty' case above handles the 'false' case
414 ec.MarkLabel (ec.LoopBegin);
415 expr.EmitSideEffect (ec);
417 ec.Emit (OpCodes.Br, ec.LoopBegin);
420 // Inform that we are infinite (ie, `we return'), only
421 // if we do not `break' inside the code.
423 ec.MarkLabel (ec.LoopEnd);
425 Label while_loop = ec.DefineLabel ();
427 ec.Emit (OpCodes.Br, ec.LoopBegin);
428 ec.MarkLabel (while_loop);
432 ec.MarkLabel (ec.LoopBegin);
435 expr.EmitBranchable (ec, while_loop, true);
437 ec.MarkLabel (ec.LoopEnd);
440 ec.LoopBegin = old_begin;
441 ec.LoopEnd = old_end;
444 public override void Emit (EmitContext ec)
449 protected override void CloneTo (CloneContext clonectx, Statement t)
451 While target = (While) t;
453 target.expr = expr.Clone (clonectx);
454 target.Statement = Statement.Clone (clonectx);
458 public class For : Statement {
460 Statement InitStatement;
462 public Statement Statement;
463 bool infinite, empty;
465 public For (Statement init_statement,
466 BooleanExpression test,
471 InitStatement = init_statement;
473 Increment = increment;
474 Statement = statement;
478 public override bool Resolve (BlockContext ec)
482 if (InitStatement != null){
483 if (!InitStatement.Resolve (ec))
488 Test = Test.Resolve (ec);
491 else if (Test is Constant){
492 bool value = !((Constant) Test).IsDefaultValue;
495 if (!Statement.ResolveUnreachable (ec, true))
497 if ((Increment != null) &&
498 !Increment.ResolveUnreachable (ec, false))
508 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
510 ec.CurrentBranching.CreateSibling ();
512 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
514 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
515 if (!Statement.Resolve (ec))
517 ec.EndFlowBranching ();
519 if (Increment != null){
520 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
521 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
524 if (!Increment.Resolve (ec))
529 // There's no direct control flow from the end of the embedded statement to the end of the loop
530 ec.CurrentBranching.CurrentUsageVector.Goto ();
532 ec.EndFlowBranching ();
537 protected override void DoEmit (EmitContext ec)
539 if (InitStatement != null)
540 InitStatement.Emit (ec);
543 Test.EmitSideEffect (ec);
547 Label old_begin = ec.LoopBegin;
548 Label old_end = ec.LoopEnd;
549 Label loop = ec.DefineLabel ();
550 Label test = ec.DefineLabel ();
552 ec.LoopBegin = ec.DefineLabel ();
553 ec.LoopEnd = ec.DefineLabel ();
555 ec.Emit (OpCodes.Br, test);
559 ec.MarkLabel (ec.LoopBegin);
564 // If test is null, there is no test, and we are just
569 // The Resolve code already catches the case for
570 // Test == Constant (false) so we know that
573 if (Test is Constant) {
574 Test.EmitSideEffect (ec);
575 ec.Emit (OpCodes.Br, loop);
577 Test.EmitBranchable (ec, loop, true);
581 ec.Emit (OpCodes.Br, loop);
582 ec.MarkLabel (ec.LoopEnd);
584 ec.LoopBegin = old_begin;
585 ec.LoopEnd = old_end;
588 protected override void CloneTo (CloneContext clonectx, Statement t)
590 For target = (For) t;
592 if (InitStatement != null)
593 target.InitStatement = InitStatement.Clone (clonectx);
595 target.Test = Test.Clone (clonectx);
596 if (Increment != null)
597 target.Increment = Increment.Clone (clonectx);
598 target.Statement = Statement.Clone (clonectx);
602 public class StatementExpression : Statement {
603 ExpressionStatement expr;
605 public StatementExpression (ExpressionStatement expr)
611 public override bool Resolve (BlockContext ec)
613 expr = expr.ResolveStatement (ec);
617 protected override void DoEmit (EmitContext ec)
619 expr.EmitStatement (ec);
622 public override string ToString ()
624 return "StatementExpression (" + expr + ")";
627 protected override void CloneTo (CloneContext clonectx, Statement t)
629 StatementExpression target = (StatementExpression) t;
631 target.expr = (ExpressionStatement) expr.Clone (clonectx);
636 // Simple version of statement list not requiring a block
638 public class StatementList : Statement
640 List<Statement> statements;
642 public StatementList (Statement first, Statement second)
644 statements = new List<Statement> () { first, second };
648 public IList<Statement> Statements {
655 public void Add (Statement statement)
657 statements.Add (statement);
660 public override bool Resolve (BlockContext ec)
662 foreach (var s in statements)
668 protected override void DoEmit (EmitContext ec)
670 foreach (var s in statements)
674 protected override void CloneTo (CloneContext clonectx, Statement target)
676 StatementList t = (StatementList) target;
678 t.statements = new List<Statement> (statements.Count);
679 foreach (Statement s in statements)
680 t.statements.Add (s.Clone (clonectx));
684 // A 'return' or a 'yield break'
685 public abstract class ExitStatement : Statement
687 protected bool unwind_protect;
688 protected abstract bool DoResolve (BlockContext ec);
690 public virtual void Error_FinallyClause (Report Report)
692 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
695 public sealed override bool Resolve (BlockContext ec)
700 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
702 ec.NeedReturnLabel ();
703 ec.CurrentBranching.CurrentUsageVector.Goto ();
709 /// Implements the return statement
711 public class Return : ExitStatement
713 protected Expression Expr;
714 public Return (Expression expr, Location l)
721 public Expression Expression {
728 protected override bool DoResolve (BlockContext ec)
731 if (ec.ReturnType == TypeManager.void_type)
734 ec.Report.Error (126, loc,
735 "An object of a type convertible to `{0}' is required for the return statement",
736 TypeManager.CSharpName (ec.ReturnType));
740 if (ec.CurrentBlock.Toplevel.IsIterator) {
741 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
742 "statement to return a value, or yield break to end the iteration");
745 AnonymousExpression am = ec.CurrentAnonymousMethod;
746 if (am == null && ec.ReturnType == TypeManager.void_type) {
747 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
748 ec.GetSignatureForError ());
751 Expr = Expr.Resolve (ec);
755 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
756 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
760 if (Expr.Type != ec.ReturnType) {
761 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
765 ec.Report.Error (1662, loc,
766 "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",
767 am.ContainerType, am.GetSignatureForError ());
776 protected override void DoEmit (EmitContext ec)
782 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
786 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
788 ec.Emit (OpCodes.Ret);
791 protected override void CloneTo (CloneContext clonectx, Statement t)
793 Return target = (Return) t;
794 // It's null for simple return;
796 target.Expr = Expr.Clone (clonectx);
800 public class Goto : Statement {
802 LabeledStatement label;
805 public override bool Resolve (BlockContext ec)
807 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
808 ec.CurrentBranching.CurrentUsageVector.Goto ();
812 public Goto (string label, Location l)
818 public string Target {
819 get { return target; }
822 public void SetResolvedTarget (LabeledStatement label)
825 label.AddReference ();
828 protected override void CloneTo (CloneContext clonectx, Statement target)
833 protected override void DoEmit (EmitContext ec)
836 throw new InternalErrorException ("goto emitted before target resolved");
837 Label l = label.LabelTarget (ec);
838 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
842 public class LabeledStatement : Statement {
848 FlowBranching.UsageVector vectors;
850 public LabeledStatement (string name, Location l)
856 public Label LabelTarget (EmitContext ec)
861 label = ec.DefineLabel ();
870 public bool IsDefined {
871 get { return defined; }
874 public bool HasBeenReferenced {
875 get { return referenced; }
878 public FlowBranching.UsageVector JumpOrigins {
879 get { return vectors; }
882 public void AddUsageVector (FlowBranching.UsageVector vector)
884 vector = vector.Clone ();
885 vector.Next = vectors;
889 protected override void CloneTo (CloneContext clonectx, Statement target)
894 public override bool Resolve (BlockContext ec)
896 // this flow-branching will be terminated when the surrounding block ends
897 ec.StartFlowBranching (this);
901 protected override void DoEmit (EmitContext ec)
904 ec.MarkLabel (label);
907 public void AddReference ()
915 /// `goto default' statement
917 public class GotoDefault : Statement {
919 public GotoDefault (Location l)
924 protected override void CloneTo (CloneContext clonectx, Statement target)
929 public override bool Resolve (BlockContext ec)
931 ec.CurrentBranching.CurrentUsageVector.Goto ();
933 if (ec.Switch == null) {
934 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
938 if (!ec.Switch.GotDefault) {
939 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
946 protected override void DoEmit (EmitContext ec)
948 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
953 /// `goto case' statement
955 public class GotoCase : Statement {
959 public GotoCase (Expression e, Location l)
965 public override bool Resolve (BlockContext ec)
967 if (ec.Switch == null){
968 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
972 ec.CurrentBranching.CurrentUsageVector.Goto ();
974 expr = expr.Resolve (ec);
978 Constant c = expr as Constant;
980 ec.Report.Error (150, expr.Location, "A constant value is expected");
984 TypeSpec type = ec.Switch.SwitchType;
985 Constant res = c.TryReduce (ec, type, c.Location);
987 c.Error_ValueCannotBeConverted (ec, loc, type, true);
991 if (!Convert.ImplicitStandardConversionExists (c, type))
992 ec.Report.Warning (469, 2, loc,
993 "The `goto case' value is not implicitly convertible to type `{0}'",
994 TypeManager.CSharpName (type));
996 object val = res.GetValue ();
998 val = SwitchLabel.NullStringCase;
1000 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1001 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1002 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1009 protected override void DoEmit (EmitContext ec)
1011 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1014 protected override void CloneTo (CloneContext clonectx, Statement t)
1016 GotoCase target = (GotoCase) t;
1018 target.expr = expr.Clone (clonectx);
1022 public class Throw : Statement {
1025 public Throw (Expression expr, Location l)
1031 public override bool Resolve (BlockContext ec)
1034 ec.CurrentBranching.CurrentUsageVector.Goto ();
1035 return ec.CurrentBranching.CheckRethrow (loc);
1038 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1039 ec.CurrentBranching.CurrentUsageVector.Goto ();
1044 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1045 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1047 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1052 protected override void DoEmit (EmitContext ec)
1055 ec.Emit (OpCodes.Rethrow);
1059 ec.Emit (OpCodes.Throw);
1063 protected override void CloneTo (CloneContext clonectx, Statement t)
1065 Throw target = (Throw) t;
1068 target.expr = expr.Clone (clonectx);
1072 public class Break : Statement {
1074 public Break (Location l)
1079 bool unwind_protect;
1081 public override bool Resolve (BlockContext ec)
1083 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1084 ec.CurrentBranching.CurrentUsageVector.Goto ();
1088 protected override void DoEmit (EmitContext ec)
1090 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1093 protected override void CloneTo (CloneContext clonectx, Statement t)
1099 public class Continue : Statement {
1101 public Continue (Location l)
1106 bool unwind_protect;
1108 public override bool Resolve (BlockContext ec)
1110 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1111 ec.CurrentBranching.CurrentUsageVector.Goto ();
1115 protected override void DoEmit (EmitContext ec)
1117 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1120 protected override void CloneTo (CloneContext clonectx, Statement t)
1126 public interface ILocalVariable
1128 void Emit (EmitContext ec);
1129 void EmitAssign (EmitContext ec);
1130 void EmitAddressOf (EmitContext ec);
1133 public interface IKnownVariable {
1134 Block Block { get; }
1135 Location Location { get; }
1139 // The information about a user-perceived local variable
1141 public class LocalInfo : IKnownVariable, ILocalVariable {
1142 public readonly FullNamedExpression Type;
1144 public TypeSpec VariableType;
1145 public readonly string Name;
1146 public readonly Location Location;
1147 public readonly Block Block;
1149 public VariableInfo VariableInfo;
1150 HoistedVariable hoisted_variant;
1159 CompilerGenerated = 64,
1163 public enum ReadOnlyContext: byte {
1170 ReadOnlyContext ro_context;
1171 LocalBuilder builder;
1173 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1181 public LocalInfo (TypeContainer ds, Block block, Location l)
1183 VariableType = ds.IsGeneric ? ds.CurrentType : ds.Definition;
1188 public void ResolveVariable (EmitContext ec)
1190 if (HoistedVariant != null)
1193 if (builder == null) {
1194 builder = ec.DeclareLocal (VariableType, Pinned);
1198 public void Emit (EmitContext ec)
1200 ec.Emit (OpCodes.Ldloc, builder);
1203 public void EmitAssign (EmitContext ec)
1205 ec.Emit (OpCodes.Stloc, builder);
1208 public void EmitAddressOf (EmitContext ec)
1210 ec.Emit (OpCodes.Ldloca, builder);
1213 public void EmitSymbolInfo (EmitContext ec)
1215 if (builder != null)
1216 ec.DefineLocalVariable (Name, builder);
1220 // Hoisted local variable variant
1222 public HoistedVariable HoistedVariant {
1224 return hoisted_variant;
1227 hoisted_variant = value;
1231 public bool IsThisAssigned (BlockContext ec, Block block)
1233 if (VariableInfo == null)
1234 throw new Exception ();
1236 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1239 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1242 public bool IsAssigned (BlockContext ec)
1244 if (VariableInfo == null)
1245 throw new Exception ();
1247 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1250 public bool Resolve (ResolveContext ec)
1252 if (VariableType != null)
1255 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1259 VariableType = texpr.Type;
1261 if (VariableType.IsStatic) {
1262 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1266 if (VariableType.IsPointer && !ec.IsUnsafe)
1267 Expression.UnsafeError (ec, Location);
1272 public bool IsConstant {
1273 get { return (flags & Flags.IsConstant) != 0; }
1274 set { flags |= Flags.IsConstant; }
1277 public bool AddressTaken {
1278 get { return (flags & Flags.AddressTaken) != 0; }
1279 set { flags |= Flags.AddressTaken; }
1282 public bool CompilerGenerated {
1283 get { return (flags & Flags.CompilerGenerated) != 0; }
1284 set { flags |= Flags.CompilerGenerated; }
1287 public override string ToString ()
1289 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1290 Name, Type, VariableInfo, Location);
1294 get { return (flags & Flags.Used) != 0; }
1295 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1298 public bool ReadOnly {
1299 get { return (flags & Flags.ReadOnly) != 0; }
1302 public void SetReadOnlyContext (ReadOnlyContext context)
1304 flags |= Flags.ReadOnly;
1305 ro_context = context;
1308 public string GetReadOnlyContext ()
1311 throw new InternalErrorException ("Variable is not readonly");
1313 switch (ro_context) {
1314 case ReadOnlyContext.Fixed:
1315 return "fixed variable";
1316 case ReadOnlyContext.Foreach:
1317 return "foreach iteration variable";
1318 case ReadOnlyContext.Using:
1319 return "using variable";
1321 throw new NotImplementedException ();
1325 // Whether the variable is pinned, if Pinned the variable has been
1326 // allocated in a pinned slot with DeclareLocal.
1328 public bool Pinned {
1329 get { return (flags & Flags.Pinned) != 0; }
1330 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1333 public bool IsThis {
1334 get { return (flags & Flags.IsThis) != 0; }
1335 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1338 Block IKnownVariable.Block {
1339 get { return Block; }
1342 Location IKnownVariable.Location {
1343 get { return Location; }
1346 public LocalInfo Clone (CloneContext clonectx)
1349 // Variables in anonymous block are not resolved yet
1351 if (VariableType == null)
1352 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1355 // Variables in method block are resolved
1357 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1358 li.VariableType = VariableType;
1364 /// Block represents a C# block.
1368 /// This class is used in a number of places: either to represent
1369 /// explicit blocks that the programmer places or implicit blocks.
1371 /// Implicit blocks are used as labels or to introduce variable
1374 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1375 /// they contain extra information that is not necessary on normal blocks.
1377 public class Block : Statement {
1378 public Block Parent;
1379 public Location StartLocation;
1380 public Location EndLocation = Location.Null;
1382 public ExplicitBlock Explicit;
1383 public ToplevelBlock Toplevel; // TODO: Use Explicit
1390 VariablesInitialized = 4,
1394 HasCapturedVariable = 64,
1395 HasCapturedThis = 1 << 7,
1396 IsExpressionTree = 1 << 8
1399 protected Flags flags;
1401 public bool Unchecked {
1402 get { return (flags & Flags.Unchecked) != 0; }
1403 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1406 public bool Unsafe {
1407 get { return (flags & Flags.Unsafe) != 0; }
1408 set { flags |= Flags.Unsafe; }
1412 // The statements in this block
1414 protected List<Statement> statements;
1417 // An array of Blocks. We keep track of children just
1418 // to generate the local variable declarations.
1420 // Statements and child statements are handled through the
1423 List<Block> children;
1426 // Labels. (label, block) pairs.
1428 protected Dictionary<string, LabeledStatement> labels;
1431 // Keeps track of (name, type) pairs
1433 Dictionary<string, LocalInfo> variables;
1436 // Keeps track of constants
1437 Dictionary<string, Expression> constants;
1440 // Temporary variables.
1442 List<LocalInfo> temporary_variables;
1445 // If this is a switch section, the enclosing switch block.
1449 protected List<Statement> scope_initializers;
1451 List<ToplevelBlock> anonymous_children;
1453 int? resolving_init_idx;
1455 protected static int id;
1459 int assignable_slots;
1460 bool unreachable_shown;
1463 public Block (Block parent)
1464 : this (parent, (Flags) 0, Location.Null, Location.Null)
1467 public Block (Block parent, Flags flags)
1468 : this (parent, flags, Location.Null, Location.Null)
1471 public Block (Block parent, Location start, Location end)
1472 : this (parent, (Flags) 0, start, end)
1476 // Useful when TopLevel block is downgraded to normal block
1478 public Block (ToplevelBlock parent, ToplevelBlock source)
1479 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1481 statements = source.statements;
1482 children = source.children;
1483 labels = source.labels;
1484 variables = source.variables;
1485 constants = source.constants;
1486 switch_block = source.switch_block;
1489 public Block (Block parent, Flags flags, Location start, Location end)
1491 if (parent != null) {
1492 parent.AddChild (this);
1494 // the appropriate constructors will fixup these fields
1495 Toplevel = parent.Toplevel;
1496 Explicit = parent.Explicit;
1499 this.Parent = parent;
1501 this.StartLocation = start;
1502 this.EndLocation = end;
1505 statements = new List<Statement> (4);
1511 get { return this_id; }
1514 public IDictionary<string, LocalInfo> Variables {
1516 if (variables == null)
1517 variables = new Dictionary<string, LocalInfo> ();
1524 public Block CreateSwitchBlock (Location start)
1526 // FIXME: should this be implicit?
1527 Block new_block = new ExplicitBlock (this, start, start);
1528 new_block.switch_block = this;
1532 void AddChild (Block b)
1534 if (children == null)
1535 children = new List<Block> (1);
1540 public void SetEndLocation (Location loc)
1545 protected void Error_158 (string name, Location loc)
1547 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1548 "by the same name in a contained scope", name);
1552 /// Adds a label to the current block.
1556 /// false if the name already exists in this block. true
1560 public bool AddLabel (LabeledStatement target)
1562 if (switch_block != null)
1563 return switch_block.AddLabel (target);
1565 string name = target.Name;
1568 while (cur != null) {
1569 LabeledStatement s = cur.DoLookupLabel (name);
1571 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1572 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1576 if (this == Explicit)
1582 while (cur != null) {
1583 if (cur.DoLookupLabel (name) != null) {
1584 Error_158 (name, target.loc);
1588 if (children != null) {
1589 foreach (Block b in children) {
1590 LabeledStatement s = b.DoLookupLabel (name);
1594 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1595 Error_158 (name, target.loc);
1603 Toplevel.CheckError158 (name, target.loc);
1606 labels = new Dictionary<string, LabeledStatement> ();
1608 labels.Add (name, target);
1612 public LabeledStatement LookupLabel (string name)
1614 LabeledStatement s = DoLookupLabel (name);
1618 if (children == null)
1621 foreach (Block child in children) {
1622 if (Explicit != child.Explicit)
1625 s = child.LookupLabel (name);
1633 LabeledStatement DoLookupLabel (string name)
1635 if (switch_block != null)
1636 return switch_block.LookupLabel (name);
1639 if (labels.ContainsKey (name))
1640 return labels [name];
1645 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1648 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1649 while (kvi == null) {
1650 b = b.Explicit.Parent;
1653 kvi = b.Explicit.GetKnownVariable (name);
1659 // Is kvi.Block nested inside 'b'
1660 if (b.Explicit != kvi.Block.Explicit) {
1662 // If a variable by the same name it defined in a nested block of this
1663 // block, we violate the invariant meaning in a block.
1666 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1667 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1672 // It's ok if the definition is in a nested subblock of b, but not
1673 // nested inside this block -- a definition in a sibling block
1674 // should not affect us.
1680 // Block 'b' and kvi.Block are the same textual block.
1681 // However, different variables are extant.
1683 // Check if the variable is in scope in both blocks. We use
1684 // an indirect check that depends on AddVariable doing its
1685 // part in maintaining the invariant-meaning-in-block property.
1687 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1690 if (this is ToplevelBlock) {
1691 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1692 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1697 // Even though we detected the error when the name is used, we
1698 // treat it as if the variable declaration was in error.
1700 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1701 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1705 protected bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1707 LocalInfo vi = GetLocalInfo (name);
1709 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1710 if (Explicit == vi.Block.Explicit) {
1711 Error_AlreadyDeclared (l, name, null);
1713 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1714 "parent or current" : "parent");
1719 if (block != null) {
1720 var tblock = block.CheckParameterNameConflict (name);
1721 if (tblock != null) {
1722 if (block == tblock && block is Linq.QueryBlock)
1723 Error_AlreadyDeclared (loc, name);
1725 Error_AlreadyDeclared (loc, name, "parent or current");
1734 public LocalInfo AddVariable (Expression type, string name, Location l)
1736 if (!CheckParentConflictName (Toplevel, name, l))
1739 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1741 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1742 Error_AlreadyDeclared (l, name, "child");
1746 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1749 if ((flags & Flags.VariablesInitialized) != 0)
1750 throw new InternalErrorException ("block has already been resolved");
1755 protected virtual void AddVariable (LocalInfo li)
1757 Variables.Add (li.Name, li);
1758 Explicit.AddKnownVariable (li.Name, li);
1761 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1763 if (reason == null) {
1764 Error_AlreadyDeclared (loc, var);
1768 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1769 "in this scope because it would give a different meaning " +
1770 "to `{0}', which is already used in a `{1}' scope " +
1771 "to denote something else", var, reason);
1774 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1776 Toplevel.Report.Error (128, loc,
1777 "A local variable named `{0}' is already defined in this scope", name);
1780 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1782 Toplevel.Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1786 public bool AddConstant (Expression type, string name, Expression value, Location l)
1788 if (AddVariable (type, name, l) == null)
1791 if (constants == null)
1792 constants = new Dictionary<string, Expression> ();
1794 constants.Add (name, value);
1796 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1801 static int next_temp_id = 0;
1803 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1805 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1807 if (temporary_variables == null)
1808 temporary_variables = new List<LocalInfo> ();
1810 int id = ++next_temp_id;
1811 string name = "$s_" + id.ToString ();
1813 LocalInfo li = new LocalInfo (te, name, this, loc);
1814 li.CompilerGenerated = true;
1815 temporary_variables.Add (li);
1819 public LocalInfo GetLocalInfo (string name)
1822 for (Block b = this; b != null; b = b.Parent) {
1823 if (b.variables != null && b.variables.TryGetValue (name, out ret)) {
1831 public Expression GetVariableType (string name)
1833 LocalInfo vi = GetLocalInfo (name);
1834 return vi == null ? null : vi.Type;
1837 public Expression GetConstantExpression (string name)
1840 for (Block b = this; b != null; b = b.Parent) {
1841 if (b.constants != null) {
1842 if (b.constants.TryGetValue (name, out ret))
1850 // It should be used by expressions which require to
1851 // register a statement during resolve process.
1853 public void AddScopeStatement (Statement s)
1855 if (scope_initializers == null)
1856 scope_initializers = new List<Statement> ();
1859 // Simple recursive helper, when resolve scope initializer another
1860 // new scope initializer can be added, this ensures it's initialized
1861 // before existing one. For now this can happen with expression trees
1862 // in base ctor initializer only
1864 if (resolving_init_idx.HasValue) {
1865 scope_initializers.Insert (resolving_init_idx.Value, s);
1866 ++resolving_init_idx;
1868 scope_initializers.Add (s);
1872 public void AddStatement (Statement s)
1875 flags |= Flags.BlockUsed;
1879 get { return (flags & Flags.BlockUsed) != 0; }
1884 flags |= Flags.BlockUsed;
1887 public bool HasRet {
1888 get { return (flags & Flags.HasRet) != 0; }
1891 public int AssignableSlots {
1894 // if ((flags & Flags.VariablesInitialized) == 0)
1895 // throw new Exception ("Variables have not been initialized yet");
1896 return assignable_slots;
1900 public IList<ToplevelBlock> AnonymousChildren {
1901 get { return anonymous_children; }
1904 public void AddAnonymousChild (ToplevelBlock b)
1906 if (anonymous_children == null)
1907 anonymous_children = new List<ToplevelBlock> ();
1909 anonymous_children.Add (b);
1912 void DoResolveConstants (BlockContext ec)
1914 if (constants == null)
1917 if (variables == null)
1918 throw new InternalErrorException ("cannot happen");
1920 foreach (var de in variables) {
1921 string name = de.Key;
1922 LocalInfo vi = de.Value;
1923 TypeSpec variable_type = vi.VariableType;
1925 if (variable_type == null) {
1926 if (vi.Type is VarExpr)
1927 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1933 if (!constants.TryGetValue (name, out cv))
1936 // Don't let 'const int Foo = Foo;' succeed.
1937 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1938 // which in turn causes the 'must be constant' error to be triggered.
1939 constants.Remove (name);
1941 if (!variable_type.IsConstantCompatible) {
1942 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1946 ec.CurrentBlock = this;
1948 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
\r
1949 using (ec.With (ResolveContext.Options.DoFlowAnalysis, false)) {
\r
1950 e = cv.Resolve (ec);
\r
1956 Constant ce = e as Constant;
1958 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1962 e = ce.ConvertImplicitly (ec, variable_type);
1964 if (TypeManager.IsReferenceType (variable_type))
1965 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1967 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1971 constants.Add (name, e);
1972 vi.IsConstant = true;
1976 protected void ResolveMeta (BlockContext ec, int offset)
1978 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1980 // If some parent block was unsafe, we remain unsafe even if this block
1981 // isn't explicitly marked as such.
1982 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
1983 flags |= Flags.VariablesInitialized;
1985 if (variables != null) {
1986 foreach (LocalInfo li in variables.Values) {
1987 if (!li.Resolve (ec))
1989 li.VariableInfo = new VariableInfo (li, offset);
1990 offset += li.VariableInfo.Length;
1993 assignable_slots = offset;
1995 DoResolveConstants (ec);
1997 if (children == null)
1999 foreach (Block b in children)
2000 b.ResolveMeta (ec, offset);
2005 // Emits the local variable declarations for a block
2007 public virtual void EmitMeta (EmitContext ec)
2009 if (variables != null){
2010 foreach (LocalInfo vi in variables.Values)
2011 vi.ResolveVariable (ec);
2014 if (temporary_variables != null) {
2015 for (int i = 0; i < temporary_variables.Count; i++)
2016 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2019 if (children != null) {
2020 for (int i = 0; i < children.Count; i++)
2021 ((Block)children[i]).EmitMeta(ec);
2025 void UsageWarning (BlockContext ec)
2027 if (variables == null || ec.Report.WarningLevel < 3)
2030 foreach (var de in variables) {
2031 LocalInfo vi = de.Value;
2034 string name = de.Key;
2036 // vi.VariableInfo can be null for 'catch' variables
2037 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2038 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2040 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2045 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2049 // Some statements are wrapped by a Block. Since
2050 // others' internal could be changed, here I treat
2051 // them as possibly wrapped by Block equally.
2052 Block b = s as Block;
2053 if (b != null && b.statements.Count == 1)
2054 s = (Statement) b.statements [0];
2057 body = ((Lock) s).Statement;
2059 body = ((For) s).Statement;
2060 else if (s is Foreach)
2061 body = ((Foreach) s).Statement;
2062 else if (s is While)
2063 body = ((While) s).Statement;
2064 else if (s is Fixed)
2065 body = ((Fixed) s).Statement;
2066 else if (s is Using)
2067 body = ((Using) s).EmbeddedStatement;
2068 else if (s is UsingTemporary)
2069 body = ((UsingTemporary) s).Statement;
2073 if (body == null || body is EmptyStatement)
2074 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2077 public override bool Resolve (BlockContext ec)
2079 Block prev_block = ec.CurrentBlock;
2082 int errors = ec.Report.Errors;
2084 ec.CurrentBlock = this;
2085 ec.StartFlowBranching (this);
2087 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2090 // Compiler generated scope statements
2092 if (scope_initializers != null) {
2093 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2094 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2097 resolving_init_idx = null;
2101 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2102 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2103 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2104 // responsible for handling the situation.
2106 int statement_count = statements.Count;
2107 for (int ix = 0; ix < statement_count; ix++){
2108 Statement s = statements [ix];
2109 // Check possible empty statement (CS0642)
2110 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2111 statements [ix + 1] is ExplicitBlock)
2112 CheckPossibleMistakenEmptyStatement (ec, s);
2115 // Warn if we detect unreachable code.
2118 if (s is EmptyStatement)
2121 if (!unreachable_shown && !(s is LabeledStatement)) {
2122 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2123 unreachable_shown = true;
2126 Block c_block = s as Block;
2127 if (c_block != null)
2128 c_block.unreachable = c_block.unreachable_shown = true;
2132 // Note that we're not using ResolveUnreachable() for unreachable
2133 // statements here. ResolveUnreachable() creates a temporary
2134 // flow branching and kills it afterwards. This leads to problems
2135 // if you have two unreachable statements where the first one
2136 // assigns a variable and the second one tries to access it.
2139 if (!s.Resolve (ec)) {
2141 if (ec.IsInProbingMode)
2144 statements [ix] = new EmptyStatement (s.loc);
2148 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2149 statements [ix] = new EmptyStatement (s.loc);
2151 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2152 if (unreachable && s is LabeledStatement)
2153 throw new InternalErrorException ("should not happen");
2156 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2157 ec.CurrentBranching, statement_count);
2159 while (ec.CurrentBranching is FlowBranchingLabeled)
2160 ec.EndFlowBranching ();
2162 bool flow_unreachable = ec.EndFlowBranching ();
2164 ec.CurrentBlock = prev_block;
2166 if (flow_unreachable)
2167 flags |= Flags.HasRet;
2169 // If we're a non-static `struct' constructor which doesn't have an
2170 // initializer, then we must initialize all of the struct's fields.
2171 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2174 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2175 foreach (LabeledStatement label in labels.Values)
2176 if (!label.HasBeenReferenced)
2177 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2180 if (ok && errors == ec.Report.Errors)
2186 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2188 unreachable_shown = true;
2192 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2194 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2195 bool ok = Resolve (ec);
2196 ec.KillFlowBranching ();
2201 protected override void DoEmit (EmitContext ec)
2203 for (int ix = 0; ix < statements.Count; ix++){
2204 statements [ix].Emit (ec);
2208 public override void Emit (EmitContext ec)
2210 if (scope_initializers != null)
2211 EmitScopeInitializers (ec);
2213 ec.Mark (StartLocation);
2216 if (SymbolWriter.HasSymbolWriter)
2217 EmitSymbolInfo (ec);
2220 protected void EmitScopeInitializers (EmitContext ec)
2222 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2224 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2225 foreach (Statement s in scope_initializers)
2229 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2232 protected virtual void EmitSymbolInfo (EmitContext ec)
2234 if (variables != null) {
2235 foreach (LocalInfo vi in variables.Values) {
2236 vi.EmitSymbolInfo (ec);
2241 public override string ToString ()
2243 return String.Format ("{0} ({1}:{2})", GetType (), this_id, StartLocation);
2246 protected override void CloneTo (CloneContext clonectx, Statement t)
2248 Block target = (Block) t;
2250 clonectx.AddBlockMap (this, target);
2252 target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2253 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2255 target.Parent = clonectx.RemapBlockCopy (Parent);
2257 if (variables != null){
2258 target.variables = new Dictionary<string, LocalInfo> ();
2260 foreach (var de in variables){
2261 LocalInfo newlocal = de.Value.Clone (clonectx);
2262 target.variables [de.Key] = newlocal;
2263 clonectx.AddVariableMap (de.Value, newlocal);
2267 target.statements = new List<Statement> (statements.Count);
2268 foreach (Statement s in statements)
2269 target.statements.Add (s.Clone (clonectx));
2271 if (target.children != null){
2272 target.children = new List<Block> (children.Count);
2273 foreach (Block b in children){
2274 target.children.Add (clonectx.LookupBlock (b));
2279 // TODO: labels, switch_block, constants (?), anonymous_children
2284 public class ExplicitBlock : Block
2286 Dictionary<string, IKnownVariable> known_variables;
2287 protected AnonymousMethodStorey am_storey;
2289 public ExplicitBlock (Block parent, Location start, Location end)
2290 : this (parent, (Flags) 0, start, end)
2294 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2295 : base (parent, flags, start, end)
2297 this.Explicit = this;
2301 // Marks a variable with name @name as being used in this or a child block.
2302 // If a variable name has been used in a child block, it's illegal to
2303 // declare a variable with the same name in the current block.
2305 internal void AddKnownVariable (string name, IKnownVariable info)
2307 if (known_variables == null)
2308 known_variables = new Dictionary<string, IKnownVariable> ();
2310 known_variables [name] = info;
2313 Parent.Explicit.AddKnownVariable (name, info);
2316 public AnonymousMethodStorey AnonymousMethodStorey {
2317 get { return am_storey; }
2321 // Creates anonymous method storey in current block
2323 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2326 // When referencing a variable in iterator storey from children anonymous method
2328 if (Toplevel.am_storey is IteratorStorey) {
2329 return Toplevel.am_storey;
2333 // An iterator has only 1 storey block
2335 if (ec.CurrentIterator != null)
2336 return ec.CurrentIterator.Storey;
2338 if (am_storey == null) {
2339 MemberBase mc = ec.MemberContext as MemberBase;
2340 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2343 // Creates anonymous method storey for this block
2345 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2351 public override void Emit (EmitContext ec)
2353 if (am_storey != null)
2354 am_storey.EmitStoreyInstantiation (ec);
2356 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2357 if (emit_debug_info)
2362 if (emit_debug_info)
2366 public override void EmitMeta (EmitContext ec)
2369 // Creates anonymous method storey
2371 if (am_storey != null) {
2372 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2374 // Creates parent storey reference when hoisted this is accessible
2376 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2377 ExplicitBlock parent = Toplevel.Parent.Explicit;
2380 // Hoisted this exists in top-level parent storey only
2382 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2383 parent = parent.Parent.Explicit;
2385 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2388 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2390 // TODO MemberCache: Review
2391 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2394 am_storey.CreateType ();
2395 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2396 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2398 am_storey.DefineType ();
2399 am_storey.ResolveTypeParameters ();
2400 am_storey.Define ();
2401 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2403 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2404 if (ref_blocks != null) {
2405 foreach (ExplicitBlock ref_block in ref_blocks) {
2406 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2407 if (b.am_storey != null) {
2408 b.am_storey.AddParentStoreyReference (ec, am_storey);
2410 // Stop propagation inside same top block
2411 if (b.Toplevel == Toplevel)
2416 b.HasCapturedVariable = true;
2425 public IKnownVariable GetKnownVariable (string name)
2427 if (known_variables == null)
2431 known_variables.TryGetValue (name, out kw);
2435 public bool HasCapturedThis
2437 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2438 get { return (flags & Flags.HasCapturedThis) != 0; }
2441 public bool HasCapturedVariable
2443 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2444 get { return (flags & Flags.HasCapturedVariable) != 0; }
2447 protected override void CloneTo (CloneContext clonectx, Statement t)
2449 ExplicitBlock target = (ExplicitBlock) t;
2450 target.known_variables = null;
2451 base.CloneTo (clonectx, t);
2455 public class ToplevelParameterInfo : IKnownVariable {
2456 public readonly ToplevelBlock Block;
2457 public readonly int Index;
2458 public VariableInfo VariableInfo;
2460 Block IKnownVariable.Block {
2461 get { return Block; }
2463 public Parameter Parameter {
2464 get { return Block.Parameters [Index]; }
2467 public TypeSpec ParameterType {
2468 get { return Block.Parameters.Types [Index]; }
2471 public Location Location {
2472 get { return Parameter.Location; }
2475 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2483 // A toplevel block contains extra information, the split is done
2484 // only to separate information that would otherwise bloat the more
2485 // lightweight Block.
2487 // In particular, this was introduced when the support for Anonymous
2488 // Methods was implemented.
2490 public class ToplevelBlock : ExplicitBlock
2493 // Block is converted to an expression
2495 sealed class BlockScopeExpression : Expression
2498 readonly ToplevelBlock block;
2500 public BlockScopeExpression (Expression child, ToplevelBlock block)
2506 public override Expression CreateExpressionTree (ResolveContext ec)
2508 throw new NotSupportedException ();
2511 protected override Expression DoResolve (ResolveContext ec)
2516 child = child.Resolve (ec);
2520 eclass = child.eclass;
2525 public override void Emit (EmitContext ec)
2527 block.EmitMeta (ec);
2528 block.EmitScopeInitializers (ec);
2533 protected ParametersCompiled parameters;
2534 protected ToplevelParameterInfo[] parameter_info;
2535 LocalInfo this_variable;
2538 CompilerContext compiler;
2540 public HoistedVariable HoistedThisVariable;
2542 public bool Resolved {
2549 // The parameters for the block.
2551 public ParametersCompiled Parameters {
2552 get { return parameters; }
2555 public Report Report {
2556 get { return compiler.Report; }
2559 public ToplevelBlock Container {
2560 get { return Parent == null ? null : Parent.Toplevel; }
2563 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2564 this (ctx, parent, (Flags) 0, parameters, start)
2568 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2569 this (ctx, null, (Flags) 0, parameters, start)
2573 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2574 this (ctx, null, flags, parameters, start)
2578 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2579 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2580 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2581 base (null, flags, start, Location.Null)
2583 this.compiler = ctx;
2584 this.Toplevel = this;
2586 this.parameters = parameters;
2587 this.Parent = parent;
2589 parent.AddAnonymousChild (this);
2591 if (!this.parameters.IsEmpty)
2592 ProcessParameters ();
2595 public ToplevelBlock (CompilerContext ctx, Location loc)
2596 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2600 protected override void CloneTo (CloneContext clonectx, Statement t)
2602 ToplevelBlock target = (ToplevelBlock) t;
2603 base.CloneTo (clonectx, t);
2605 if (parameters.Count != 0) {
2606 target.parameter_info = new ToplevelParameterInfo[parameters.Count];
2607 for (int i = 0; i < parameters.Count; ++i)
2608 target.parameter_info[i] = new ToplevelParameterInfo (target, i);
2612 public bool CheckError158 (string name, Location loc)
2614 if (AnonymousChildren != null) {
2615 foreach (ToplevelBlock child in AnonymousChildren) {
2616 if (!child.CheckError158 (name, loc))
2621 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2622 if (!c.DoCheckError158 (name, loc))
2629 void ProcessParameters ()
2631 int n = parameters.Count;
2632 parameter_info = new ToplevelParameterInfo [n];
2633 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2634 for (int i = 0; i < n; ++i) {
2635 parameter_info [i] = new ToplevelParameterInfo (this, i);
2637 Parameter p = parameters [i];
2641 string name = p.Name;
2642 if (CheckParentConflictName (top_parent, name, loc))
2643 AddKnownVariable (name, parameter_info [i]);
2646 // mark this block as "used" so that we create local declarations in a sub-block
2647 // FIXME: This appears to uncover a lot of bugs
2651 bool DoCheckError158 (string name, Location loc)
2653 LabeledStatement s = LookupLabel (name);
2655 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2656 Error_158 (name, loc);
2663 public override Expression CreateExpressionTree (ResolveContext ec)
2665 if (statements.Count == 1) {
2666 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2667 if (scope_initializers != null)
2668 expr = new BlockScopeExpression (expr, this);
2673 return base.CreateExpressionTree (ec);
2677 // Reformats this block to be top-level iterator block
2679 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2683 // Creates block with original statements
2684 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2686 source.statements = new List<Statement> (1);
2687 source.AddStatement (new Return (iterator, iterator.Location));
2688 source.IsIterator = false;
2690 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2691 source.am_storey = iterator_storey;
2692 return iterator_storey;
2696 // Returns a parameter reference expression for the given name,
2697 // or null if there is no such parameter
2699 public Expression GetParameterReference (string name, Location loc)
2701 for (ToplevelBlock t = this; t != null; t = t.Container) {
2702 if (t.parameters.IsEmpty)
2705 Expression expr = t.GetParameterReferenceExpression (name, loc);
2713 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2715 int idx = parameters.GetParameterIndexByName (name);
2717 null : new ParameterReference (parameter_info [idx], loc);
2720 public ToplevelBlock CheckParameterNameConflict (string name)
2722 for (ToplevelBlock t = this; t != null; t = t.Container) {
2723 if (t.HasParameterWithName (name))
2730 protected virtual bool HasParameterWithName (string name)
2732 return parameters.GetParameterIndexByName (name) >= 0;
2736 // Returns the "this" instance variable of this block.
2737 // See AddThisVariable() for more information.
2739 public LocalInfo ThisVariable {
2740 get { return this_variable; }
2744 // This is used by non-static `struct' constructors which do not have an
2745 // initializer - in this case, the constructor must initialize all of the
2746 // struct's fields. To do this, we add a "this" variable and use the flow
2747 // analysis code to ensure that it's been fully initialized before control
2748 // leaves the constructor.
2750 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2752 if (this_variable == null) {
2753 this_variable = new LocalInfo (ds, this, l);
2754 this_variable.Used = true;
2755 this_variable.IsThis = true;
2757 Variables.Add ("this", this_variable);
2760 return this_variable;
2763 public bool IsIterator {
2764 get { return (flags & Flags.IsIterator) != 0; }
2765 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2769 // Block has been converted to expression tree
2771 public bool IsExpressionTree {
2772 get { return (flags & Flags.IsExpressionTree) != 0; }
2775 public bool IsThisAssigned (BlockContext ec)
2777 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2780 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2787 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2788 flags |= Flags.IsExpressionTree;
2791 if (!ResolveMeta (rc, ip))
2794 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2795 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2800 unreachable = top_level.End ();
2802 } catch (Exception e) {
2803 if (e is CompletionResult || rc.Report.IsDisabled)
2806 if (rc.CurrentBlock != null) {
2807 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2809 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2812 if (Report.DebugFlags > 0)
2816 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2817 if (rc.CurrentAnonymousMethod == null) {
2818 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2820 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2821 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2822 rc.CurrentAnonymousMethod.GetSignatureForError ());
2830 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2832 int errors = ec.Report.Errors;
2833 int orig_count = parameters.Count;
2838 // Assert: orig_count != parameter.Count => orig_count == 0
2839 if (orig_count != 0 && orig_count != parameters.Count)
2840 throw new InternalErrorException ("parameter information mismatch");
2842 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2844 for (int i = 0; i < orig_count; ++i) {
2845 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2847 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2850 VariableInfo vi = new VariableInfo (ip, i, offset);
2851 parameter_info [i].VariableInfo = vi;
2852 offset += vi.Length;
2855 ResolveMeta (ec, offset);
2857 return ec.Report.Errors == errors;
2861 // Check whether all `out' parameters have been assigned.
2863 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2865 if (vector.IsUnreachable)
2868 int n = parameter_info == null ? 0 : parameter_info.Length;
2870 for (int i = 0; i < n; i++) {
2871 VariableInfo var = parameter_info [i].VariableInfo;
2876 if (vector.IsAssigned (var, false))
2879 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2884 public override void Emit (EmitContext ec)
2886 if (Report.Errors > 0)
2894 if (ec.HasReturnLabel)
2895 ec.ReturnLabel = ec.DefineLabel ();
2899 ec.Mark (EndLocation);
2901 if (ec.HasReturnLabel)
2902 ec.MarkLabel (ec.ReturnLabel);
2904 if (ec.return_value != null) {
2905 ec.Emit (OpCodes.Ldloc, ec.return_value);
2906 ec.Emit (OpCodes.Ret);
2909 // If `HasReturnLabel' is set, then we already emitted a
2910 // jump to the end of the method, so we must emit a `ret'
2913 // Unfortunately, System.Reflection.Emit automatically emits
2914 // a leave to the end of a finally block. This is a problem
2915 // if no code is following the try/finally block since we may
2916 // jump to a point after the end of the method.
2917 // As a workaround, we're always creating a return label in
2921 if (ec.HasReturnLabel || !unreachable) {
2922 if (ec.ReturnType != TypeManager.void_type)
2923 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2924 ec.Emit (OpCodes.Ret);
2929 } catch (Exception e){
2930 Console.WriteLine ("Exception caught by the compiler while emitting:");
2931 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2933 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2939 public override void EmitMeta (EmitContext ec)
2941 // Avoid declaring an IL variable for this_variable since it is not accessed
2942 // from the generated IL
2943 if (this_variable != null)
2944 Variables.Remove ("this");
2948 protected override void EmitSymbolInfo (EmitContext ec)
2950 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2951 if ((ae != null) && (ae.Storey != null))
2952 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2954 base.EmitSymbolInfo (ec);
2958 public class SwitchLabel {
2965 Label il_label_code;
2966 bool il_label_code_set;
2968 public static readonly object NullStringCase = new object ();
2971 // if expr == null, then it is the default case.
2973 public SwitchLabel (Expression expr, Location l)
2979 public Expression Label {
2985 public Location Location {
2989 public object Converted {
2995 public Label GetILLabel (EmitContext ec)
2998 il_label = ec.DefineLabel ();
2999 il_label_set = true;
3004 public Label GetILLabelCode (EmitContext ec)
3006 if (!il_label_code_set){
3007 il_label_code = ec.DefineLabel ();
3008 il_label_code_set = true;
3010 return il_label_code;
3014 // Resolves the expression, reduces it to a literal if possible
3015 // and then converts it to the requested type.
3017 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3019 Expression e = label.Resolve (ec);
3024 Constant c = e as Constant;
3026 ec.Report.Error (150, loc, "A constant value is expected");
3030 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3031 converted = NullStringCase;
3035 if (allow_nullable && c.GetValue () == null) {
3036 converted = NullStringCase;
3040 c = c.ImplicitConversionRequired (ec, required_type, loc);
3044 converted = c.GetValue ();
3048 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3051 if (converted == null)
3053 else if (converted == NullStringCase)
3056 label = converted.ToString ();
3058 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3059 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3062 public SwitchLabel Clone (CloneContext clonectx)
3064 return new SwitchLabel (label.Clone (clonectx), loc);
3068 public class SwitchSection {
3069 // An array of SwitchLabels.
3070 public readonly List<SwitchLabel> Labels;
3071 public readonly Block Block;
3073 public SwitchSection (List<SwitchLabel> labels, Block block)
3079 public SwitchSection Clone (CloneContext clonectx)
3081 var cloned_labels = new List<SwitchLabel> ();
3083 foreach (SwitchLabel sl in cloned_labels)
3084 cloned_labels.Add (sl.Clone (clonectx));
3086 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3090 public class Switch : Statement {
3091 public List<SwitchSection> Sections;
3092 public Expression Expr;
3095 /// Maps constants whose type type SwitchType to their SwitchLabels.
3097 public IDictionary<object, SwitchLabel> Elements;
3100 /// The governing switch type
3102 public TypeSpec SwitchType;
3107 Label default_target;
3109 Expression new_expr;
3112 SwitchSection constant_section;
3113 SwitchSection default_section;
3115 ExpressionStatement string_dictionary;
3116 FieldExpr switch_cache_field;
3117 static int unique_counter;
3120 // Nullable Types support
3122 Nullable.Unwrap unwrap;
3124 protected bool HaveUnwrap {
3125 get { return unwrap != null; }
3129 // The types allowed to be implicitly cast from
3130 // on the governing type
3132 static TypeSpec [] allowed_types;
3134 public Switch (Expression e, List<SwitchSection> sects, Location l)
3141 public bool GotDefault {
3143 return default_section != null;
3147 public Label DefaultTarget {
3149 return default_target;
3154 // Determines the governing type for a switch. The returned
3155 // expression might be the expression from the switch, or an
3156 // expression that includes any potential conversions to the
3157 // integral types or to string.
3159 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3161 TypeSpec t = expr.Type;
3163 if (t == TypeManager.byte_type ||
3164 t == TypeManager.sbyte_type ||
3165 t == TypeManager.ushort_type ||
3166 t == TypeManager.short_type ||
3167 t == TypeManager.uint32_type ||
3168 t == TypeManager.int32_type ||
3169 t == TypeManager.uint64_type ||
3170 t == TypeManager.int64_type ||
3171 t == TypeManager.char_type ||
3172 t == TypeManager.string_type ||
3173 t == TypeManager.bool_type ||
3174 TypeManager.IsEnumType (t))
3177 if (allowed_types == null){
3178 allowed_types = new TypeSpec [] {
3179 TypeManager.sbyte_type,
3180 TypeManager.byte_type,
3181 TypeManager.short_type,
3182 TypeManager.ushort_type,
3183 TypeManager.int32_type,
3184 TypeManager.uint32_type,
3185 TypeManager.int64_type,
3186 TypeManager.uint64_type,
3187 TypeManager.char_type,
3188 TypeManager.string_type
3193 // Try to find a *user* defined implicit conversion.
3195 // If there is no implicit conversion, or if there are multiple
3196 // conversions, we have to report an error
3198 Expression converted = null;
3199 foreach (TypeSpec tt in allowed_types){
3202 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3207 // Ignore over-worked ImplicitUserConversions that do
3208 // an implicit conversion in addition to the user conversion.
3210 if (!(e is UserCast))
3213 if (converted != null){
3214 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3224 // Performs the basic sanity checks on the switch statement
3225 // (looks for duplicate keys and non-constant expressions).
3227 // It also returns a hashtable with the keys that we will later
3228 // use to compute the switch tables
3230 bool CheckSwitch (ResolveContext ec)
3233 Elements = new Dictionary<object, SwitchLabel> ();
3235 foreach (SwitchSection ss in Sections){
3236 foreach (SwitchLabel sl in ss.Labels){
3237 if (sl.Label == null){
3238 if (default_section != null){
3239 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3242 default_section = ss;
3246 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3251 object key = sl.Converted;
3252 if (key == SwitchLabel.NullStringCase)
3253 has_null_case = true;
3256 Elements.Add (key, sl);
3257 } catch (ArgumentException) {
3258 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3266 void EmitObjectInteger (EmitContext ec, object k)
3269 ec.EmitInt ((int) k);
3270 else if (k is Constant) {
3271 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3274 ec.EmitInt (unchecked ((int) (uint) k));
3277 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3279 ec.EmitInt ((int) (long) k);
3280 ec.Emit (OpCodes.Conv_I8);
3283 ec.EmitLong ((long) k);
3285 else if (k is ulong)
3287 ulong ul = (ulong) k;
3290 ec.EmitInt (unchecked ((int) ul));
3291 ec.Emit (OpCodes.Conv_U8);
3295 ec.EmitLong (unchecked ((long) ul));
3299 ec.EmitInt ((int) ((char) k));
3300 else if (k is sbyte)
3301 ec.EmitInt ((int) ((sbyte) k));
3303 ec.EmitInt ((int) ((byte) k));
3304 else if (k is short)
3305 ec.EmitInt ((int) ((short) k));
3306 else if (k is ushort)
3307 ec.EmitInt ((int) ((ushort) k));
3309 ec.EmitInt (((bool) k) ? 1 : 0);
3311 throw new Exception ("Unhandled case");
3314 // structure used to hold blocks of keys while calculating table switch
3315 class KeyBlock : IComparable
3317 public KeyBlock (long _first)
3319 first = last = _first;
3323 public List<object> element_keys;
3324 // how many items are in the bucket
3325 public int Size = 1;
3328 get { return (int) (last - first + 1); }
3330 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3332 return kb_last.last - kb_first.first + 1;
3334 public int CompareTo (object obj)
3336 KeyBlock kb = (KeyBlock) obj;
3337 int nLength = Length;
3338 int nLengthOther = kb.Length;
3339 if (nLengthOther == nLength)
3340 return (int) (kb.first - first);
3341 return nLength - nLengthOther;
3346 /// This method emits code for a lookup-based switch statement (non-string)
3347 /// Basically it groups the cases into blocks that are at least half full,
3348 /// and then spits out individual lookup opcodes for each block.
3349 /// It emits the longest blocks first, and short blocks are just
3350 /// handled with direct compares.
3352 /// <param name="ec"></param>
3353 /// <param name="val"></param>
3354 /// <returns></returns>
3355 void TableSwitchEmit (EmitContext ec, Expression val)
3357 int element_count = Elements.Count;
3358 object [] element_keys = new object [element_count];
3359 Elements.Keys.CopyTo (element_keys, 0);
3360 Array.Sort (element_keys);
3362 // initialize the block list with one element per key
3363 var key_blocks = new List<KeyBlock> (element_count);
3364 foreach (object key in element_keys)
3365 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3367 KeyBlock current_kb;
3368 // iteratively merge the blocks while they are at least half full
3369 // there's probably a really cool way to do this with a tree...
3370 while (key_blocks.Count > 1)
3372 var key_blocks_new = new List<KeyBlock> ();
3373 current_kb = (KeyBlock) key_blocks [0];
3374 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3376 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3377 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3380 current_kb.last = kb.last;
3381 current_kb.Size += kb.Size;
3385 // start a new block
3386 key_blocks_new.Add (current_kb);
3390 key_blocks_new.Add (current_kb);
3391 if (key_blocks.Count == key_blocks_new.Count)
3393 key_blocks = key_blocks_new;
3396 // initialize the key lists
3397 foreach (KeyBlock kb in key_blocks)
3398 kb.element_keys = new List<object> ();
3400 // fill the key lists
3402 if (key_blocks.Count > 0) {
3403 current_kb = (KeyBlock) key_blocks [0];
3404 foreach (object key in element_keys)
3406 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3407 System.Convert.ToInt64 (key) > current_kb.last;
3409 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3410 current_kb.element_keys.Add (key);
3414 // sort the blocks so we can tackle the largest ones first
3417 // okay now we can start...
3418 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3419 Label lbl_default = default_target;
3421 Type type_keys = null;
3422 if (element_keys.Length > 0)
3423 type_keys = element_keys [0].GetType (); // used for conversions
3425 TypeSpec compare_type;
3427 if (TypeManager.IsEnumType (SwitchType))
3428 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3430 compare_type = SwitchType;
3432 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3434 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3435 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3438 foreach (object key in kb.element_keys) {
3439 SwitchLabel sl = (SwitchLabel) Elements [key];
3440 if (key is int && (int) key == 0) {
3441 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3444 EmitObjectInteger (ec, key);
3445 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3451 // TODO: if all the keys in the block are the same and there are
3452 // no gaps/defaults then just use a range-check.
3453 if (compare_type == TypeManager.int64_type ||
3454 compare_type == TypeManager.uint64_type)
3456 // TODO: optimize constant/I4 cases
3458 // check block range (could be > 2^31)
3460 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3461 ec.Emit (OpCodes.Blt, lbl_default);
3463 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3464 ec.Emit (OpCodes.Bgt, lbl_default);
3470 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3471 ec.Emit (OpCodes.Sub);
3473 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3479 int first = (int) kb.first;
3483 ec.Emit (OpCodes.Sub);
3487 ec.EmitInt (-first);
3488 ec.Emit (OpCodes.Add);
3492 // first, build the list of labels for the switch
3494 int cJumps = kb.Length;
3495 Label [] switch_labels = new Label [cJumps];
3496 for (int iJump = 0; iJump < cJumps; iJump++)
3498 object key = kb.element_keys [iKey];
3499 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3501 SwitchLabel sl = (SwitchLabel) Elements [key];
3502 switch_labels [iJump] = sl.GetILLabel (ec);
3506 switch_labels [iJump] = lbl_default;
3508 // emit the switch opcode
3509 ec.Emit (OpCodes.Switch, switch_labels);
3512 // mark the default for this block
3514 ec.MarkLabel (lbl_default);
3517 // TODO: find the default case and emit it here,
3518 // to prevent having to do the following jump.
3519 // make sure to mark other labels in the default section
3521 // the last default just goes to the end
3522 if (element_keys.Length > 0)
3523 ec.Emit (OpCodes.Br, lbl_default);
3525 // now emit the code for the sections
3526 bool found_default = false;
3528 foreach (SwitchSection ss in Sections) {
3529 foreach (SwitchLabel sl in ss.Labels) {
3530 if (sl.Converted == SwitchLabel.NullStringCase) {
3531 ec.MarkLabel (null_target);
3532 } else if (sl.Label == null) {
3533 ec.MarkLabel (lbl_default);
3534 found_default = true;
3536 ec.MarkLabel (null_target);
3538 ec.MarkLabel (sl.GetILLabel (ec));
3539 ec.MarkLabel (sl.GetILLabelCode (ec));
3544 if (!found_default) {
3545 ec.MarkLabel (lbl_default);
3546 if (!has_null_case) {
3547 ec.MarkLabel (null_target);
3551 ec.MarkLabel (lbl_end);
3554 SwitchSection FindSection (SwitchLabel label)
3556 foreach (SwitchSection ss in Sections){
3557 foreach (SwitchLabel sl in ss.Labels){
3566 public static void Reset ()
3569 allowed_types = null;
3572 public override bool Resolve (BlockContext ec)
3574 Expr = Expr.Resolve (ec);
3578 new_expr = SwitchGoverningType (ec, Expr);
3580 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3581 unwrap = Nullable.Unwrap.Create (Expr, false);
3585 new_expr = SwitchGoverningType (ec, unwrap);
3588 if (new_expr == null){
3589 ec.Report.Error (151, loc,
3590 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3591 TypeManager.CSharpName (Expr.Type));
3596 SwitchType = new_expr.Type;
3598 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3599 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3603 if (!CheckSwitch (ec))
3607 Elements.Remove (SwitchLabel.NullStringCase);
3609 Switch old_switch = ec.Switch;
3611 ec.Switch.SwitchType = SwitchType;
3613 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3614 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3616 var constant = new_expr as Constant;
3617 if (constant != null) {
3619 object key = constant.GetValue ();
3621 if (Elements.TryGetValue (key, out label))
3622 constant_section = FindSection (label);
3624 if (constant_section == null)
3625 constant_section = default_section;
3630 foreach (SwitchSection ss in Sections){
3632 ec.CurrentBranching.CreateSibling (
3633 null, FlowBranching.SiblingType.SwitchSection);
3637 if (is_constant && (ss != constant_section)) {
3638 // If we're a constant switch, we're only emitting
3639 // one single section - mark all the others as
3641 ec.CurrentBranching.CurrentUsageVector.Goto ();
3642 if (!ss.Block.ResolveUnreachable (ec, true)) {
3646 if (!ss.Block.Resolve (ec))
3651 if (default_section == null)
3652 ec.CurrentBranching.CreateSibling (
3653 null, FlowBranching.SiblingType.SwitchSection);
3655 ec.EndFlowBranching ();
3656 ec.Switch = old_switch;
3658 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3663 if (SwitchType == TypeManager.string_type && !is_constant) {
3664 // TODO: Optimize single case, and single+default case
3665 ResolveStringSwitchMap (ec);
3671 void ResolveStringSwitchMap (ResolveContext ec)
3673 FullNamedExpression string_dictionary_type;
3674 if (TypeManager.generic_ienumerable_type != null) {
3675 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3676 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3678 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3680 new TypeExpression (TypeManager.string_type, loc),
3681 new TypeExpression (TypeManager.int32_type, loc)), loc);
3683 MemberAccess system_collections_generic = new MemberAccess (
3684 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3686 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3689 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3690 Field field = new Field (ctype, string_dictionary_type,
3691 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3692 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3693 if (!field.Define ())
3695 ctype.AddField (field);
3697 var init = new List<Expression> ();
3700 string value = null;
3701 foreach (SwitchSection section in Sections) {
3702 int last_count = init.Count;
3703 foreach (SwitchLabel sl in section.Labels) {
3704 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3707 value = (string) sl.Converted;
3708 var init_args = new List<Expression> (2);
3709 init_args.Add (new StringLiteral (value, sl.Location));
3710 init_args.Add (new IntConstant (counter, loc));
3711 init.Add (new CollectionElementInitializer (init_args, loc));
3715 // Don't add empty sections
3717 if (last_count == init.Count)
3720 Elements.Add (counter, section.Labels [0]);
3724 Arguments args = new Arguments (1);
3725 args.Add (new Argument (new IntConstant (init.Count, loc)));
3726 Expression initializer = new NewInitialize (string_dictionary_type, args,
3727 new CollectionOrObjectInitializers (init, loc), loc);
3729 switch_cache_field = new FieldExpr (field, loc);
3730 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3733 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3735 Label l_initialized = ec.DefineLabel ();
3738 // Skip initialization when value is null
3740 value.EmitBranchable (ec, null_target, false);
3743 // Check if string dictionary is initialized and initialize
3745 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3746 string_dictionary.EmitStatement (ec);
3747 ec.MarkLabel (l_initialized);
3749 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3751 ResolveContext rc = new ResolveContext (ec.MemberContext);
3753 if (TypeManager.generic_ienumerable_type != null) {
3754 Arguments get_value_args = new Arguments (2);
3755 get_value_args.Add (new Argument (value));
3756 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3757 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3758 if (get_item == null)
3762 // A value was not found, go to default case
3764 get_item.EmitBranchable (ec, default_target, false);
3766 Arguments get_value_args = new Arguments (1);
3767 get_value_args.Add (new Argument (value));
3769 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3770 if (get_item == null)
3773 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3774 get_item_object.EmitAssign (ec, get_item, true, false);
3775 ec.Emit (OpCodes.Brfalse, default_target);
3777 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3778 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3780 get_item_int.EmitStatement (ec);
3781 get_item_object.Release (ec);
3784 TableSwitchEmit (ec, string_switch_variable);
3785 string_switch_variable.Release (ec);
3788 protected override void DoEmit (EmitContext ec)
3790 default_target = ec.DefineLabel ();
3791 null_target = ec.DefineLabel ();
3793 // Store variable for comparission purposes
3794 // TODO: Don't duplicate non-captured VariableReference
3795 LocalTemporary value;
3797 value = new LocalTemporary (SwitchType);
3798 unwrap.EmitCheck (ec);
3799 ec.Emit (OpCodes.Brfalse, null_target);
3802 } else if (!is_constant) {
3803 value = new LocalTemporary (SwitchType);
3810 // Setup the codegen context
3812 Label old_end = ec.LoopEnd;
3813 Switch old_switch = ec.Switch;
3815 ec.LoopEnd = ec.DefineLabel ();
3820 if (constant_section != null)
3821 constant_section.Block.Emit (ec);
3822 } else if (string_dictionary != null) {
3823 DoEmitStringSwitch (value, ec);
3825 TableSwitchEmit (ec, value);
3831 // Restore context state.
3832 ec.MarkLabel (ec.LoopEnd);
3835 // Restore the previous context
3837 ec.LoopEnd = old_end;
3838 ec.Switch = old_switch;
3841 protected override void CloneTo (CloneContext clonectx, Statement t)
3843 Switch target = (Switch) t;
3845 target.Expr = Expr.Clone (clonectx);
3846 target.Sections = new List<SwitchSection> ();
3847 foreach (SwitchSection ss in Sections){
3848 target.Sections.Add (ss.Clone (clonectx));
3853 // A place where execution can restart in an iterator
3854 public abstract class ResumableStatement : Statement
3857 protected Label resume_point;
3859 public Label PrepareForEmit (EmitContext ec)
3863 resume_point = ec.DefineLabel ();
3865 return resume_point;
3868 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3872 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3877 // Base class for statements that are implemented in terms of try...finally
3878 public abstract class ExceptionStatement : ResumableStatement
3882 List<ResumableStatement> resume_points;
3883 int first_resume_pc;
3885 protected abstract void EmitPreTryBody (EmitContext ec);
3886 protected abstract void EmitTryBody (EmitContext ec);
3887 protected abstract void EmitFinallyBody (EmitContext ec);
3889 protected sealed override void DoEmit (EmitContext ec)
3891 EmitPreTryBody (ec);
3893 if (resume_points != null) {
3894 ec.EmitInt ((int) Iterator.State.Running);
3895 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3898 ec.BeginExceptionBlock ();
3900 if (resume_points != null) {
3901 ec.MarkLabel (resume_point);
3903 // For normal control flow, we want to fall-through the Switch
3904 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3905 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3906 ec.EmitInt (first_resume_pc);
3907 ec.Emit (OpCodes.Sub);
3909 Label [] labels = new Label [resume_points.Count];
3910 for (int i = 0; i < resume_points.Count; ++i)
3911 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3912 ec.Emit (OpCodes.Switch, labels);
3917 ec.BeginFinallyBlock ();
3919 Label start_finally = ec.DefineLabel ();
3920 if (resume_points != null) {
3921 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3922 ec.Emit (OpCodes.Brfalse_S, start_finally);
3923 ec.Emit (OpCodes.Endfinally);
3926 ec.MarkLabel (start_finally);
3927 EmitFinallyBody (ec);
3929 ec.EndExceptionBlock ();
3932 public void SomeCodeFollows ()
3934 code_follows = true;
3937 public override bool Resolve (BlockContext ec)
3939 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3940 // So, ensure there's some IL code after this statement.
3941 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3942 ec.NeedReturnLabel ();
3944 iter = ec.CurrentIterator;
3948 public void AddResumePoint (ResumableStatement stmt, int pc)
3950 if (resume_points == null) {
3951 resume_points = new List<ResumableStatement> ();
3952 first_resume_pc = pc;
3955 if (pc != first_resume_pc + resume_points.Count)
3956 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3958 resume_points.Add (stmt);
3961 Label dispose_try_block;
3962 bool prepared_for_dispose, emitted_dispose;
3963 public override Label PrepareForDispose (EmitContext ec, Label end)
3965 if (!prepared_for_dispose) {
3966 prepared_for_dispose = true;
3967 dispose_try_block = ec.DefineLabel ();
3969 return dispose_try_block;
3972 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3974 if (emitted_dispose)
3977 emitted_dispose = true;
3979 Label end_of_try = ec.DefineLabel ();
3981 // Ensure that the only way we can get into this code is through a dispatcher
3982 if (have_dispatcher)
3983 ec.Emit (OpCodes.Br, end);
3985 ec.BeginExceptionBlock ();
3987 ec.MarkLabel (dispose_try_block);
3989 Label [] labels = null;
3990 for (int i = 0; i < resume_points.Count; ++i) {
3991 ResumableStatement s = (ResumableStatement) resume_points [i];
3992 Label ret = s.PrepareForDispose (ec, end_of_try);
3993 if (ret.Equals (end_of_try) && labels == null)
3995 if (labels == null) {
3996 labels = new Label [resume_points.Count];
3997 for (int j = 0; j < i; ++j)
3998 labels [j] = end_of_try;
4003 if (labels != null) {
4005 for (j = 1; j < labels.Length; ++j)
4006 if (!labels [0].Equals (labels [j]))
4008 bool emit_dispatcher = j < labels.Length;
4010 if (emit_dispatcher) {
4011 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4012 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4013 ec.EmitInt (first_resume_pc);
4014 ec.Emit (OpCodes.Sub);
4015 ec.Emit (OpCodes.Switch, labels);
4016 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4019 foreach (ResumableStatement s in resume_points)
4020 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4023 ec.MarkLabel (end_of_try);
4025 ec.BeginFinallyBlock ();
4027 EmitFinallyBody (ec);
4029 ec.EndExceptionBlock ();
4033 public class Lock : ExceptionStatement {
4035 public Statement Statement;
4036 TemporaryVariable temp;
4038 public Lock (Expression expr, Statement stmt, Location l)
4045 public override bool Resolve (BlockContext ec)
4047 expr = expr.Resolve (ec);
4051 if (!TypeManager.IsReferenceType (expr.Type)){
4052 ec.Report.Error (185, loc,
4053 "`{0}' is not a reference type as required by the lock statement",
4054 TypeManager.CSharpName (expr.Type));
4058 ec.StartFlowBranching (this);
4059 bool ok = Statement.Resolve (ec);
4060 ec.EndFlowBranching ();
4062 ok &= base.Resolve (ec);
4064 // Avoid creating libraries that reference the internal
4066 TypeSpec t = expr.Type;
4067 if (t == TypeManager.null_type)
4068 t = TypeManager.object_type;
4070 temp = new TemporaryVariable (t, loc);
4073 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4074 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4075 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4076 monitor_type, "Enter", loc, TypeManager.object_type);
4077 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4078 monitor_type, "Exit", loc, TypeManager.object_type);
4084 protected override void EmitPreTryBody (EmitContext ec)
4086 temp.EmitAssign (ec, expr);
4088 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4091 protected override void EmitTryBody (EmitContext ec)
4093 Statement.Emit (ec);
4096 protected override void EmitFinallyBody (EmitContext ec)
4099 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4102 protected override void CloneTo (CloneContext clonectx, Statement t)
4104 Lock target = (Lock) t;
4106 target.expr = expr.Clone (clonectx);
4107 target.Statement = Statement.Clone (clonectx);
4111 public class Unchecked : Statement {
4114 public Unchecked (Block b, Location loc)
4121 public override bool Resolve (BlockContext ec)
4123 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4124 return Block.Resolve (ec);
4127 protected override void DoEmit (EmitContext ec)
4129 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4133 protected override void CloneTo (CloneContext clonectx, Statement t)
4135 Unchecked target = (Unchecked) t;
4137 target.Block = clonectx.LookupBlock (Block);
4141 public class Checked : Statement {
4144 public Checked (Block b, Location loc)
4147 b.Unchecked = false;
4151 public override bool Resolve (BlockContext ec)
4153 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4154 return Block.Resolve (ec);
4157 protected override void DoEmit (EmitContext ec)
4159 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4163 protected override void CloneTo (CloneContext clonectx, Statement t)
4165 Checked target = (Checked) t;
4167 target.Block = clonectx.LookupBlock (Block);
4171 public class Unsafe : Statement {
4174 public Unsafe (Block b, Location loc)
4177 Block.Unsafe = true;
4181 public override bool Resolve (BlockContext ec)
4183 if (ec.CurrentIterator != null)
4184 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4186 using (ec.Set (ResolveContext.Options.UnsafeScope))
4187 return Block.Resolve (ec);
4190 protected override void DoEmit (EmitContext ec)
4195 protected override void CloneTo (CloneContext clonectx, Statement t)
4197 Unsafe target = (Unsafe) t;
4199 target.Block = clonectx.LookupBlock (Block);
4206 public class Fixed : Statement {
4208 List<KeyValuePair<LocalInfo, Expression>> declarators;
4209 Statement statement;
4214 abstract class Emitter
4216 protected LocalInfo vi;
4217 protected Expression converted;
4219 protected Emitter (Expression expr, LocalInfo li)
4225 public abstract void Emit (EmitContext ec);
4226 public abstract void EmitExit (EmitContext ec);
4229 class ExpressionEmitter : Emitter {
4230 public ExpressionEmitter (Expression converted, LocalInfo li) :
4231 base (converted, li)
4235 public override void Emit (EmitContext ec) {
4237 // Store pointer in pinned location
4239 converted.Emit (ec);
4243 public override void EmitExit (EmitContext ec)
4245 ec.Emit (OpCodes.Ldc_I4_0);
4246 ec.Emit (OpCodes.Conv_U);
4251 class StringEmitter : Emitter
4253 LocalInfo pinned_string;
4255 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4258 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4259 pinned_string.Pinned = true;
4262 public StringEmitter Resolve (ResolveContext rc)
4264 pinned_string.Resolve (rc);
4266 if (TypeManager.int_get_offset_to_string_data == null) {
4267 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4268 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4274 public override void Emit (EmitContext ec)
4276 pinned_string.ResolveVariable (ec);
4278 converted.Emit (ec);
4279 pinned_string.EmitAssign (ec);
4281 // TODO: Should use Binary::Add
4282 pinned_string.Emit (ec);
4283 ec.Emit (OpCodes.Conv_I);
4285 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4286 //pe.InstanceExpression = pinned_string;
4287 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4289 ec.Emit (OpCodes.Add);
4293 public override void EmitExit (EmitContext ec)
4295 ec.Emit (OpCodes.Ldnull);
4296 pinned_string.EmitAssign (ec);
4300 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4303 declarators = decls;
4308 public Statement Statement {
4309 get { return statement; }
4312 public override bool Resolve (BlockContext ec)
4315 Expression.UnsafeError (ec, loc);
4319 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4320 if (texpr == null) {
4321 if (type is VarExpr)
4322 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4327 expr_type = texpr.Type;
4329 data = new Emitter [declarators.Count];
4331 if (!expr_type.IsPointer){
4332 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4337 foreach (var p in declarators){
4338 LocalInfo vi = p.Key;
4339 Expression e = p.Value;
4341 vi.VariableInfo.SetAssigned (ec);
4342 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4345 // The rules for the possible declarators are pretty wise,
4346 // but the production on the grammar is more concise.
4348 // So we have to enforce these rules here.
4350 // We do not resolve before doing the case 1 test,
4351 // because the grammar is explicit in that the token &
4352 // is present, so we need to test for this particular case.
4356 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4360 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4370 if (e.Type.IsArray){
4371 TypeSpec array_type = TypeManager.GetElementType (e.Type);
4374 // Provided that array_type is unmanaged,
4376 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4380 // and T* is implicitly convertible to the
4381 // pointer type given in the fixed statement.
4383 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4385 Expression converted = Convert.ImplicitConversionRequired (
4386 ec, array_ptr, vi.VariableType, loc);
4387 if (converted == null)
4391 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4393 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4394 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4395 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4396 new NullPointer (loc),
4399 converted = converted.Resolve (ec);
4401 data [i] = new ExpressionEmitter (converted, vi);
4410 if (e.Type == TypeManager.string_type){
4411 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4416 // Case 4: fixed buffer
4417 if (e is FixedBufferPtr) {
4418 data [i++] = new ExpressionEmitter (e, vi);
4423 // Case 1: & object.
4425 Unary u = e as Unary;
4426 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4427 IVariableReference vr = u.Expr as IVariableReference;
4428 if (vr == null || !vr.IsFixed) {
4429 data [i] = new ExpressionEmitter (e, vi);
4433 if (data [i++] == null)
4434 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4436 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4439 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4440 bool ok = statement.Resolve (ec);
4441 bool flow_unreachable = ec.EndFlowBranching ();
4442 has_ret = flow_unreachable;
4447 protected override void DoEmit (EmitContext ec)
4449 for (int i = 0; i < data.Length; i++) {
4453 statement.Emit (ec);
4459 // Clear the pinned variable
4461 for (int i = 0; i < data.Length; i++) {
4462 data [i].EmitExit (ec);
4466 protected override void CloneTo (CloneContext clonectx, Statement t)
4468 Fixed target = (Fixed) t;
4470 target.type = type.Clone (clonectx);
4471 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4472 foreach (var p in declarators) {
4473 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4474 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4477 target.statement = statement.Clone (clonectx);
4481 public class Catch : Statement {
4482 public readonly string Name;
4484 public Block VarBlock;
4486 Expression type_expr;
4489 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4494 VarBlock = var_block;
4498 public TypeSpec CatchType {
4504 public bool IsGeneral {
4506 return type_expr == null;
4510 protected override void DoEmit (EmitContext ec)
4512 if (CatchType != null)
4513 ec.BeginCatchBlock (CatchType);
4515 ec.BeginCatchBlock (TypeManager.object_type);
4517 if (VarBlock != null)
4521 // TODO: Move to resolve
4522 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4523 lvr.Resolve (new ResolveContext (ec.MemberContext));
4525 // Only to make verifier happy
4526 if (TypeManager.IsGenericParameter (lvr.Type))
4527 ec.Emit (OpCodes.Unbox_Any, lvr.Type);
4530 if (lvr.IsHoisted) {
4531 LocalTemporary lt = new LocalTemporary (lvr.Type);
4535 // Variable is at the top of the stack
4536 source = EmptyExpression.Null;
4539 lvr.EmitAssign (ec, source, false, false);
4541 ec.Emit (OpCodes.Pop);
4546 public override bool Resolve (BlockContext ec)
4548 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4549 if (type_expr != null) {
4550 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4556 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4557 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4563 if (!Block.Resolve (ec))
4566 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4567 // emit the "unused variable" warnings.
4568 if (VarBlock != null)
4569 return VarBlock.Resolve (ec);
4575 protected override void CloneTo (CloneContext clonectx, Statement t)
4577 Catch target = (Catch) t;
4579 if (type_expr != null)
4580 target.type_expr = type_expr.Clone (clonectx);
4581 if (VarBlock != null)
4582 target.VarBlock = clonectx.LookupBlock (VarBlock);
4583 target.Block = clonectx.LookupBlock (Block);
4587 public class TryFinally : ExceptionStatement {
4591 public TryFinally (Statement stmt, Block fini, Location l)
4598 public override bool Resolve (BlockContext ec)
4602 ec.StartFlowBranching (this);
4604 if (!stmt.Resolve (ec))
4608 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4609 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4610 if (!fini.Resolve (ec))
4614 ec.EndFlowBranching ();
4616 ok &= base.Resolve (ec);
4621 protected override void EmitPreTryBody (EmitContext ec)
4625 protected override void EmitTryBody (EmitContext ec)
4630 protected override void EmitFinallyBody (EmitContext ec)
4635 protected override void CloneTo (CloneContext clonectx, Statement t)
4637 TryFinally target = (TryFinally) t;
4639 target.stmt = (Statement) stmt.Clone (clonectx);
4641 target.fini = clonectx.LookupBlock (fini);
4645 public class TryCatch : Statement {
4647 public List<Catch> Specific;
4648 public Catch General;
4649 bool inside_try_finally, code_follows;
4651 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4654 this.Specific = catch_clauses;
4655 this.inside_try_finally = inside_try_finally;
4657 Catch c = catch_clauses [0];
4660 catch_clauses.RemoveAt (0);
4666 public override bool Resolve (BlockContext ec)
4670 ec.StartFlowBranching (this);
4672 if (!Block.Resolve (ec))
4675 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4677 foreach (Catch c in Specific){
4678 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4680 if (c.Name != null) {
4681 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4683 throw new Exception ();
4685 vi.VariableInfo = null;
4688 if (!c.Resolve (ec)) {
4693 TypeSpec resolved_type = c.CatchType;
4694 for (int ii = 0; ii < last_index; ++ii) {
4695 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4696 ec.Report.Error (160, c.loc,
4697 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4698 TypeManager.CSharpName (prev_catches [ii]));
4703 prev_catches [last_index++] = resolved_type;
4706 if (General != null) {
4707 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4708 foreach (Catch c in Specific){
4709 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4710 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'");
4715 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4717 if (!General.Resolve (ec))
4721 ec.EndFlowBranching ();
4723 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4724 // So, ensure there's some IL code after this statement
4725 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4726 ec.NeedReturnLabel ();
4731 public void SomeCodeFollows ()
4733 code_follows = true;
4736 protected override void DoEmit (EmitContext ec)
4738 if (!inside_try_finally)
4739 ec.BeginExceptionBlock ();
4743 foreach (Catch c in Specific)
4746 if (General != null)
4749 if (!inside_try_finally)
4750 ec.EndExceptionBlock ();
4753 protected override void CloneTo (CloneContext clonectx, Statement t)
4755 TryCatch target = (TryCatch) t;
4757 target.Block = clonectx.LookupBlock (Block);
4758 if (General != null)
4759 target.General = (Catch) General.Clone (clonectx);
4760 if (Specific != null){
4761 target.Specific = new List<Catch> ();
4762 foreach (Catch c in Specific)
4763 target.Specific.Add ((Catch) c.Clone (clonectx));
4768 // FIXME: Why is it almost exact copy of Using ??
4769 public class UsingTemporary : ExceptionStatement
4771 protected TemporaryVariable local_copy;
4772 Statement statement;
4774 protected Statement dispose_call;
4776 public UsingTemporary (Expression expr, Statement stmt, Location l)
4784 public Expression Expression {
4790 public Statement Statement {
4798 protected virtual bool DoResolve (BlockContext ec)
4800 expr = expr.Resolve (ec);
4804 if (!expr.Type.ImplementsInterface (TypeManager.idisposable_type) &&
4805 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4806 if (expr.Type != InternalType.Dynamic) {
4807 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4811 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4814 var expr_type = expr.Type;
4816 local_copy = new TemporaryVariable (expr_type, loc);
4817 local_copy.Resolve (ec);
4819 if (TypeManager.void_dispose_void == null) {
4820 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4821 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4824 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
4825 dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
4826 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
4829 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
4831 // Add conditional call when disposing possible null variable
4832 if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
4833 dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
4835 return dispose_call.Resolve (ec);
4838 public override bool Resolve (BlockContext ec)
4840 bool ok = DoResolve (ec);
4842 ec.StartFlowBranching (this);
4844 ok &= statement.Resolve (ec);
4846 ec.EndFlowBranching ();
4848 ok &= base.Resolve (ec);
4853 protected override void EmitPreTryBody (EmitContext ec)
4855 local_copy.EmitAssign (ec, expr);
4858 protected override void EmitTryBody (EmitContext ec)
4860 statement.Emit (ec);
4863 protected override void EmitFinallyBody (EmitContext ec)
4865 dispose_call.Emit (ec);
4868 protected override void CloneTo (CloneContext clonectx, Statement t)
4870 UsingTemporary target = (UsingTemporary) t;
4872 target.expr = expr.Clone (clonectx);
4873 target.statement = statement.Clone (clonectx);
4877 public class Using : ExceptionStatement {
4879 public Statement EmbeddedStatement {
4880 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4886 ExpressionStatement assign;
4888 public Using (Expression var, Expression init, Statement stmt, Location l)
4896 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4898 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4899 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4900 TypeManager.CSharpName (expr.Type));
4903 protected override void EmitPreTryBody (EmitContext ec)
4905 assign.EmitStatement (ec);
4908 protected override void EmitTryBody (EmitContext ec)
4913 protected override void EmitFinallyBody (EmitContext ec)
4915 Label skip = ec.DefineLabel ();
4917 bool emit_null_check = !TypeManager.IsValueType (var.Type);
4918 if (emit_null_check) {
4920 ec.Emit (OpCodes.Brfalse, skip);
4923 Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
4925 if (emit_null_check)
4926 ec.MarkLabel (skip);
4929 public override bool Resolve (BlockContext ec)
4931 if (!ResolveVariable (ec))
4934 ec.StartFlowBranching (this);
4936 bool ok = stmt.Resolve (ec);
4938 ec.EndFlowBranching ();
4940 ok &= base.Resolve (ec);
4942 if (TypeManager.void_dispose_void == null) {
4943 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4944 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4950 bool ResolveVariable (BlockContext ec)
4952 assign = new SimpleAssign (var, init, loc);
4953 assign = assign.ResolveStatement (ec);
4957 if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4961 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
4963 if (assign.Type == InternalType.Dynamic) {
4964 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
4965 var = new TemporaryVariable (e.Type, loc);
4966 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
4970 Error_IsNotConvertibleToIDisposable (ec, var);
4974 throw new NotImplementedException ("covariance?");
4977 protected override void CloneTo (CloneContext clonectx, Statement t)
4979 Using target = (Using) t;
4981 target.var = var.Clone (clonectx);
4982 target.init = init.Clone (clonectx);
4983 target.stmt = stmt.Clone (clonectx);
4988 /// Implementation of the foreach C# statement
4990 public class Foreach : Statement {
4992 sealed class ArrayForeach : Statement
4994 class ArrayCounter : TemporaryVariable
4996 StatementExpression increment;
4998 public ArrayCounter (Location loc)
4999 : base (TypeManager.int32_type, loc)
5003 public void ResolveIncrement (BlockContext ec)
5005 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5006 increment.Resolve (ec);
5009 public void EmitIncrement (EmitContext ec)
5011 increment.Emit (ec);
5015 readonly Foreach for_each;
5016 readonly Statement statement;
5019 TemporaryVariable[] lengths;
5020 Expression [] length_exprs;
5021 ArrayCounter[] counter;
5023 TemporaryVariable copy;
5026 public ArrayForeach (Foreach @foreach, int rank)
5028 for_each = @foreach;
5029 statement = for_each.statement;
5032 counter = new ArrayCounter [rank];
5033 length_exprs = new Expression [rank];
5036 // Only use temporary length variables when dealing with
5037 // multi-dimensional arrays
5040 lengths = new TemporaryVariable [rank];
5043 protected override void CloneTo (CloneContext clonectx, Statement target)
5045 throw new NotImplementedException ();
5048 public override bool Resolve (BlockContext ec)
5050 copy = new TemporaryVariable (for_each.expr.Type, loc);
5053 int rank = length_exprs.Length;
5054 Arguments list = new Arguments (rank);
5055 for (int i = 0; i < rank; i++) {
5056 counter [i] = new ArrayCounter (loc);
5057 counter [i].ResolveIncrement (ec);
5060 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5062 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5063 lengths [i].Resolve (ec);
5065 Arguments args = new Arguments (1);
5066 args.Add (new Argument (new IntConstant (i, loc)));
5067 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5070 list.Add (new Argument (counter [i]));
5073 access = new ElementAccess (copy, list, loc).Resolve (ec);
5077 Expression var_type = for_each.type;
5078 VarExpr ve = var_type as VarExpr;
5080 // Infer implicitly typed local variable from foreach array type
5081 var_type = new TypeExpression (access.Type, ve.Location);
5084 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5085 if (var_type == null)
5088 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5094 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5095 ec.CurrentBranching.CreateSibling ();
5097 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5098 if (for_each.variable == null)
5101 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5102 if (!statement.Resolve (ec))
5104 ec.EndFlowBranching ();
5106 // There's no direct control flow from the end of the embedded statement to the end of the loop
5107 ec.CurrentBranching.CurrentUsageVector.Goto ();
5109 ec.EndFlowBranching ();
5114 protected override void DoEmit (EmitContext ec)
5116 copy.EmitAssign (ec, for_each.expr);
5118 int rank = length_exprs.Length;
5119 Label[] test = new Label [rank];
5120 Label[] loop = new Label [rank];
5122 for (int i = 0; i < rank; i++) {
5123 test [i] = ec.DefineLabel ();
5124 loop [i] = ec.DefineLabel ();
5126 if (lengths != null)
5127 lengths [i].EmitAssign (ec, length_exprs [i]);
5130 IntConstant zero = new IntConstant (0, loc);
5131 for (int i = 0; i < rank; i++) {
5132 counter [i].EmitAssign (ec, zero);
5134 ec.Emit (OpCodes.Br, test [i]);
5135 ec.MarkLabel (loop [i]);
5138 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5140 statement.Emit (ec);
5142 ec.MarkLabel (ec.LoopBegin);
5144 for (int i = rank - 1; i >= 0; i--){
5145 counter [i].EmitIncrement (ec);
5147 ec.MarkLabel (test [i]);
5148 counter [i].Emit (ec);
5150 if (lengths != null)
5151 lengths [i].Emit (ec);
5153 length_exprs [i].Emit (ec);
5155 ec.Emit (OpCodes.Blt, loop [i]);
5158 ec.MarkLabel (ec.LoopEnd);
5162 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5164 class Body : Statement
5167 Expression variable, current, conv;
5168 Statement statement;
5171 public Body (TypeSpec type, Expression variable,
5172 Expression current, Statement statement,
5176 this.variable = variable;
5177 this.current = current;
5178 this.statement = statement;
5182 protected override void CloneTo (CloneContext clonectx, Statement target)
5184 throw new NotImplementedException ();
5187 public override bool Resolve (BlockContext ec)
5189 current = current.Resolve (ec);
5190 if (current == null)
5193 conv = Convert.ExplicitConversion (ec, current, type, loc);
5197 assign = new SimpleAssign (variable, conv, loc);
5198 if (assign.Resolve (ec) == null)
5201 if (!statement.Resolve (ec))
5207 protected override void DoEmit (EmitContext ec)
5209 assign.EmitStatement (ec);
5210 statement.Emit (ec);
5214 class Dispose : UsingTemporary
5216 LocalTemporary dispose;
5218 public Dispose (TemporaryVariable variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
5219 : base (expr, statement, loc)
5221 base.local_copy = variable;
5222 this.dispose = dispose;
5225 protected override bool DoResolve (BlockContext ec)
5227 if (TypeManager.void_dispose_void == null) {
5228 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5229 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5232 Expression dispose_var = (Expression) dispose ?? local_copy;
5234 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5235 dispose_mg.InstanceExpression = dispose_var;
5237 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
5239 if (!dispose_var.Type.IsStruct)
5240 dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
5242 return dispose_call.Resolve (ec);
5245 protected override void EmitFinallyBody (EmitContext ec)
5247 Label call_dispose = ec.DefineLabel ();
5248 if (dispose != null) {
5249 local_copy.Emit (ec, false);
5250 ec.Emit (OpCodes.Isinst, dispose.Type);
5254 base.EmitFinallyBody (ec);
5256 if (dispose != null) {
5257 ec.MarkLabel (call_dispose);
5258 dispose.Release (ec);
5263 Expression variable, expr;
5264 Statement statement;
5265 Expression var_type;
5266 ExpressionStatement init;
5268 public CollectionForeach (Expression var_type, Expression var,
5269 Expression expr, Statement stmt, Location l)
5271 this.var_type = var_type;
5272 this.variable = var;
5278 protected override void CloneTo (CloneContext clonectx, Statement target)
5280 throw new NotImplementedException ();
5283 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5285 rc.Report.SymbolRelatedToPreviousError (enumerator);
5286 rc.Report.Error (202, loc,
5287 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5288 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5291 void Error_AmbiguousIEnumerable (ResolveContext rc, TypeSpec type)
5293 rc.Report.SymbolRelatedToPreviousError (type);
5294 rc.Report.Error (1640, loc,
5295 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5296 type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5299 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5302 // Option 1: Try to match by name GetEnumerator first
5304 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type, "GetEnumerator", 0, true, loc); // TODO: What if CS0229 ?
5306 var mg = mexpr as MethodGroupExpr;
5308 mg.InstanceExpression = expr;
5309 Arguments args = new Arguments (0);
5310 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5312 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5318 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5320 TypeSpec iface_candidate = null;
5321 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5322 var ifaces = t.Interfaces;
5323 if (ifaces != null) {
5324 foreach (var iface in ifaces) {
5325 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5326 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5327 Error_AmbiguousIEnumerable (rc, expr.Type);
5331 iface_candidate = iface;
5335 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5336 iface_candidate = iface;
5342 if (iface_candidate == null) {
5343 rc.Report.Error (1579, loc,
5344 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5345 expr.Type.GetSignatureForError (), "GetEnumerator");
5350 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5351 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5356 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5357 mg.InstanceExpression = expr;
5361 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5363 var ms = MemberCache.FindMember (enumerator.ReturnType,
5364 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5365 BindingRestriction.InstanceOnly) as MethodSpec;
5367 if (ms == null || !ms.IsPublic) {
5368 Error_WrongEnumerator (rc, enumerator);
5372 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5375 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5377 var ps = MemberCache.FindMember (enumerator.ReturnType,
5378 MemberFilter.Property ("Current", null),
5379 BindingRestriction.InstanceOnly) as PropertySpec;
5381 if (ps == null || !ps.IsPublic) {
5382 Error_WrongEnumerator (rc, enumerator);
5389 public override bool Resolve (BlockContext ec)
5391 bool is_dynamic = expr.Type == InternalType.Dynamic;
5393 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5395 var get_enumerator_mg = ResolveGetEnumerator (ec);
5396 if (get_enumerator_mg == null) {
5400 var get_enumerator = get_enumerator_mg.BestCandidate;
5401 var enumerator = new TemporaryVariable (get_enumerator.ReturnType, loc);
5402 enumerator.Resolve (ec);
5404 // Prepare bool MoveNext ()
5405 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5406 if (move_next_mg == null) {
5410 move_next_mg.InstanceExpression = enumerator;
5412 // Prepare ~T~ Current { get; }
5413 var current_prop = ResolveCurrent (ec, get_enumerator);
5414 if (current_prop == null) {
5418 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator }.Resolve (ec);
5419 if (current_pe == null)
5422 VarExpr ve = var_type as VarExpr;
5424 // Infer implicitly typed local variable from foreach enumerable type
5425 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5428 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5429 if (var_type == null)
5432 var init = new Invocation (get_enumerator_mg, null);
5435 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5436 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5438 var enum_type = enumerator.Type;
5441 // Add Dispose method call when enumerator can be IDisposable
5443 if (!enumerator.Type.ImplementsInterface (TypeManager.idisposable_type)) {
5444 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5446 // Runtime Dispose check
5448 var tv = new LocalTemporary (TypeManager.idisposable_type);
5449 statement = new Dispose (enumerator, tv, init, statement, loc);
5452 // No Dispose call needed
5454 this.init = new SimpleAssign (enumerator, init);
5455 this.init.Resolve (ec);
5459 // Static Dispose check
5461 statement = new Dispose (enumerator, null, init, statement, loc);
5464 return statement.Resolve (ec);
5467 protected override void DoEmit (EmitContext ec)
5470 init.EmitStatement (ec);
5472 statement.Emit (ec);
5475 #region IErrorHandler Members
5477 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5479 ec.Report.SymbolRelatedToPreviousError (best);
5480 ec.Report.Warning (278, 2, loc,
5481 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5482 expr.Type.GetSignatureForError (), "enumerable",
5483 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5485 Error_AmbiguousIEnumerable (ec, expr.Type);
5489 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5494 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5499 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5508 Expression variable;
5510 Statement statement;
5512 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5513 Statement stmt, Location l)
5516 this.variable = var;
5522 public Statement Statement {
5523 get { return statement; }
5526 public override bool Resolve (BlockContext ec)
5528 expr = expr.Resolve (ec);
5533 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5537 if (expr.Type == TypeManager.string_type) {
5538 statement = new ArrayForeach (this, 1);
5539 } else if (expr.Type is ArrayContainer) {
5540 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5542 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5543 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5544 expr.ExprClassName);
5548 statement = new CollectionForeach (type, variable, expr, statement, loc);
5551 return statement.Resolve (ec);
5554 protected override void DoEmit (EmitContext ec)
5556 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5557 ec.LoopBegin = ec.DefineLabel ();
5558 ec.LoopEnd = ec.DefineLabel ();
5560 statement.Emit (ec);
5562 ec.LoopBegin = old_begin;
5563 ec.LoopEnd = old_end;
5566 protected override void CloneTo (CloneContext clonectx, Statement t)
5568 Foreach target = (Foreach) t;
5570 target.type = type.Clone (clonectx);
5571 target.variable = variable.Clone (clonectx);
5572 target.expr = expr.Clone (clonectx);
5573 target.statement = statement.Clone (clonectx);