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.
1447 protected ExplicitBlock 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 ExplicitBlock CreateSwitchBlock (Location start)
1526 // FIXME: Only explicit block should be created
1527 var new_block = new ExplicitBlock (this, start, start);
1528 new_block.switch_block = Explicit;
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)) {
1949 using (ec.With (ResolveContext.Options.DoFlowAnalysis, false)) {
1950 e = cv.Resolve (ec);
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;
2339 // Switch block does not follow sequential flow and we cannot emit
2340 // storey initialization inside the block because it can be jumped over
2341 // for all non-first cases. Instead we push it to the parent block to be
2342 // always initialized
2344 if (switch_block != null)
2345 return switch_block.CreateAnonymousMethodStorey (ec);
2347 if (am_storey == null) {
2348 MemberBase mc = ec.MemberContext as MemberBase;
2349 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2352 // Creates anonymous method storey for this block
2354 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2360 public override void Emit (EmitContext ec)
2362 if (am_storey != null)
2363 am_storey.EmitStoreyInstantiation (ec);
2365 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2366 if (emit_debug_info)
2371 if (emit_debug_info)
2375 public override void EmitMeta (EmitContext ec)
2378 // Creates anonymous method storey
2380 if (am_storey != null) {
2381 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2383 // Creates parent storey reference when hoisted this is accessible
2385 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2386 ExplicitBlock parent = Toplevel.Parent.Explicit;
2389 // Hoisted this exists in top-level parent storey only
2391 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2392 parent = parent.Parent.Explicit;
2394 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2397 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2399 // TODO MemberCache: Review
2400 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2403 am_storey.CreateType ();
2404 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2405 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2407 am_storey.DefineType ();
2408 am_storey.ResolveTypeParameters ();
2409 am_storey.Define ();
2410 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2412 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2413 if (ref_blocks != null) {
2414 foreach (ExplicitBlock ref_block in ref_blocks) {
2415 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2416 if (b.am_storey != null) {
2417 b.am_storey.AddParentStoreyReference (ec, am_storey);
2419 // Stop propagation inside same top block
2420 if (b.Toplevel == Toplevel)
2425 b.HasCapturedVariable = true;
2434 public IKnownVariable GetKnownVariable (string name)
2436 if (known_variables == null)
2440 known_variables.TryGetValue (name, out kw);
2444 public bool HasCapturedThis
2446 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2447 get { return (flags & Flags.HasCapturedThis) != 0; }
2450 public bool HasCapturedVariable
2452 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2453 get { return (flags & Flags.HasCapturedVariable) != 0; }
2456 protected override void CloneTo (CloneContext clonectx, Statement t)
2458 ExplicitBlock target = (ExplicitBlock) t;
2459 target.known_variables = null;
2460 base.CloneTo (clonectx, t);
2464 public class ToplevelParameterInfo : IKnownVariable {
2465 public readonly ToplevelBlock Block;
2466 public readonly int Index;
2467 public VariableInfo VariableInfo;
2469 Block IKnownVariable.Block {
2470 get { return Block; }
2472 public Parameter Parameter {
2473 get { return Block.Parameters [Index]; }
2476 public TypeSpec ParameterType {
2477 get { return Block.Parameters.Types [Index]; }
2480 public Location Location {
2481 get { return Parameter.Location; }
2484 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2492 // A toplevel block contains extra information, the split is done
2493 // only to separate information that would otherwise bloat the more
2494 // lightweight Block.
2496 // In particular, this was introduced when the support for Anonymous
2497 // Methods was implemented.
2499 public class ToplevelBlock : ExplicitBlock
2502 // Block is converted to an expression
2504 sealed class BlockScopeExpression : Expression
2507 readonly ToplevelBlock block;
2509 public BlockScopeExpression (Expression child, ToplevelBlock block)
2515 public override Expression CreateExpressionTree (ResolveContext ec)
2517 throw new NotSupportedException ();
2520 protected override Expression DoResolve (ResolveContext ec)
2525 child = child.Resolve (ec);
2529 eclass = child.eclass;
2534 public override void Emit (EmitContext ec)
2536 block.EmitMeta (ec);
2537 block.EmitScopeInitializers (ec);
2542 protected ParametersCompiled parameters;
2543 protected ToplevelParameterInfo[] parameter_info;
2544 LocalInfo this_variable;
2547 CompilerContext compiler;
2549 public HoistedVariable HoistedThisVariable;
2551 public bool Resolved {
2558 // The parameters for the block.
2560 public ParametersCompiled Parameters {
2561 get { return parameters; }
2564 public Report Report {
2565 get { return compiler.Report; }
2568 public ToplevelBlock Container {
2569 get { return Parent == null ? null : Parent.Toplevel; }
2572 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2573 this (ctx, parent, (Flags) 0, parameters, start)
2577 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2578 this (ctx, null, (Flags) 0, parameters, start)
2582 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2583 this (ctx, null, flags, parameters, start)
2587 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2588 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2589 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2590 base (null, flags, start, Location.Null)
2592 this.compiler = ctx;
2593 this.Toplevel = this;
2595 this.parameters = parameters;
2596 this.Parent = parent;
2598 parent.AddAnonymousChild (this);
2600 if (!this.parameters.IsEmpty)
2601 ProcessParameters ();
2604 public ToplevelBlock (CompilerContext ctx, Location loc)
2605 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2609 protected override void CloneTo (CloneContext clonectx, Statement t)
2611 ToplevelBlock target = (ToplevelBlock) t;
2612 base.CloneTo (clonectx, t);
2614 if (parameters.Count != 0) {
2615 target.parameter_info = new ToplevelParameterInfo[parameters.Count];
2616 for (int i = 0; i < parameters.Count; ++i)
2617 target.parameter_info[i] = new ToplevelParameterInfo (target, i);
2621 public bool CheckError158 (string name, Location loc)
2623 if (AnonymousChildren != null) {
2624 foreach (ToplevelBlock child in AnonymousChildren) {
2625 if (!child.CheckError158 (name, loc))
2630 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2631 if (!c.DoCheckError158 (name, loc))
2638 void ProcessParameters ()
2640 int n = parameters.Count;
2641 parameter_info = new ToplevelParameterInfo [n];
2642 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2643 for (int i = 0; i < n; ++i) {
2644 parameter_info [i] = new ToplevelParameterInfo (this, i);
2646 Parameter p = parameters [i];
2650 string name = p.Name;
2651 if (CheckParentConflictName (top_parent, name, loc))
2652 AddKnownVariable (name, parameter_info [i]);
2655 // mark this block as "used" so that we create local declarations in a sub-block
2656 // FIXME: This appears to uncover a lot of bugs
2660 bool DoCheckError158 (string name, Location loc)
2662 LabeledStatement s = LookupLabel (name);
2664 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2665 Error_158 (name, loc);
2672 public override Expression CreateExpressionTree (ResolveContext ec)
2674 if (statements.Count == 1) {
2675 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2676 if (scope_initializers != null)
2677 expr = new BlockScopeExpression (expr, this);
2682 return base.CreateExpressionTree (ec);
2686 // Reformats this block to be top-level iterator block
2688 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2692 // Creates block with original statements
2693 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2695 source.statements = new List<Statement> (1);
2696 source.AddStatement (new Return (iterator, iterator.Location));
2697 source.IsIterator = false;
2699 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2700 source.am_storey = iterator_storey;
2701 return iterator_storey;
2705 // Creates an arguments set from all parameters, useful for method proxy calls
2707 public Arguments GetAllParametersArguments ()
2709 int count = parameters.Count;
2710 Arguments args = new Arguments (count);
2711 for (int i = 0; i < count; ++i) {
2712 var arg_expr = new ParameterReference (parameter_info[i], parameters[i].Location);
2713 args.Add (new Argument (arg_expr));
2720 // Returns a parameter reference expression for the given name,
2721 // or null if there is no such parameter
2723 public Expression GetParameterReference (string name, Location loc)
2725 for (ToplevelBlock t = this; t != null; t = t.Container) {
2726 if (t.parameters.IsEmpty)
2729 Expression expr = t.GetParameterReferenceExpression (name, loc);
2737 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2739 int idx = parameters.GetParameterIndexByName (name);
2741 null : new ParameterReference (parameter_info [idx], loc);
2744 public ToplevelBlock CheckParameterNameConflict (string name)
2746 for (ToplevelBlock t = this; t != null; t = t.Container) {
2747 if (t.HasParameterWithName (name))
2754 protected virtual bool HasParameterWithName (string name)
2756 return parameters.GetParameterIndexByName (name) >= 0;
2760 // Returns the "this" instance variable of this block.
2761 // See AddThisVariable() for more information.
2763 public LocalInfo ThisVariable {
2764 get { return this_variable; }
2768 // This is used by non-static `struct' constructors which do not have an
2769 // initializer - in this case, the constructor must initialize all of the
2770 // struct's fields. To do this, we add a "this" variable and use the flow
2771 // analysis code to ensure that it's been fully initialized before control
2772 // leaves the constructor.
2774 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2776 if (this_variable == null) {
2777 this_variable = new LocalInfo (ds, this, l);
2778 this_variable.Used = true;
2779 this_variable.IsThis = true;
2781 Variables.Add ("this", this_variable);
2784 return this_variable;
2787 public bool IsIterator {
2788 get { return (flags & Flags.IsIterator) != 0; }
2789 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2793 // Block has been converted to expression tree
2795 public bool IsExpressionTree {
2796 get { return (flags & Flags.IsExpressionTree) != 0; }
2799 public bool IsThisAssigned (BlockContext ec)
2801 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2804 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2811 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2812 flags |= Flags.IsExpressionTree;
2815 if (!ResolveMeta (rc, ip))
2818 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2819 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2824 unreachable = top_level.End ();
2826 } catch (Exception e) {
2827 if (e is CompletionResult || rc.Report.IsDisabled)
2830 if (rc.CurrentBlock != null) {
2831 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2833 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2836 if (Report.DebugFlags > 0)
2840 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2841 if (rc.CurrentAnonymousMethod == null) {
2842 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2844 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2845 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2846 rc.CurrentAnonymousMethod.GetSignatureForError ());
2854 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2856 int errors = ec.Report.Errors;
2857 int orig_count = parameters.Count;
2862 // Assert: orig_count != parameter.Count => orig_count == 0
2863 if (orig_count != 0 && orig_count != parameters.Count)
2864 throw new InternalErrorException ("parameter information mismatch");
2866 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2868 for (int i = 0; i < orig_count; ++i) {
2869 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2871 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2874 VariableInfo vi = new VariableInfo (ip, i, offset);
2875 parameter_info [i].VariableInfo = vi;
2876 offset += vi.Length;
2879 ResolveMeta (ec, offset);
2881 return ec.Report.Errors == errors;
2885 // Check whether all `out' parameters have been assigned.
2887 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2889 if (vector.IsUnreachable)
2892 int n = parameter_info == null ? 0 : parameter_info.Length;
2894 for (int i = 0; i < n; i++) {
2895 VariableInfo var = parameter_info [i].VariableInfo;
2900 if (vector.IsAssigned (var, false))
2903 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2908 public override void Emit (EmitContext ec)
2910 if (Report.Errors > 0)
2918 if (ec.HasReturnLabel)
2919 ec.ReturnLabel = ec.DefineLabel ();
2923 ec.Mark (EndLocation);
2925 if (ec.HasReturnLabel)
2926 ec.MarkLabel (ec.ReturnLabel);
2928 if (ec.return_value != null) {
2929 ec.Emit (OpCodes.Ldloc, ec.return_value);
2930 ec.Emit (OpCodes.Ret);
2933 // If `HasReturnLabel' is set, then we already emitted a
2934 // jump to the end of the method, so we must emit a `ret'
2937 // Unfortunately, System.Reflection.Emit automatically emits
2938 // a leave to the end of a finally block. This is a problem
2939 // if no code is following the try/finally block since we may
2940 // jump to a point after the end of the method.
2941 // As a workaround, we're always creating a return label in
2945 if (ec.HasReturnLabel || !unreachable) {
2946 if (ec.ReturnType != TypeManager.void_type)
2947 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2948 ec.Emit (OpCodes.Ret);
2953 } catch (Exception e){
2954 Console.WriteLine ("Exception caught by the compiler while emitting:");
2955 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2957 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2963 public override void EmitMeta (EmitContext ec)
2965 // Avoid declaring an IL variable for this_variable since it is not accessed
2966 // from the generated IL
2967 if (this_variable != null)
2968 Variables.Remove ("this");
2972 protected override void EmitSymbolInfo (EmitContext ec)
2974 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2975 if ((ae != null) && (ae.Storey != null))
2976 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2978 base.EmitSymbolInfo (ec);
2982 public class SwitchLabel {
2989 Label il_label_code;
2990 bool il_label_code_set;
2992 public static readonly object NullStringCase = new object ();
2995 // if expr == null, then it is the default case.
2997 public SwitchLabel (Expression expr, Location l)
3003 public Expression Label {
3009 public Location Location {
3013 public object Converted {
3019 public Label GetILLabel (EmitContext ec)
3022 il_label = ec.DefineLabel ();
3023 il_label_set = true;
3028 public Label GetILLabelCode (EmitContext ec)
3030 if (!il_label_code_set){
3031 il_label_code = ec.DefineLabel ();
3032 il_label_code_set = true;
3034 return il_label_code;
3038 // Resolves the expression, reduces it to a literal if possible
3039 // and then converts it to the requested type.
3041 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3043 Expression e = label.Resolve (ec);
3048 Constant c = e as Constant;
3050 ec.Report.Error (150, loc, "A constant value is expected");
3054 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3055 converted = NullStringCase;
3059 if (allow_nullable && c.GetValue () == null) {
3060 converted = NullStringCase;
3064 c = c.ImplicitConversionRequired (ec, required_type, loc);
3068 converted = c.GetValue ();
3072 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3075 if (converted == null)
3077 else if (converted == NullStringCase)
3080 label = converted.ToString ();
3082 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3083 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3086 public SwitchLabel Clone (CloneContext clonectx)
3088 return new SwitchLabel (label.Clone (clonectx), loc);
3092 public class SwitchSection {
3093 // An array of SwitchLabels.
3094 public readonly List<SwitchLabel> Labels;
3095 public readonly Block Block;
3097 public SwitchSection (List<SwitchLabel> labels, Block block)
3103 public SwitchSection Clone (CloneContext clonectx)
3105 var cloned_labels = new List<SwitchLabel> ();
3107 foreach (SwitchLabel sl in cloned_labels)
3108 cloned_labels.Add (sl.Clone (clonectx));
3110 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3114 public class Switch : Statement {
3115 public List<SwitchSection> Sections;
3116 public Expression Expr;
3119 /// Maps constants whose type type SwitchType to their SwitchLabels.
3121 public IDictionary<object, SwitchLabel> Elements;
3124 /// The governing switch type
3126 public TypeSpec SwitchType;
3131 Label default_target;
3133 Expression new_expr;
3136 SwitchSection constant_section;
3137 SwitchSection default_section;
3139 ExpressionStatement string_dictionary;
3140 FieldExpr switch_cache_field;
3141 static int unique_counter;
3144 // Nullable Types support
3146 Nullable.Unwrap unwrap;
3148 protected bool HaveUnwrap {
3149 get { return unwrap != null; }
3153 // The types allowed to be implicitly cast from
3154 // on the governing type
3156 static TypeSpec [] allowed_types;
3158 public Switch (Expression e, List<SwitchSection> sects, Location l)
3165 public bool GotDefault {
3167 return default_section != null;
3171 public Label DefaultTarget {
3173 return default_target;
3178 // Determines the governing type for a switch. The returned
3179 // expression might be the expression from the switch, or an
3180 // expression that includes any potential conversions to the
3181 // integral types or to string.
3183 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3185 TypeSpec t = expr.Type;
3187 if (t == TypeManager.byte_type ||
3188 t == TypeManager.sbyte_type ||
3189 t == TypeManager.ushort_type ||
3190 t == TypeManager.short_type ||
3191 t == TypeManager.uint32_type ||
3192 t == TypeManager.int32_type ||
3193 t == TypeManager.uint64_type ||
3194 t == TypeManager.int64_type ||
3195 t == TypeManager.char_type ||
3196 t == TypeManager.string_type ||
3197 t == TypeManager.bool_type ||
3198 TypeManager.IsEnumType (t))
3201 if (allowed_types == null){
3202 allowed_types = new TypeSpec [] {
3203 TypeManager.sbyte_type,
3204 TypeManager.byte_type,
3205 TypeManager.short_type,
3206 TypeManager.ushort_type,
3207 TypeManager.int32_type,
3208 TypeManager.uint32_type,
3209 TypeManager.int64_type,
3210 TypeManager.uint64_type,
3211 TypeManager.char_type,
3212 TypeManager.string_type
3217 // Try to find a *user* defined implicit conversion.
3219 // If there is no implicit conversion, or if there are multiple
3220 // conversions, we have to report an error
3222 Expression converted = null;
3223 foreach (TypeSpec tt in allowed_types){
3226 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3231 // Ignore over-worked ImplicitUserConversions that do
3232 // an implicit conversion in addition to the user conversion.
3234 if (!(e is UserCast))
3237 if (converted != null){
3238 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3248 // Performs the basic sanity checks on the switch statement
3249 // (looks for duplicate keys and non-constant expressions).
3251 // It also returns a hashtable with the keys that we will later
3252 // use to compute the switch tables
3254 bool CheckSwitch (ResolveContext ec)
3257 Elements = new Dictionary<object, SwitchLabel> ();
3259 foreach (SwitchSection ss in Sections){
3260 foreach (SwitchLabel sl in ss.Labels){
3261 if (sl.Label == null){
3262 if (default_section != null){
3263 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3266 default_section = ss;
3270 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3275 object key = sl.Converted;
3276 if (key == SwitchLabel.NullStringCase)
3277 has_null_case = true;
3280 Elements.Add (key, sl);
3281 } catch (ArgumentException) {
3282 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3290 void EmitObjectInteger (EmitContext ec, object k)
3293 ec.EmitInt ((int) k);
3294 else if (k is Constant) {
3295 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3298 ec.EmitInt (unchecked ((int) (uint) k));
3301 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3303 ec.EmitInt ((int) (long) k);
3304 ec.Emit (OpCodes.Conv_I8);
3307 ec.EmitLong ((long) k);
3309 else if (k is ulong)
3311 ulong ul = (ulong) k;
3314 ec.EmitInt (unchecked ((int) ul));
3315 ec.Emit (OpCodes.Conv_U8);
3319 ec.EmitLong (unchecked ((long) ul));
3323 ec.EmitInt ((int) ((char) k));
3324 else if (k is sbyte)
3325 ec.EmitInt ((int) ((sbyte) k));
3327 ec.EmitInt ((int) ((byte) k));
3328 else if (k is short)
3329 ec.EmitInt ((int) ((short) k));
3330 else if (k is ushort)
3331 ec.EmitInt ((int) ((ushort) k));
3333 ec.EmitInt (((bool) k) ? 1 : 0);
3335 throw new Exception ("Unhandled case");
3338 // structure used to hold blocks of keys while calculating table switch
3339 class KeyBlock : IComparable
3341 public KeyBlock (long _first)
3343 first = last = _first;
3347 public List<object> element_keys;
3348 // how many items are in the bucket
3349 public int Size = 1;
3352 get { return (int) (last - first + 1); }
3354 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3356 return kb_last.last - kb_first.first + 1;
3358 public int CompareTo (object obj)
3360 KeyBlock kb = (KeyBlock) obj;
3361 int nLength = Length;
3362 int nLengthOther = kb.Length;
3363 if (nLengthOther == nLength)
3364 return (int) (kb.first - first);
3365 return nLength - nLengthOther;
3370 /// This method emits code for a lookup-based switch statement (non-string)
3371 /// Basically it groups the cases into blocks that are at least half full,
3372 /// and then spits out individual lookup opcodes for each block.
3373 /// It emits the longest blocks first, and short blocks are just
3374 /// handled with direct compares.
3376 /// <param name="ec"></param>
3377 /// <param name="val"></param>
3378 /// <returns></returns>
3379 void TableSwitchEmit (EmitContext ec, Expression val)
3381 int element_count = Elements.Count;
3382 object [] element_keys = new object [element_count];
3383 Elements.Keys.CopyTo (element_keys, 0);
3384 Array.Sort (element_keys);
3386 // initialize the block list with one element per key
3387 var key_blocks = new List<KeyBlock> (element_count);
3388 foreach (object key in element_keys)
3389 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3391 KeyBlock current_kb;
3392 // iteratively merge the blocks while they are at least half full
3393 // there's probably a really cool way to do this with a tree...
3394 while (key_blocks.Count > 1)
3396 var key_blocks_new = new List<KeyBlock> ();
3397 current_kb = (KeyBlock) key_blocks [0];
3398 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3400 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3401 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3404 current_kb.last = kb.last;
3405 current_kb.Size += kb.Size;
3409 // start a new block
3410 key_blocks_new.Add (current_kb);
3414 key_blocks_new.Add (current_kb);
3415 if (key_blocks.Count == key_blocks_new.Count)
3417 key_blocks = key_blocks_new;
3420 // initialize the key lists
3421 foreach (KeyBlock kb in key_blocks)
3422 kb.element_keys = new List<object> ();
3424 // fill the key lists
3426 if (key_blocks.Count > 0) {
3427 current_kb = (KeyBlock) key_blocks [0];
3428 foreach (object key in element_keys)
3430 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3431 System.Convert.ToInt64 (key) > current_kb.last;
3433 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3434 current_kb.element_keys.Add (key);
3438 // sort the blocks so we can tackle the largest ones first
3441 // okay now we can start...
3442 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3443 Label lbl_default = default_target;
3445 Type type_keys = null;
3446 if (element_keys.Length > 0)
3447 type_keys = element_keys [0].GetType (); // used for conversions
3449 TypeSpec compare_type;
3451 if (TypeManager.IsEnumType (SwitchType))
3452 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3454 compare_type = SwitchType;
3456 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3458 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3459 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3462 foreach (object key in kb.element_keys) {
3463 SwitchLabel sl = (SwitchLabel) Elements [key];
3464 if (key is int && (int) key == 0) {
3465 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3468 EmitObjectInteger (ec, key);
3469 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3475 // TODO: if all the keys in the block are the same and there are
3476 // no gaps/defaults then just use a range-check.
3477 if (compare_type == TypeManager.int64_type ||
3478 compare_type == TypeManager.uint64_type)
3480 // TODO: optimize constant/I4 cases
3482 // check block range (could be > 2^31)
3484 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3485 ec.Emit (OpCodes.Blt, lbl_default);
3487 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3488 ec.Emit (OpCodes.Bgt, lbl_default);
3494 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3495 ec.Emit (OpCodes.Sub);
3497 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3503 int first = (int) kb.first;
3507 ec.Emit (OpCodes.Sub);
3511 ec.EmitInt (-first);
3512 ec.Emit (OpCodes.Add);
3516 // first, build the list of labels for the switch
3518 int cJumps = kb.Length;
3519 Label [] switch_labels = new Label [cJumps];
3520 for (int iJump = 0; iJump < cJumps; iJump++)
3522 object key = kb.element_keys [iKey];
3523 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3525 SwitchLabel sl = (SwitchLabel) Elements [key];
3526 switch_labels [iJump] = sl.GetILLabel (ec);
3530 switch_labels [iJump] = lbl_default;
3532 // emit the switch opcode
3533 ec.Emit (OpCodes.Switch, switch_labels);
3536 // mark the default for this block
3538 ec.MarkLabel (lbl_default);
3541 // TODO: find the default case and emit it here,
3542 // to prevent having to do the following jump.
3543 // make sure to mark other labels in the default section
3545 // the last default just goes to the end
3546 if (element_keys.Length > 0)
3547 ec.Emit (OpCodes.Br, lbl_default);
3549 // now emit the code for the sections
3550 bool found_default = false;
3552 foreach (SwitchSection ss in Sections) {
3553 foreach (SwitchLabel sl in ss.Labels) {
3554 if (sl.Converted == SwitchLabel.NullStringCase) {
3555 ec.MarkLabel (null_target);
3556 } else if (sl.Label == null) {
3557 ec.MarkLabel (lbl_default);
3558 found_default = true;
3560 ec.MarkLabel (null_target);
3562 ec.MarkLabel (sl.GetILLabel (ec));
3563 ec.MarkLabel (sl.GetILLabelCode (ec));
3568 if (!found_default) {
3569 ec.MarkLabel (lbl_default);
3570 if (!has_null_case) {
3571 ec.MarkLabel (null_target);
3575 ec.MarkLabel (lbl_end);
3578 SwitchSection FindSection (SwitchLabel label)
3580 foreach (SwitchSection ss in Sections){
3581 foreach (SwitchLabel sl in ss.Labels){
3590 public static void Reset ()
3593 allowed_types = null;
3596 public override bool Resolve (BlockContext ec)
3598 Expr = Expr.Resolve (ec);
3602 new_expr = SwitchGoverningType (ec, Expr);
3604 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3605 unwrap = Nullable.Unwrap.Create (Expr, false);
3609 new_expr = SwitchGoverningType (ec, unwrap);
3612 if (new_expr == null){
3613 ec.Report.Error (151, loc,
3614 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3615 TypeManager.CSharpName (Expr.Type));
3620 SwitchType = new_expr.Type;
3622 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3623 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3627 if (!CheckSwitch (ec))
3631 Elements.Remove (SwitchLabel.NullStringCase);
3633 Switch old_switch = ec.Switch;
3635 ec.Switch.SwitchType = SwitchType;
3637 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3638 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3640 var constant = new_expr as Constant;
3641 if (constant != null) {
3643 object key = constant.GetValue ();
3645 if (Elements.TryGetValue (key, out label))
3646 constant_section = FindSection (label);
3648 if (constant_section == null)
3649 constant_section = default_section;
3654 foreach (SwitchSection ss in Sections){
3656 ec.CurrentBranching.CreateSibling (
3657 null, FlowBranching.SiblingType.SwitchSection);
3661 if (is_constant && (ss != constant_section)) {
3662 // If we're a constant switch, we're only emitting
3663 // one single section - mark all the others as
3665 ec.CurrentBranching.CurrentUsageVector.Goto ();
3666 if (!ss.Block.ResolveUnreachable (ec, true)) {
3670 if (!ss.Block.Resolve (ec))
3675 if (default_section == null)
3676 ec.CurrentBranching.CreateSibling (
3677 null, FlowBranching.SiblingType.SwitchSection);
3679 ec.EndFlowBranching ();
3680 ec.Switch = old_switch;
3682 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3687 if (SwitchType == TypeManager.string_type && !is_constant) {
3688 // TODO: Optimize single case, and single+default case
3689 ResolveStringSwitchMap (ec);
3695 void ResolveStringSwitchMap (ResolveContext ec)
3697 FullNamedExpression string_dictionary_type;
3698 if (TypeManager.generic_ienumerable_type != null) {
3699 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3700 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3702 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3704 new TypeExpression (TypeManager.string_type, loc),
3705 new TypeExpression (TypeManager.int32_type, loc)), loc);
3707 MemberAccess system_collections_generic = new MemberAccess (
3708 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3710 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3713 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3714 Field field = new Field (ctype, string_dictionary_type,
3715 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3716 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3717 if (!field.Define ())
3719 ctype.AddField (field);
3721 var init = new List<Expression> ();
3724 string value = null;
3725 foreach (SwitchSection section in Sections) {
3726 int last_count = init.Count;
3727 foreach (SwitchLabel sl in section.Labels) {
3728 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3731 value = (string) sl.Converted;
3732 var init_args = new List<Expression> (2);
3733 init_args.Add (new StringLiteral (value, sl.Location));
3734 init_args.Add (new IntConstant (counter, loc));
3735 init.Add (new CollectionElementInitializer (init_args, loc));
3739 // Don't add empty sections
3741 if (last_count == init.Count)
3744 Elements.Add (counter, section.Labels [0]);
3748 Arguments args = new Arguments (1);
3749 args.Add (new Argument (new IntConstant (init.Count, loc)));
3750 Expression initializer = new NewInitialize (string_dictionary_type, args,
3751 new CollectionOrObjectInitializers (init, loc), loc);
3753 switch_cache_field = new FieldExpr (field, loc);
3754 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3757 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3759 Label l_initialized = ec.DefineLabel ();
3762 // Skip initialization when value is null
3764 value.EmitBranchable (ec, null_target, false);
3767 // Check if string dictionary is initialized and initialize
3769 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3770 string_dictionary.EmitStatement (ec);
3771 ec.MarkLabel (l_initialized);
3773 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3775 ResolveContext rc = new ResolveContext (ec.MemberContext);
3777 if (TypeManager.generic_ienumerable_type != null) {
3778 Arguments get_value_args = new Arguments (2);
3779 get_value_args.Add (new Argument (value));
3780 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3781 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3782 if (get_item == null)
3786 // A value was not found, go to default case
3788 get_item.EmitBranchable (ec, default_target, false);
3790 Arguments get_value_args = new Arguments (1);
3791 get_value_args.Add (new Argument (value));
3793 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3794 if (get_item == null)
3797 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3798 get_item_object.EmitAssign (ec, get_item, true, false);
3799 ec.Emit (OpCodes.Brfalse, default_target);
3801 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3802 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3804 get_item_int.EmitStatement (ec);
3805 get_item_object.Release (ec);
3808 TableSwitchEmit (ec, string_switch_variable);
3809 string_switch_variable.Release (ec);
3812 protected override void DoEmit (EmitContext ec)
3814 default_target = ec.DefineLabel ();
3815 null_target = ec.DefineLabel ();
3817 // Store variable for comparission purposes
3818 // TODO: Don't duplicate non-captured VariableReference
3819 LocalTemporary value;
3821 value = new LocalTemporary (SwitchType);
3822 unwrap.EmitCheck (ec);
3823 ec.Emit (OpCodes.Brfalse, null_target);
3826 } else if (!is_constant) {
3827 value = new LocalTemporary (SwitchType);
3834 // Setup the codegen context
3836 Label old_end = ec.LoopEnd;
3837 Switch old_switch = ec.Switch;
3839 ec.LoopEnd = ec.DefineLabel ();
3844 if (constant_section != null)
3845 constant_section.Block.Emit (ec);
3846 } else if (string_dictionary != null) {
3847 DoEmitStringSwitch (value, ec);
3849 TableSwitchEmit (ec, value);
3855 // Restore context state.
3856 ec.MarkLabel (ec.LoopEnd);
3859 // Restore the previous context
3861 ec.LoopEnd = old_end;
3862 ec.Switch = old_switch;
3865 protected override void CloneTo (CloneContext clonectx, Statement t)
3867 Switch target = (Switch) t;
3869 target.Expr = Expr.Clone (clonectx);
3870 target.Sections = new List<SwitchSection> ();
3871 foreach (SwitchSection ss in Sections){
3872 target.Sections.Add (ss.Clone (clonectx));
3877 // A place where execution can restart in an iterator
3878 public abstract class ResumableStatement : Statement
3881 protected Label resume_point;
3883 public Label PrepareForEmit (EmitContext ec)
3887 resume_point = ec.DefineLabel ();
3889 return resume_point;
3892 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3896 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3901 // Base class for statements that are implemented in terms of try...finally
3902 public abstract class ExceptionStatement : ResumableStatement
3906 List<ResumableStatement> resume_points;
3907 int first_resume_pc;
3909 protected abstract void EmitPreTryBody (EmitContext ec);
3910 protected abstract void EmitTryBody (EmitContext ec);
3911 protected abstract void EmitFinallyBody (EmitContext ec);
3913 protected sealed override void DoEmit (EmitContext ec)
3915 EmitPreTryBody (ec);
3917 if (resume_points != null) {
3918 ec.EmitInt ((int) Iterator.State.Running);
3919 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3922 ec.BeginExceptionBlock ();
3924 if (resume_points != null) {
3925 ec.MarkLabel (resume_point);
3927 // For normal control flow, we want to fall-through the Switch
3928 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3929 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3930 ec.EmitInt (first_resume_pc);
3931 ec.Emit (OpCodes.Sub);
3933 Label [] labels = new Label [resume_points.Count];
3934 for (int i = 0; i < resume_points.Count; ++i)
3935 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3936 ec.Emit (OpCodes.Switch, labels);
3941 ec.BeginFinallyBlock ();
3943 Label start_finally = ec.DefineLabel ();
3944 if (resume_points != null) {
3945 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3946 ec.Emit (OpCodes.Brfalse_S, start_finally);
3947 ec.Emit (OpCodes.Endfinally);
3950 ec.MarkLabel (start_finally);
3951 EmitFinallyBody (ec);
3953 ec.EndExceptionBlock ();
3956 public void SomeCodeFollows ()
3958 code_follows = true;
3961 public override bool Resolve (BlockContext ec)
3963 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3964 // So, ensure there's some IL code after this statement.
3965 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3966 ec.NeedReturnLabel ();
3968 iter = ec.CurrentIterator;
3972 public void AddResumePoint (ResumableStatement stmt, int pc)
3974 if (resume_points == null) {
3975 resume_points = new List<ResumableStatement> ();
3976 first_resume_pc = pc;
3979 if (pc != first_resume_pc + resume_points.Count)
3980 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3982 resume_points.Add (stmt);
3985 Label dispose_try_block;
3986 bool prepared_for_dispose, emitted_dispose;
3987 public override Label PrepareForDispose (EmitContext ec, Label end)
3989 if (!prepared_for_dispose) {
3990 prepared_for_dispose = true;
3991 dispose_try_block = ec.DefineLabel ();
3993 return dispose_try_block;
3996 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3998 if (emitted_dispose)
4001 emitted_dispose = true;
4003 Label end_of_try = ec.DefineLabel ();
4005 // Ensure that the only way we can get into this code is through a dispatcher
4006 if (have_dispatcher)
4007 ec.Emit (OpCodes.Br, end);
4009 ec.BeginExceptionBlock ();
4011 ec.MarkLabel (dispose_try_block);
4013 Label [] labels = null;
4014 for (int i = 0; i < resume_points.Count; ++i) {
4015 ResumableStatement s = (ResumableStatement) resume_points [i];
4016 Label ret = s.PrepareForDispose (ec, end_of_try);
4017 if (ret.Equals (end_of_try) && labels == null)
4019 if (labels == null) {
4020 labels = new Label [resume_points.Count];
4021 for (int j = 0; j < i; ++j)
4022 labels [j] = end_of_try;
4027 if (labels != null) {
4029 for (j = 1; j < labels.Length; ++j)
4030 if (!labels [0].Equals (labels [j]))
4032 bool emit_dispatcher = j < labels.Length;
4034 if (emit_dispatcher) {
4035 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4036 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4037 ec.EmitInt (first_resume_pc);
4038 ec.Emit (OpCodes.Sub);
4039 ec.Emit (OpCodes.Switch, labels);
4040 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4043 foreach (ResumableStatement s in resume_points)
4044 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4047 ec.MarkLabel (end_of_try);
4049 ec.BeginFinallyBlock ();
4051 EmitFinallyBody (ec);
4053 ec.EndExceptionBlock ();
4057 public class Lock : ExceptionStatement {
4059 public Statement Statement;
4060 TemporaryVariable temp;
4062 public Lock (Expression expr, Statement stmt, Location l)
4069 public override bool Resolve (BlockContext ec)
4071 expr = expr.Resolve (ec);
4075 if (!TypeManager.IsReferenceType (expr.Type)){
4076 ec.Report.Error (185, loc,
4077 "`{0}' is not a reference type as required by the lock statement",
4078 TypeManager.CSharpName (expr.Type));
4082 ec.StartFlowBranching (this);
4083 bool ok = Statement.Resolve (ec);
4084 ec.EndFlowBranching ();
4086 ok &= base.Resolve (ec);
4088 temp = new TemporaryVariable (expr.Type, loc);
4091 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4092 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4093 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4094 monitor_type, "Enter", loc, TypeManager.object_type);
4095 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4096 monitor_type, "Exit", loc, TypeManager.object_type);
4102 protected override void EmitPreTryBody (EmitContext ec)
4104 temp.EmitAssign (ec, expr);
4106 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4109 protected override void EmitTryBody (EmitContext ec)
4111 Statement.Emit (ec);
4114 protected override void EmitFinallyBody (EmitContext ec)
4117 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4120 protected override void CloneTo (CloneContext clonectx, Statement t)
4122 Lock target = (Lock) t;
4124 target.expr = expr.Clone (clonectx);
4125 target.Statement = Statement.Clone (clonectx);
4129 public class Unchecked : Statement {
4132 public Unchecked (Block b, Location loc)
4139 public override bool Resolve (BlockContext ec)
4141 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4142 return Block.Resolve (ec);
4145 protected override void DoEmit (EmitContext ec)
4147 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4151 protected override void CloneTo (CloneContext clonectx, Statement t)
4153 Unchecked target = (Unchecked) t;
4155 target.Block = clonectx.LookupBlock (Block);
4159 public class Checked : Statement {
4162 public Checked (Block b, Location loc)
4165 b.Unchecked = false;
4169 public override bool Resolve (BlockContext ec)
4171 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4172 return Block.Resolve (ec);
4175 protected override void DoEmit (EmitContext ec)
4177 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4181 protected override void CloneTo (CloneContext clonectx, Statement t)
4183 Checked target = (Checked) t;
4185 target.Block = clonectx.LookupBlock (Block);
4189 public class Unsafe : Statement {
4192 public Unsafe (Block b, Location loc)
4195 Block.Unsafe = true;
4199 public override bool Resolve (BlockContext ec)
4201 if (ec.CurrentIterator != null)
4202 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4204 using (ec.Set (ResolveContext.Options.UnsafeScope))
4205 return Block.Resolve (ec);
4208 protected override void DoEmit (EmitContext ec)
4213 protected override void CloneTo (CloneContext clonectx, Statement t)
4215 Unsafe target = (Unsafe) t;
4217 target.Block = clonectx.LookupBlock (Block);
4224 public class Fixed : Statement {
4226 List<KeyValuePair<LocalInfo, Expression>> declarators;
4227 Statement statement;
4232 abstract class Emitter
4234 protected LocalInfo vi;
4235 protected Expression converted;
4237 protected Emitter (Expression expr, LocalInfo li)
4243 public abstract void Emit (EmitContext ec);
4244 public abstract void EmitExit (EmitContext ec);
4247 class ExpressionEmitter : Emitter {
4248 public ExpressionEmitter (Expression converted, LocalInfo li) :
4249 base (converted, li)
4253 public override void Emit (EmitContext ec) {
4255 // Store pointer in pinned location
4257 converted.Emit (ec);
4261 public override void EmitExit (EmitContext ec)
4263 ec.Emit (OpCodes.Ldc_I4_0);
4264 ec.Emit (OpCodes.Conv_U);
4269 class StringEmitter : Emitter
4271 LocalInfo pinned_string;
4273 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4276 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4277 pinned_string.Pinned = true;
4280 public StringEmitter Resolve (ResolveContext rc)
4282 pinned_string.Resolve (rc);
4284 if (TypeManager.int_get_offset_to_string_data == null) {
4285 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4286 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4292 public override void Emit (EmitContext ec)
4294 pinned_string.ResolveVariable (ec);
4296 converted.Emit (ec);
4297 pinned_string.EmitAssign (ec);
4299 // TODO: Should use Binary::Add
4300 pinned_string.Emit (ec);
4301 ec.Emit (OpCodes.Conv_I);
4303 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4304 //pe.InstanceExpression = pinned_string;
4305 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4307 ec.Emit (OpCodes.Add);
4311 public override void EmitExit (EmitContext ec)
4313 ec.Emit (OpCodes.Ldnull);
4314 pinned_string.EmitAssign (ec);
4318 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4321 declarators = decls;
4326 public Statement Statement {
4327 get { return statement; }
4330 public override bool Resolve (BlockContext ec)
4333 Expression.UnsafeError (ec, loc);
4337 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4338 if (texpr == null) {
4339 if (type is VarExpr)
4340 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4345 expr_type = texpr.Type;
4347 data = new Emitter [declarators.Count];
4349 if (!expr_type.IsPointer){
4350 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4355 foreach (var p in declarators){
4356 LocalInfo vi = p.Key;
4357 Expression e = p.Value;
4359 vi.VariableInfo.SetAssigned (ec);
4360 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4363 // The rules for the possible declarators are pretty wise,
4364 // but the production on the grammar is more concise.
4366 // So we have to enforce these rules here.
4368 // We do not resolve before doing the case 1 test,
4369 // because the grammar is explicit in that the token &
4370 // is present, so we need to test for this particular case.
4374 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4378 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4388 if (e.Type.IsArray){
4389 TypeSpec array_type = TypeManager.GetElementType (e.Type);
4392 // Provided that array_type is unmanaged,
4394 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4398 // and T* is implicitly convertible to the
4399 // pointer type given in the fixed statement.
4401 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4403 Expression converted = Convert.ImplicitConversionRequired (
4404 ec, array_ptr, vi.VariableType, loc);
4405 if (converted == null)
4409 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4411 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4412 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4413 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4414 new NullPointer (loc),
4417 converted = converted.Resolve (ec);
4419 data [i] = new ExpressionEmitter (converted, vi);
4428 if (e.Type == TypeManager.string_type){
4429 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4434 // Case 4: fixed buffer
4435 if (e is FixedBufferPtr) {
4436 data [i++] = new ExpressionEmitter (e, vi);
4441 // Case 1: & object.
4443 Unary u = e as Unary;
4444 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4445 IVariableReference vr = u.Expr as IVariableReference;
4446 if (vr == null || !vr.IsFixed) {
4447 data [i] = new ExpressionEmitter (e, vi);
4451 if (data [i++] == null)
4452 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4454 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4457 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4458 bool ok = statement.Resolve (ec);
4459 bool flow_unreachable = ec.EndFlowBranching ();
4460 has_ret = flow_unreachable;
4465 protected override void DoEmit (EmitContext ec)
4467 for (int i = 0; i < data.Length; i++) {
4471 statement.Emit (ec);
4477 // Clear the pinned variable
4479 for (int i = 0; i < data.Length; i++) {
4480 data [i].EmitExit (ec);
4484 protected override void CloneTo (CloneContext clonectx, Statement t)
4486 Fixed target = (Fixed) t;
4488 target.type = type.Clone (clonectx);
4489 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4490 foreach (var p in declarators) {
4491 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4492 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4495 target.statement = statement.Clone (clonectx);
4499 public class Catch : Statement {
4500 public readonly string Name;
4502 public Block VarBlock;
4504 Expression type_expr;
4507 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4512 VarBlock = var_block;
4516 public TypeSpec CatchType {
4522 public bool IsGeneral {
4524 return type_expr == null;
4528 protected override void DoEmit (EmitContext ec)
4530 if (CatchType != null)
4531 ec.BeginCatchBlock (CatchType);
4533 ec.BeginCatchBlock (TypeManager.object_type);
4535 if (VarBlock != null)
4539 // TODO: Move to resolve
4540 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4541 lvr.Resolve (new ResolveContext (ec.MemberContext));
4543 // Only to make verifier happy
4544 if (TypeManager.IsGenericParameter (lvr.Type))
4545 ec.Emit (OpCodes.Unbox_Any, lvr.Type);
4548 if (lvr.IsHoisted) {
4549 LocalTemporary lt = new LocalTemporary (lvr.Type);
4553 // Variable is at the top of the stack
4554 source = EmptyExpression.Null;
4557 lvr.EmitAssign (ec, source, false, false);
4559 ec.Emit (OpCodes.Pop);
4564 public override bool Resolve (BlockContext ec)
4566 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4567 if (type_expr != null) {
4568 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4574 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)){
4575 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4581 if (!Block.Resolve (ec))
4584 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4585 // emit the "unused variable" warnings.
4586 if (VarBlock != null)
4587 return VarBlock.Resolve (ec);
4593 protected override void CloneTo (CloneContext clonectx, Statement t)
4595 Catch target = (Catch) t;
4597 if (type_expr != null)
4598 target.type_expr = type_expr.Clone (clonectx);
4599 if (VarBlock != null)
4600 target.VarBlock = clonectx.LookupBlock (VarBlock);
4601 target.Block = clonectx.LookupBlock (Block);
4605 public class TryFinally : ExceptionStatement {
4609 public TryFinally (Statement stmt, Block fini, Location l)
4616 public override bool Resolve (BlockContext ec)
4620 ec.StartFlowBranching (this);
4622 if (!stmt.Resolve (ec))
4626 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4627 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4628 if (!fini.Resolve (ec))
4632 ec.EndFlowBranching ();
4634 ok &= base.Resolve (ec);
4639 protected override void EmitPreTryBody (EmitContext ec)
4643 protected override void EmitTryBody (EmitContext ec)
4648 protected override void EmitFinallyBody (EmitContext ec)
4653 protected override void CloneTo (CloneContext clonectx, Statement t)
4655 TryFinally target = (TryFinally) t;
4657 target.stmt = (Statement) stmt.Clone (clonectx);
4659 target.fini = clonectx.LookupBlock (fini);
4663 public class TryCatch : Statement {
4665 public List<Catch> Specific;
4666 public Catch General;
4667 bool inside_try_finally, code_follows;
4669 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4672 this.Specific = catch_clauses;
4673 this.inside_try_finally = inside_try_finally;
4675 Catch c = catch_clauses [0];
4678 catch_clauses.RemoveAt (0);
4684 public override bool Resolve (BlockContext ec)
4688 ec.StartFlowBranching (this);
4690 if (!Block.Resolve (ec))
4693 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4695 foreach (Catch c in Specific){
4696 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4698 if (c.Name != null) {
4699 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4701 throw new Exception ();
4703 vi.VariableInfo = null;
4706 if (!c.Resolve (ec)) {
4711 TypeSpec resolved_type = c.CatchType;
4712 for (int ii = 0; ii < last_index; ++ii) {
4713 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4714 ec.Report.Error (160, c.loc,
4715 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4716 TypeManager.CSharpName (prev_catches [ii]));
4721 prev_catches [last_index++] = resolved_type;
4724 if (General != null) {
4725 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4726 foreach (Catch c in Specific){
4727 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4728 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'");
4733 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4735 if (!General.Resolve (ec))
4739 ec.EndFlowBranching ();
4741 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4742 // So, ensure there's some IL code after this statement
4743 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4744 ec.NeedReturnLabel ();
4749 public void SomeCodeFollows ()
4751 code_follows = true;
4754 protected override void DoEmit (EmitContext ec)
4756 if (!inside_try_finally)
4757 ec.BeginExceptionBlock ();
4761 foreach (Catch c in Specific)
4764 if (General != null)
4767 if (!inside_try_finally)
4768 ec.EndExceptionBlock ();
4771 protected override void CloneTo (CloneContext clonectx, Statement t)
4773 TryCatch target = (TryCatch) t;
4775 target.Block = clonectx.LookupBlock (Block);
4776 if (General != null)
4777 target.General = (Catch) General.Clone (clonectx);
4778 if (Specific != null){
4779 target.Specific = new List<Catch> ();
4780 foreach (Catch c in Specific)
4781 target.Specific.Add ((Catch) c.Clone (clonectx));
4786 // FIXME: Why is it almost exact copy of Using ??
4787 public class UsingTemporary : ExceptionStatement
4789 protected TemporaryVariable local_copy;
4790 Statement statement;
4792 protected Statement dispose_call;
4794 public UsingTemporary (Expression expr, Statement stmt, Location l)
4802 public Expression Expression {
4808 public Statement Statement {
4816 protected virtual bool DoResolve (BlockContext ec)
4818 expr = expr.Resolve (ec);
4822 if (!expr.Type.ImplementsInterface (TypeManager.idisposable_type) &&
4823 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4824 if (expr.Type != InternalType.Dynamic) {
4825 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4829 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4832 var expr_type = expr.Type;
4834 local_copy = new TemporaryVariable (expr_type, loc);
4835 local_copy.Resolve (ec);
4837 if (TypeManager.void_dispose_void == null) {
4838 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4839 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4842 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
4843 dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
4844 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
4847 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
4849 // Add conditional call when disposing possible null variable
4850 if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
4851 dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
4853 return dispose_call.Resolve (ec);
4856 public override bool Resolve (BlockContext ec)
4858 bool ok = DoResolve (ec);
4860 ec.StartFlowBranching (this);
4862 ok &= statement.Resolve (ec);
4864 ec.EndFlowBranching ();
4866 ok &= base.Resolve (ec);
4871 protected override void EmitPreTryBody (EmitContext ec)
4873 local_copy.EmitAssign (ec, expr);
4876 protected override void EmitTryBody (EmitContext ec)
4878 statement.Emit (ec);
4881 protected override void EmitFinallyBody (EmitContext ec)
4883 dispose_call.Emit (ec);
4886 protected override void CloneTo (CloneContext clonectx, Statement t)
4888 UsingTemporary target = (UsingTemporary) t;
4890 target.expr = expr.Clone (clonectx);
4891 target.statement = statement.Clone (clonectx);
4895 public class Using : ExceptionStatement {
4897 public Statement EmbeddedStatement {
4898 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4904 ExpressionStatement assign;
4906 public Using (Expression var, Expression init, Statement stmt, Location l)
4914 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4916 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4917 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4918 TypeManager.CSharpName (expr.Type));
4921 protected override void EmitPreTryBody (EmitContext ec)
4923 assign.EmitStatement (ec);
4926 protected override void EmitTryBody (EmitContext ec)
4931 protected override void EmitFinallyBody (EmitContext ec)
4933 Label skip = ec.DefineLabel ();
4935 bool emit_null_check = !TypeManager.IsValueType (var.Type);
4936 if (emit_null_check) {
4938 ec.Emit (OpCodes.Brfalse, skip);
4941 Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
4943 if (emit_null_check)
4944 ec.MarkLabel (skip);
4947 public override bool Resolve (BlockContext ec)
4949 if (!ResolveVariable (ec))
4952 ec.StartFlowBranching (this);
4954 bool ok = stmt.Resolve (ec);
4956 ec.EndFlowBranching ();
4958 ok &= base.Resolve (ec);
4960 if (TypeManager.void_dispose_void == null) {
4961 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4962 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4968 bool ResolveVariable (BlockContext ec)
4970 assign = new SimpleAssign (var, init, loc);
4971 assign = assign.ResolveStatement (ec);
4975 if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4979 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
4981 if (assign.Type == InternalType.Dynamic) {
4982 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
4983 var = new TemporaryVariable (e.Type, loc);
4984 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
4988 Error_IsNotConvertibleToIDisposable (ec, var);
4992 throw new NotImplementedException ("covariance?");
4995 protected override void CloneTo (CloneContext clonectx, Statement t)
4997 Using target = (Using) t;
4999 target.var = var.Clone (clonectx);
5000 target.init = init.Clone (clonectx);
5001 target.stmt = stmt.Clone (clonectx);
5006 /// Implementation of the foreach C# statement
5008 public class Foreach : Statement {
5010 sealed class ArrayForeach : Statement
5012 class ArrayCounter : TemporaryVariable
5014 StatementExpression increment;
5016 public ArrayCounter (Location loc)
5017 : base (TypeManager.int32_type, loc)
5021 public void ResolveIncrement (BlockContext ec)
5023 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5024 increment.Resolve (ec);
5027 public void EmitIncrement (EmitContext ec)
5029 increment.Emit (ec);
5033 readonly Foreach for_each;
5034 readonly Statement statement;
5037 TemporaryVariable[] lengths;
5038 Expression [] length_exprs;
5039 ArrayCounter[] counter;
5041 TemporaryVariable copy;
5044 public ArrayForeach (Foreach @foreach, int rank)
5046 for_each = @foreach;
5047 statement = for_each.statement;
5050 counter = new ArrayCounter [rank];
5051 length_exprs = new Expression [rank];
5054 // Only use temporary length variables when dealing with
5055 // multi-dimensional arrays
5058 lengths = new TemporaryVariable [rank];
5061 protected override void CloneTo (CloneContext clonectx, Statement target)
5063 throw new NotImplementedException ();
5066 public override bool Resolve (BlockContext ec)
5068 copy = new TemporaryVariable (for_each.expr.Type, loc);
5071 int rank = length_exprs.Length;
5072 Arguments list = new Arguments (rank);
5073 for (int i = 0; i < rank; i++) {
5074 counter [i] = new ArrayCounter (loc);
5075 counter [i].ResolveIncrement (ec);
5078 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5080 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5081 lengths [i].Resolve (ec);
5083 Arguments args = new Arguments (1);
5084 args.Add (new Argument (new IntConstant (i, loc)));
5085 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5088 list.Add (new Argument (counter [i]));
5091 access = new ElementAccess (copy, list, loc).Resolve (ec);
5095 Expression var_type = for_each.type;
5096 VarExpr ve = var_type as VarExpr;
5098 // Infer implicitly typed local variable from foreach array type
5099 var_type = new TypeExpression (access.Type, ve.Location);
5102 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5103 if (var_type == null)
5106 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5112 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5113 ec.CurrentBranching.CreateSibling ();
5115 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5116 if (for_each.variable == null)
5119 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5120 if (!statement.Resolve (ec))
5122 ec.EndFlowBranching ();
5124 // There's no direct control flow from the end of the embedded statement to the end of the loop
5125 ec.CurrentBranching.CurrentUsageVector.Goto ();
5127 ec.EndFlowBranching ();
5132 protected override void DoEmit (EmitContext ec)
5134 copy.EmitAssign (ec, for_each.expr);
5136 int rank = length_exprs.Length;
5137 Label[] test = new Label [rank];
5138 Label[] loop = new Label [rank];
5140 for (int i = 0; i < rank; i++) {
5141 test [i] = ec.DefineLabel ();
5142 loop [i] = ec.DefineLabel ();
5144 if (lengths != null)
5145 lengths [i].EmitAssign (ec, length_exprs [i]);
5148 IntConstant zero = new IntConstant (0, loc);
5149 for (int i = 0; i < rank; i++) {
5150 counter [i].EmitAssign (ec, zero);
5152 ec.Emit (OpCodes.Br, test [i]);
5153 ec.MarkLabel (loop [i]);
5156 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5158 statement.Emit (ec);
5160 ec.MarkLabel (ec.LoopBegin);
5162 for (int i = rank - 1; i >= 0; i--){
5163 counter [i].EmitIncrement (ec);
5165 ec.MarkLabel (test [i]);
5166 counter [i].Emit (ec);
5168 if (lengths != null)
5169 lengths [i].Emit (ec);
5171 length_exprs [i].Emit (ec);
5173 ec.Emit (OpCodes.Blt, loop [i]);
5176 ec.MarkLabel (ec.LoopEnd);
5180 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5182 class Body : Statement
5185 Expression variable, current, conv;
5186 Statement statement;
5189 public Body (TypeSpec type, Expression variable,
5190 Expression current, Statement statement,
5194 this.variable = variable;
5195 this.current = current;
5196 this.statement = statement;
5200 protected override void CloneTo (CloneContext clonectx, Statement target)
5202 throw new NotImplementedException ();
5205 public override bool Resolve (BlockContext ec)
5207 current = current.Resolve (ec);
5208 if (current == null)
5211 conv = Convert.ExplicitConversion (ec, current, type, loc);
5215 assign = new SimpleAssign (variable, conv, loc);
5216 if (assign.Resolve (ec) == null)
5219 if (!statement.Resolve (ec))
5225 protected override void DoEmit (EmitContext ec)
5227 assign.EmitStatement (ec);
5228 statement.Emit (ec);
5232 class Dispose : UsingTemporary
5234 LocalTemporary dispose;
5236 public Dispose (TemporaryVariable variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
5237 : base (expr, statement, loc)
5239 base.local_copy = variable;
5240 this.dispose = dispose;
5243 protected override bool DoResolve (BlockContext ec)
5245 if (TypeManager.void_dispose_void == null) {
5246 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5247 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5250 Expression dispose_var = (Expression) dispose ?? local_copy;
5252 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5253 dispose_mg.InstanceExpression = dispose_var;
5255 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
5257 if (!dispose_var.Type.IsStruct)
5258 dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
5260 return dispose_call.Resolve (ec);
5263 protected override void EmitFinallyBody (EmitContext ec)
5265 Label call_dispose = ec.DefineLabel ();
5266 if (dispose != null) {
5267 local_copy.Emit (ec, false);
5268 ec.Emit (OpCodes.Isinst, dispose.Type);
5272 base.EmitFinallyBody (ec);
5274 if (dispose != null) {
5275 ec.MarkLabel (call_dispose);
5276 dispose.Release (ec);
5281 Expression variable, expr;
5282 Statement statement;
5283 Expression var_type;
5284 ExpressionStatement init;
5285 bool ambiguous_getenumerator_name;
5287 public CollectionForeach (Expression var_type, Expression var,
5288 Expression expr, Statement stmt, Location l)
5290 this.var_type = var_type;
5291 this.variable = var;
5297 protected override void CloneTo (CloneContext clonectx, Statement target)
5299 throw new NotImplementedException ();
5302 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5304 rc.Report.SymbolRelatedToPreviousError (enumerator);
5305 rc.Report.Error (202, loc,
5306 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5307 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5310 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5313 // Option 1: Try to match by name GetEnumerator first
5315 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type, "GetEnumerator", 0, true, loc); // TODO: What if CS0229 ?
5317 var mg = mexpr as MethodGroupExpr;
5319 mg.InstanceExpression = expr;
5320 Arguments args = new Arguments (0);
5321 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5323 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5324 if (ambiguous_getenumerator_name)
5327 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5333 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5335 TypeSpec iface_candidate = null;
5336 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5337 var ifaces = t.Interfaces;
5338 if (ifaces != null) {
5339 foreach (var iface in ifaces) {
5340 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5341 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5342 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5343 rc.Report.Error (1640, loc,
5344 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5345 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5350 iface_candidate = iface;
5354 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5355 iface_candidate = iface;
5361 if (iface_candidate == null) {
5362 rc.Report.Error (1579, loc,
5363 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5364 expr.Type.GetSignatureForError (), "GetEnumerator");
5369 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5370 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5375 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5376 mg.InstanceExpression = expr;
5380 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5382 var ms = MemberCache.FindMember (enumerator.ReturnType,
5383 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5384 BindingRestriction.InstanceOnly) as MethodSpec;
5386 if (ms == null || !ms.IsPublic) {
5387 Error_WrongEnumerator (rc, enumerator);
5391 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5394 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5396 var ps = MemberCache.FindMember (enumerator.ReturnType,
5397 MemberFilter.Property ("Current", null),
5398 BindingRestriction.InstanceOnly) as PropertySpec;
5400 if (ps == null || !ps.IsPublic) {
5401 Error_WrongEnumerator (rc, enumerator);
5408 public override bool Resolve (BlockContext ec)
5410 bool is_dynamic = expr.Type == InternalType.Dynamic;
5412 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5414 var get_enumerator_mg = ResolveGetEnumerator (ec);
5415 if (get_enumerator_mg == null) {
5419 var get_enumerator = get_enumerator_mg.BestCandidate;
5420 var enumerator = new TemporaryVariable (get_enumerator.ReturnType, loc);
5421 enumerator.Resolve (ec);
5423 // Prepare bool MoveNext ()
5424 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5425 if (move_next_mg == null) {
5429 move_next_mg.InstanceExpression = enumerator;
5431 // Prepare ~T~ Current { get; }
5432 var current_prop = ResolveCurrent (ec, get_enumerator);
5433 if (current_prop == null) {
5437 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator }.Resolve (ec);
5438 if (current_pe == null)
5441 VarExpr ve = var_type as VarExpr;
5443 // Infer implicitly typed local variable from foreach enumerable type
5444 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5447 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5448 if (var_type == null)
5451 var init = new Invocation (get_enumerator_mg, null);
5454 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5455 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5457 var enum_type = enumerator.Type;
5460 // Add Dispose method call when enumerator can be IDisposable
5462 if (!enumerator.Type.ImplementsInterface (TypeManager.idisposable_type)) {
5463 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5465 // Runtime Dispose check
5467 var tv = new LocalTemporary (TypeManager.idisposable_type);
5468 statement = new Dispose (enumerator, tv, init, statement, loc);
5471 // No Dispose call needed
5473 this.init = new SimpleAssign (enumerator, init);
5474 this.init.Resolve (ec);
5478 // Static Dispose check
5480 statement = new Dispose (enumerator, null, init, statement, loc);
5483 return statement.Resolve (ec);
5486 protected override void DoEmit (EmitContext ec)
5489 init.EmitStatement (ec);
5491 statement.Emit (ec);
5494 #region IErrorHandler Members
5496 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5498 ec.Report.SymbolRelatedToPreviousError (best);
5499 ec.Report.Warning (278, 2, loc,
5500 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5501 expr.Type.GetSignatureForError (), "enumerable",
5502 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5504 ambiguous_getenumerator_name = true;
5508 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5513 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5518 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5527 Expression variable;
5529 Statement statement;
5531 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5532 Statement stmt, Location l)
5535 this.variable = var;
5541 public Statement Statement {
5542 get { return statement; }
5545 public override bool Resolve (BlockContext ec)
5547 expr = expr.Resolve (ec);
5552 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5556 if (expr.Type == TypeManager.string_type) {
5557 statement = new ArrayForeach (this, 1);
5558 } else if (expr.Type is ArrayContainer) {
5559 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5561 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5562 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5563 expr.ExprClassName);
5567 statement = new CollectionForeach (type, variable, expr, statement, loc);
5570 return statement.Resolve (ec);
5573 protected override void DoEmit (EmitContext ec)
5575 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5576 ec.LoopBegin = ec.DefineLabel ();
5577 ec.LoopEnd = ec.DefineLabel ();
5579 statement.Emit (ec);
5581 ec.LoopBegin = old_begin;
5582 ec.LoopEnd = old_end;
5585 protected override void CloneTo (CloneContext clonectx, Statement t)
5587 Foreach target = (Foreach) t;
5589 target.type = type.Clone (clonectx);
5590 target.variable = variable.Clone (clonectx);
5591 target.expr = expr.Clone (clonectx);
5592 target.statement = statement.Clone (clonectx);