2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections.Generic;
20 namespace Mono.CSharp {
22 public abstract class Statement {
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (BlockContext ec)
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
38 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
50 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
52 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53 bool ok = Resolve (ec);
54 ec.KillFlowBranching ();
60 /// Return value indicates whether all code paths emitted return.
62 protected abstract void DoEmit (EmitContext ec);
64 public virtual void Emit (EmitContext ec)
71 // This routine must be overrided in derived classes and make copies
72 // of all the data that might be modified if resolved
74 protected abstract void CloneTo (CloneContext clonectx, Statement target);
76 public Statement Clone (CloneContext clonectx)
78 Statement s = (Statement) this.MemberwiseClone ();
79 CloneTo (clonectx, s);
83 public virtual Expression CreateExpressionTree (ResolveContext ec)
85 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
89 public Statement PerformClone ()
91 CloneContext clonectx = new CloneContext ();
93 return Clone (clonectx);
97 public sealed class EmptyStatement : Statement
99 public EmptyStatement (Location loc)
104 public override bool Resolve (BlockContext ec)
109 public override bool ResolveUnreachable (BlockContext ec, bool warn)
114 public override void Emit (EmitContext ec)
118 protected override void DoEmit (EmitContext ec)
120 throw new NotSupportedException ();
123 protected override void CloneTo (CloneContext clonectx, Statement target)
129 public class If : Statement {
131 public Statement TrueStatement;
132 public Statement FalseStatement;
136 public If (Expression bool_expr, Statement true_statement, Location l)
137 : this (bool_expr, true_statement, null, l)
141 public If (Expression bool_expr,
142 Statement true_statement,
143 Statement false_statement,
146 this.expr = bool_expr;
147 TrueStatement = true_statement;
148 FalseStatement = false_statement;
152 public override bool Resolve (BlockContext ec)
156 Report.Debug (1, "START IF BLOCK", loc);
158 expr = expr.Resolve (ec);
163 // Dead code elimination
165 if (expr is Constant) {
166 bool take = !((Constant) expr).IsDefaultValue;
169 if (!TrueStatement.Resolve (ec))
172 if ((FalseStatement != null) &&
173 !FalseStatement.ResolveUnreachable (ec, true))
175 FalseStatement = null;
177 if (!TrueStatement.ResolveUnreachable (ec, true))
179 TrueStatement = null;
181 if ((FalseStatement != null) &&
182 !FalseStatement.Resolve (ec))
190 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
192 ok &= TrueStatement.Resolve (ec);
194 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
196 ec.CurrentBranching.CreateSibling ();
198 if (FalseStatement != null)
199 ok &= FalseStatement.Resolve (ec);
201 ec.EndFlowBranching ();
203 Report.Debug (1, "END IF BLOCK", loc);
208 protected override void DoEmit (EmitContext ec)
210 Label false_target = ec.DefineLabel ();
214 // If we're a boolean constant, Resolve() already
215 // eliminated dead code for us.
217 Constant c = expr as Constant;
219 c.EmitSideEffect (ec);
221 if (!c.IsDefaultValue)
222 TrueStatement.Emit (ec);
223 else if (FalseStatement != null)
224 FalseStatement.Emit (ec);
229 expr.EmitBranchable (ec, false_target, false);
231 TrueStatement.Emit (ec);
233 if (FalseStatement != null){
234 bool branch_emitted = false;
236 end = ec.DefineLabel ();
238 ec.Emit (OpCodes.Br, end);
239 branch_emitted = true;
242 ec.MarkLabel (false_target);
243 FalseStatement.Emit (ec);
248 ec.MarkLabel (false_target);
252 protected override void CloneTo (CloneContext clonectx, Statement t)
256 target.expr = expr.Clone (clonectx);
257 target.TrueStatement = TrueStatement.Clone (clonectx);
258 if (FalseStatement != null)
259 target.FalseStatement = FalseStatement.Clone (clonectx);
263 public class Do : Statement {
264 public Expression expr;
265 public Statement EmbeddedStatement;
267 public Do (Statement statement, BooleanExpression bool_expr, Location l)
270 EmbeddedStatement = statement;
274 public override bool Resolve (BlockContext ec)
278 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
280 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
282 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
283 if (!EmbeddedStatement.Resolve (ec))
285 ec.EndFlowBranching ();
287 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
288 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
290 expr = expr.Resolve (ec);
293 else if (expr is Constant){
294 bool infinite = !((Constant) expr).IsDefaultValue;
296 ec.CurrentBranching.CurrentUsageVector.Goto ();
299 ec.EndFlowBranching ();
304 protected override void DoEmit (EmitContext ec)
306 Label loop = ec.DefineLabel ();
307 Label old_begin = ec.LoopBegin;
308 Label old_end = ec.LoopEnd;
310 ec.LoopBegin = ec.DefineLabel ();
311 ec.LoopEnd = ec.DefineLabel ();
314 EmbeddedStatement.Emit (ec);
315 ec.MarkLabel (ec.LoopBegin);
318 // Dead code elimination
320 if (expr is Constant){
321 bool res = !((Constant) expr).IsDefaultValue;
323 expr.EmitSideEffect (ec);
325 ec.Emit (OpCodes.Br, loop);
327 expr.EmitBranchable (ec, loop, true);
329 ec.MarkLabel (ec.LoopEnd);
331 ec.LoopBegin = old_begin;
332 ec.LoopEnd = old_end;
335 protected override void CloneTo (CloneContext clonectx, Statement t)
339 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
340 target.expr = expr.Clone (clonectx);
344 public class While : Statement {
345 public Expression expr;
346 public Statement Statement;
347 bool infinite, empty;
349 public While (BooleanExpression bool_expr, Statement statement, Location l)
351 this.expr = bool_expr;
352 Statement = statement;
356 public override bool Resolve (BlockContext ec)
360 expr = expr.Resolve (ec);
365 // Inform whether we are infinite or not
367 if (expr is Constant){
368 bool value = !((Constant) expr).IsDefaultValue;
371 if (!Statement.ResolveUnreachable (ec, true))
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
381 ec.CurrentBranching.CreateSibling ();
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!Statement.Resolve (ec))
386 ec.EndFlowBranching ();
388 // There's no direct control flow from the end of the embedded statement to the end of the loop
389 ec.CurrentBranching.CurrentUsageVector.Goto ();
391 ec.EndFlowBranching ();
396 protected override void DoEmit (EmitContext ec)
399 expr.EmitSideEffect (ec);
403 Label old_begin = ec.LoopBegin;
404 Label old_end = ec.LoopEnd;
406 ec.LoopBegin = ec.DefineLabel ();
407 ec.LoopEnd = ec.DefineLabel ();
410 // Inform whether we are infinite or not
412 if (expr is Constant){
413 // expr is 'true', since the 'empty' case above handles the 'false' case
414 ec.MarkLabel (ec.LoopBegin);
415 expr.EmitSideEffect (ec);
417 ec.Emit (OpCodes.Br, ec.LoopBegin);
420 // Inform that we are infinite (ie, `we return'), only
421 // if we do not `break' inside the code.
423 ec.MarkLabel (ec.LoopEnd);
425 Label while_loop = ec.DefineLabel ();
427 ec.Emit (OpCodes.Br, ec.LoopBegin);
428 ec.MarkLabel (while_loop);
432 ec.MarkLabel (ec.LoopBegin);
435 expr.EmitBranchable (ec, while_loop, true);
437 ec.MarkLabel (ec.LoopEnd);
440 ec.LoopBegin = old_begin;
441 ec.LoopEnd = old_end;
444 public override void Emit (EmitContext ec)
449 protected override void CloneTo (CloneContext clonectx, Statement t)
451 While target = (While) t;
453 target.expr = expr.Clone (clonectx);
454 target.Statement = Statement.Clone (clonectx);
458 public class For : Statement {
460 Statement InitStatement;
462 public Statement Statement;
463 bool infinite, empty;
465 public For (Statement init_statement,
466 BooleanExpression test,
471 InitStatement = init_statement;
473 Increment = increment;
474 Statement = statement;
478 public override bool Resolve (BlockContext ec)
482 if (InitStatement != null){
483 if (!InitStatement.Resolve (ec))
488 Test = Test.Resolve (ec);
491 else if (Test is Constant){
492 bool value = !((Constant) Test).IsDefaultValue;
495 if (!Statement.ResolveUnreachable (ec, true))
497 if ((Increment != null) &&
498 !Increment.ResolveUnreachable (ec, false))
508 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
510 ec.CurrentBranching.CreateSibling ();
512 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
514 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
515 if (!Statement.Resolve (ec))
517 ec.EndFlowBranching ();
519 if (Increment != null){
520 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
521 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
524 if (!Increment.Resolve (ec))
529 // There's no direct control flow from the end of the embedded statement to the end of the loop
530 ec.CurrentBranching.CurrentUsageVector.Goto ();
532 ec.EndFlowBranching ();
537 protected override void DoEmit (EmitContext ec)
539 if (InitStatement != null)
540 InitStatement.Emit (ec);
543 Test.EmitSideEffect (ec);
547 Label old_begin = ec.LoopBegin;
548 Label old_end = ec.LoopEnd;
549 Label loop = ec.DefineLabel ();
550 Label test = ec.DefineLabel ();
552 ec.LoopBegin = ec.DefineLabel ();
553 ec.LoopEnd = ec.DefineLabel ();
555 ec.Emit (OpCodes.Br, test);
559 ec.MarkLabel (ec.LoopBegin);
564 // If test is null, there is no test, and we are just
569 // The Resolve code already catches the case for
570 // Test == Constant (false) so we know that
573 if (Test is Constant) {
574 Test.EmitSideEffect (ec);
575 ec.Emit (OpCodes.Br, loop);
577 Test.EmitBranchable (ec, loop, true);
581 ec.Emit (OpCodes.Br, loop);
582 ec.MarkLabel (ec.LoopEnd);
584 ec.LoopBegin = old_begin;
585 ec.LoopEnd = old_end;
588 protected override void CloneTo (CloneContext clonectx, Statement t)
590 For target = (For) t;
592 if (InitStatement != null)
593 target.InitStatement = InitStatement.Clone (clonectx);
595 target.Test = Test.Clone (clonectx);
596 if (Increment != null)
597 target.Increment = Increment.Clone (clonectx);
598 target.Statement = Statement.Clone (clonectx);
602 public class StatementExpression : Statement {
603 ExpressionStatement expr;
605 public StatementExpression (ExpressionStatement expr)
611 public override bool Resolve (BlockContext ec)
613 expr = expr.ResolveStatement (ec);
617 protected override void DoEmit (EmitContext ec)
619 expr.EmitStatement (ec);
622 public override string ToString ()
624 return "StatementExpression (" + expr + ")";
627 protected override void CloneTo (CloneContext clonectx, Statement t)
629 StatementExpression target = (StatementExpression) t;
631 target.expr = (ExpressionStatement) expr.Clone (clonectx);
636 // Simple version of statement list not requiring a block
638 public class StatementList : Statement
640 List<Statement> statements;
642 public StatementList (Statement first, Statement second)
644 statements = new List<Statement> () { first, second };
648 public IList<Statement> Statements {
655 public void Add (Statement statement)
657 statements.Add (statement);
660 public override bool Resolve (BlockContext ec)
662 foreach (var s in statements)
668 protected override void DoEmit (EmitContext ec)
670 foreach (var s in statements)
674 protected override void CloneTo (CloneContext clonectx, Statement target)
676 StatementList t = (StatementList) target;
678 t.statements = new List<Statement> (statements.Count);
679 foreach (Statement s in statements)
680 t.statements.Add (s.Clone (clonectx));
684 // A 'return' or a 'yield break'
685 public abstract class ExitStatement : Statement
687 protected bool unwind_protect;
688 protected abstract bool DoResolve (BlockContext ec);
690 public virtual void Error_FinallyClause (Report Report)
692 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
695 public sealed override bool Resolve (BlockContext ec)
700 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
702 ec.NeedReturnLabel ();
703 ec.CurrentBranching.CurrentUsageVector.Goto ();
709 /// Implements the return statement
711 public class Return : ExitStatement
713 protected Expression Expr;
714 public Return (Expression expr, Location l)
721 public Expression Expression {
728 protected override bool DoResolve (BlockContext ec)
731 if (ec.ReturnType == TypeManager.void_type)
734 ec.Report.Error (126, loc,
735 "An object of a type convertible to `{0}' is required for the return statement",
736 TypeManager.CSharpName (ec.ReturnType));
740 if (ec.CurrentBlock.Toplevel.IsIterator) {
741 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
742 "statement to return a value, or yield break to end the iteration");
745 AnonymousExpression am = ec.CurrentAnonymousMethod;
746 if (am == null && ec.ReturnType == TypeManager.void_type) {
747 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
748 ec.GetSignatureForError ());
751 Expr = Expr.Resolve (ec);
755 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
756 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
760 if (Expr.Type != ec.ReturnType) {
761 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
765 ec.Report.Error (1662, loc,
766 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
767 am.ContainerType, am.GetSignatureForError ());
776 protected override void DoEmit (EmitContext ec)
782 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
786 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
788 ec.Emit (OpCodes.Ret);
791 protected override void CloneTo (CloneContext clonectx, Statement t)
793 Return target = (Return) t;
794 // It's null for simple return;
796 target.Expr = Expr.Clone (clonectx);
800 public class Goto : Statement {
802 LabeledStatement label;
805 public override bool Resolve (BlockContext ec)
807 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
808 ec.CurrentBranching.CurrentUsageVector.Goto ();
812 public Goto (string label, Location l)
818 public string Target {
819 get { return target; }
822 public void SetResolvedTarget (LabeledStatement label)
825 label.AddReference ();
828 protected override void CloneTo (CloneContext clonectx, Statement target)
833 protected override void DoEmit (EmitContext ec)
836 throw new InternalErrorException ("goto emitted before target resolved");
837 Label l = label.LabelTarget (ec);
838 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
842 public class LabeledStatement : Statement {
848 FlowBranching.UsageVector vectors;
850 public LabeledStatement (string name, Location l)
856 public Label LabelTarget (EmitContext ec)
861 label = ec.DefineLabel ();
870 public bool IsDefined {
871 get { return defined; }
874 public bool HasBeenReferenced {
875 get { return referenced; }
878 public FlowBranching.UsageVector JumpOrigins {
879 get { return vectors; }
882 public void AddUsageVector (FlowBranching.UsageVector vector)
884 vector = vector.Clone ();
885 vector.Next = vectors;
889 protected override void CloneTo (CloneContext clonectx, Statement target)
894 public override bool Resolve (BlockContext ec)
896 // this flow-branching will be terminated when the surrounding block ends
897 ec.StartFlowBranching (this);
901 protected override void DoEmit (EmitContext ec)
904 ec.MarkLabel (label);
907 public void AddReference ()
915 /// `goto default' statement
917 public class GotoDefault : Statement {
919 public GotoDefault (Location l)
924 protected override void CloneTo (CloneContext clonectx, Statement target)
929 public override bool Resolve (BlockContext ec)
931 ec.CurrentBranching.CurrentUsageVector.Goto ();
933 if (ec.Switch == null) {
934 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
938 if (!ec.Switch.GotDefault) {
939 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
946 protected override void DoEmit (EmitContext ec)
948 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
953 /// `goto case' statement
955 public class GotoCase : Statement {
959 public GotoCase (Expression e, Location l)
965 public override bool Resolve (BlockContext ec)
967 if (ec.Switch == null){
968 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
972 ec.CurrentBranching.CurrentUsageVector.Goto ();
974 expr = expr.Resolve (ec);
978 Constant c = expr as Constant;
980 ec.Report.Error (150, expr.Location, "A constant value is expected");
984 TypeSpec type = ec.Switch.SwitchType;
985 Constant res = c.TryReduce (ec, type, c.Location);
987 c.Error_ValueCannotBeConverted (ec, loc, type, true);
991 if (!Convert.ImplicitStandardConversionExists (c, type))
992 ec.Report.Warning (469, 2, loc,
993 "The `goto case' value is not implicitly convertible to type `{0}'",
994 TypeManager.CSharpName (type));
996 object val = res.GetValue ();
998 val = SwitchLabel.NullStringCase;
1000 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1001 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1002 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1009 protected override void DoEmit (EmitContext ec)
1011 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1014 protected override void CloneTo (CloneContext clonectx, Statement t)
1016 GotoCase target = (GotoCase) t;
1018 target.expr = expr.Clone (clonectx);
1022 public class Throw : Statement {
1025 public Throw (Expression expr, Location l)
1031 public override bool Resolve (BlockContext ec)
1034 ec.CurrentBranching.CurrentUsageVector.Goto ();
1035 return ec.CurrentBranching.CheckRethrow (loc);
1038 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1039 ec.CurrentBranching.CurrentUsageVector.Goto ();
1044 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1045 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1047 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1052 protected override void DoEmit (EmitContext ec)
1055 ec.Emit (OpCodes.Rethrow);
1059 ec.Emit (OpCodes.Throw);
1063 protected override void CloneTo (CloneContext clonectx, Statement t)
1065 Throw target = (Throw) t;
1068 target.expr = expr.Clone (clonectx);
1072 public class Break : Statement {
1074 public Break (Location l)
1079 bool unwind_protect;
1081 public override bool Resolve (BlockContext ec)
1083 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1084 ec.CurrentBranching.CurrentUsageVector.Goto ();
1088 protected override void DoEmit (EmitContext ec)
1090 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1093 protected override void CloneTo (CloneContext clonectx, Statement t)
1099 public class Continue : Statement {
1101 public Continue (Location l)
1106 bool unwind_protect;
1108 public override bool Resolve (BlockContext ec)
1110 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1111 ec.CurrentBranching.CurrentUsageVector.Goto ();
1115 protected override void DoEmit (EmitContext ec)
1117 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1120 protected override void CloneTo (CloneContext clonectx, Statement t)
1126 public interface ILocalVariable
1128 void Emit (EmitContext ec);
1129 void EmitAssign (EmitContext ec);
1130 void EmitAddressOf (EmitContext ec);
1133 public interface IKnownVariable {
1134 Block Block { get; }
1135 Location Location { get; }
1139 // The information about a user-perceived local variable
1141 public class LocalInfo : IKnownVariable, ILocalVariable {
1142 public readonly FullNamedExpression Type;
1144 public TypeSpec VariableType;
1145 public readonly string Name;
1146 public readonly Location Location;
1147 public readonly Block Block;
1149 public VariableInfo VariableInfo;
1150 HoistedVariable hoisted_variant;
1159 CompilerGenerated = 64,
1163 public enum ReadOnlyContext: byte {
1170 ReadOnlyContext ro_context;
1171 LocalBuilder builder;
1173 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1181 public LocalInfo (TypeContainer ds, Block block, Location l)
1183 VariableType = ds.IsGeneric ? ds.CurrentType : ds.Definition;
1188 public void ResolveVariable (EmitContext ec)
1190 if (HoistedVariant != null)
1193 if (builder == null) {
1194 builder = ec.DeclareLocal (VariableType, Pinned);
1198 public void Emit (EmitContext ec)
1200 ec.Emit (OpCodes.Ldloc, builder);
1203 public void EmitAssign (EmitContext ec)
1205 ec.Emit (OpCodes.Stloc, builder);
1208 public void EmitAddressOf (EmitContext ec)
1210 ec.Emit (OpCodes.Ldloca, builder);
1213 public void EmitSymbolInfo (EmitContext ec)
1215 if (builder != null)
1216 ec.DefineLocalVariable (Name, builder);
1220 // Hoisted local variable variant
1222 public HoistedVariable HoistedVariant {
1224 return hoisted_variant;
1227 hoisted_variant = value;
1231 public bool IsThisAssigned (BlockContext ec, Block block)
1233 if (VariableInfo == null)
1234 throw new Exception ();
1236 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1239 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1242 public bool IsAssigned (BlockContext ec)
1244 if (VariableInfo == null)
1245 throw new Exception ();
1247 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1250 public bool Resolve (ResolveContext ec)
1252 if (VariableType != null)
1255 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1259 VariableType = texpr.Type;
1261 if (VariableType.IsStatic) {
1262 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1266 if (VariableType.IsPointer && !ec.IsUnsafe)
1267 Expression.UnsafeError (ec, Location);
1272 public bool IsConstant {
1273 get { return (flags & Flags.IsConstant) != 0; }
1274 set { flags |= Flags.IsConstant; }
1277 public bool AddressTaken {
1278 get { return (flags & Flags.AddressTaken) != 0; }
1279 set { flags |= Flags.AddressTaken; }
1282 public bool CompilerGenerated {
1283 get { return (flags & Flags.CompilerGenerated) != 0; }
1284 set { flags |= Flags.CompilerGenerated; }
1287 public override string ToString ()
1289 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1290 Name, Type, VariableInfo, Location);
1294 get { return (flags & Flags.Used) != 0; }
1295 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1298 public bool ReadOnly {
1299 get { return (flags & Flags.ReadOnly) != 0; }
1302 public void SetReadOnlyContext (ReadOnlyContext context)
1304 flags |= Flags.ReadOnly;
1305 ro_context = context;
1308 public string GetReadOnlyContext ()
1311 throw new InternalErrorException ("Variable is not readonly");
1313 switch (ro_context) {
1314 case ReadOnlyContext.Fixed:
1315 return "fixed variable";
1316 case ReadOnlyContext.Foreach:
1317 return "foreach iteration variable";
1318 case ReadOnlyContext.Using:
1319 return "using variable";
1321 throw new NotImplementedException ();
1325 // Whether the variable is pinned, if Pinned the variable has been
1326 // allocated in a pinned slot with DeclareLocal.
1328 public bool Pinned {
1329 get { return (flags & Flags.Pinned) != 0; }
1330 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1333 public bool IsThis {
1334 get { return (flags & Flags.IsThis) != 0; }
1335 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1338 Block IKnownVariable.Block {
1339 get { return Block; }
1342 Location IKnownVariable.Location {
1343 get { return Location; }
1346 public LocalInfo Clone (CloneContext clonectx)
1349 // Variables in anonymous block are not resolved yet
1351 if (VariableType == null)
1352 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1355 // Variables in method block are resolved
1357 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1358 li.VariableType = VariableType;
1364 /// Block represents a C# block.
1368 /// This class is used in a number of places: either to represent
1369 /// explicit blocks that the programmer places or implicit blocks.
1371 /// Implicit blocks are used as labels or to introduce variable
1374 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1375 /// they contain extra information that is not necessary on normal blocks.
1377 public class Block : Statement {
1378 public Block Parent;
1379 public Location StartLocation;
1380 public Location EndLocation = Location.Null;
1382 public ExplicitBlock Explicit;
1383 public ToplevelBlock Toplevel; // TODO: Use Explicit
1390 VariablesInitialized = 4,
1394 HasCapturedVariable = 64,
1395 HasCapturedThis = 1 << 7,
1396 IsExpressionTree = 1 << 8
1399 protected Flags flags;
1401 public bool Unchecked {
1402 get { return (flags & Flags.Unchecked) != 0; }
1403 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1406 public bool Unsafe {
1407 get { return (flags & Flags.Unsafe) != 0; }
1408 set { flags |= Flags.Unsafe; }
1412 // The statements in this block
1414 protected List<Statement> statements;
1417 // An array of Blocks. We keep track of children just
1418 // to generate the local variable declarations.
1420 // Statements and child statements are handled through the
1423 List<Block> children;
1426 // Labels. (label, block) pairs.
1428 protected Dictionary<string, LabeledStatement> labels;
1431 // Keeps track of (name, type) pairs
1433 Dictionary<string, LocalInfo> variables;
1436 // Keeps track of constants
1437 Dictionary<string, Expression> constants;
1440 // Temporary variables.
1442 List<LocalInfo> temporary_variables;
1445 // If this is a switch section, the enclosing switch block.
1449 protected List<Statement> scope_initializers;
1451 List<ToplevelBlock> anonymous_children;
1453 int? resolving_init_idx;
1455 protected static int id;
1459 int assignable_slots;
1460 bool unreachable_shown;
1463 public Block (Block parent)
1464 : this (parent, (Flags) 0, Location.Null, Location.Null)
1467 public Block (Block parent, Flags flags)
1468 : this (parent, flags, Location.Null, Location.Null)
1471 public Block (Block parent, Location start, Location end)
1472 : this (parent, (Flags) 0, start, end)
1476 // Useful when TopLevel block is downgraded to normal block
1478 public Block (ToplevelBlock parent, ToplevelBlock source)
1479 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1481 statements = source.statements;
1482 children = source.children;
1483 labels = source.labels;
1484 variables = source.variables;
1485 constants = source.constants;
1486 switch_block = source.switch_block;
1489 public Block (Block parent, Flags flags, Location start, Location end)
1491 if (parent != null) {
1492 parent.AddChild (this);
1494 // the appropriate constructors will fixup these fields
1495 Toplevel = parent.Toplevel;
1496 Explicit = parent.Explicit;
1499 this.Parent = parent;
1501 this.StartLocation = start;
1502 this.EndLocation = end;
1505 statements = new List<Statement> (4);
1511 get { return this_id; }
1514 public IDictionary<string, LocalInfo> Variables {
1516 if (variables == null)
1517 variables = new Dictionary<string, LocalInfo> ();
1524 public Block CreateSwitchBlock (Location start)
1526 // FIXME: should this be implicit?
1527 Block new_block = new ExplicitBlock (this, start, start);
1528 new_block.switch_block = this;
1532 void AddChild (Block b)
1534 if (children == null)
1535 children = new List<Block> (1);
1540 public void SetEndLocation (Location loc)
1545 protected void Error_158 (string name, Location loc)
1547 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1548 "by the same name in a contained scope", name);
1552 /// Adds a label to the current block.
1556 /// false if the name already exists in this block. true
1560 public bool AddLabel (LabeledStatement target)
1562 if (switch_block != null)
1563 return switch_block.AddLabel (target);
1565 string name = target.Name;
1568 while (cur != null) {
1569 LabeledStatement s = cur.DoLookupLabel (name);
1571 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1572 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1576 if (this == Explicit)
1582 while (cur != null) {
1583 if (cur.DoLookupLabel (name) != null) {
1584 Error_158 (name, target.loc);
1588 if (children != null) {
1589 foreach (Block b in children) {
1590 LabeledStatement s = b.DoLookupLabel (name);
1594 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1595 Error_158 (name, target.loc);
1603 Toplevel.CheckError158 (name, target.loc);
1606 labels = new Dictionary<string, LabeledStatement> ();
1608 labels.Add (name, target);
1612 public LabeledStatement LookupLabel (string name)
1614 LabeledStatement s = DoLookupLabel (name);
1618 if (children == null)
1621 foreach (Block child in children) {
1622 if (Explicit != child.Explicit)
1625 s = child.LookupLabel (name);
1633 LabeledStatement DoLookupLabel (string name)
1635 if (switch_block != null)
1636 return switch_block.LookupLabel (name);
1639 if (labels.ContainsKey (name))
1640 return labels [name];
1645 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1648 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1649 while (kvi == null) {
1650 b = b.Explicit.Parent;
1653 kvi = b.Explicit.GetKnownVariable (name);
1659 // Is kvi.Block nested inside 'b'
1660 if (b.Explicit != kvi.Block.Explicit) {
1662 // If a variable by the same name it defined in a nested block of this
1663 // block, we violate the invariant meaning in a block.
1666 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1667 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1672 // It's ok if the definition is in a nested subblock of b, but not
1673 // nested inside this block -- a definition in a sibling block
1674 // should not affect us.
1680 // Block 'b' and kvi.Block are the same textual block.
1681 // However, different variables are extant.
1683 // Check if the variable is in scope in both blocks. We use
1684 // an indirect check that depends on AddVariable doing its
1685 // part in maintaining the invariant-meaning-in-block property.
1687 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1690 if (this is ToplevelBlock) {
1691 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1692 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1697 // Even though we detected the error when the name is used, we
1698 // treat it as if the variable declaration was in error.
1700 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1701 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1705 protected bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1707 LocalInfo vi = GetLocalInfo (name);
1709 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1710 if (Explicit == vi.Block.Explicit) {
1711 Error_AlreadyDeclared (l, name, null);
1713 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1714 "parent or current" : "parent");
1719 if (block != null) {
1720 var tblock = block.CheckParameterNameConflict (name);
1721 if (tblock != null) {
1722 if (block == tblock && block is Linq.QueryBlock)
1723 Error_AlreadyDeclared (loc, name);
1725 Error_AlreadyDeclared (loc, name, "parent or current");
1734 public LocalInfo AddVariable (Expression type, string name, Location l)
1736 if (!CheckParentConflictName (Toplevel, name, l))
1739 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1741 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1742 Error_AlreadyDeclared (l, name, "child");
1746 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1749 if ((flags & Flags.VariablesInitialized) != 0)
1750 throw new InternalErrorException ("block has already been resolved");
1755 protected virtual void AddVariable (LocalInfo li)
1757 Variables.Add (li.Name, li);
1758 Explicit.AddKnownVariable (li.Name, li);
1761 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1763 if (reason == null) {
1764 Error_AlreadyDeclared (loc, var);
1768 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1769 "in this scope because it would give a different meaning " +
1770 "to `{0}', which is already used in a `{1}' scope " +
1771 "to denote something else", var, reason);
1774 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1776 Toplevel.Report.Error (128, loc,
1777 "A local variable named `{0}' is already defined in this scope", name);
1780 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1782 Toplevel.Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1786 public bool AddConstant (Expression type, string name, Expression value, Location l)
1788 if (AddVariable (type, name, l) == null)
1791 if (constants == null)
1792 constants = new Dictionary<string, Expression> ();
1794 constants.Add (name, value);
1796 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1801 static int next_temp_id = 0;
1803 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1805 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1807 if (temporary_variables == null)
1808 temporary_variables = new List<LocalInfo> ();
1810 int id = ++next_temp_id;
1811 string name = "$s_" + id.ToString ();
1813 LocalInfo li = new LocalInfo (te, name, this, loc);
1814 li.CompilerGenerated = true;
1815 temporary_variables.Add (li);
1819 public LocalInfo GetLocalInfo (string name)
1822 for (Block b = this; b != null; b = b.Parent) {
1823 if (b.variables != null && b.variables.TryGetValue (name, out ret)) {
1831 public Expression GetVariableType (string name)
1833 LocalInfo vi = GetLocalInfo (name);
1834 return vi == null ? null : vi.Type;
1837 public Expression GetConstantExpression (string name)
1840 for (Block b = this; b != null; b = b.Parent) {
1841 if (b.constants != null) {
1842 if (b.constants.TryGetValue (name, out ret))
1850 // It should be used by expressions which require to
1851 // register a statement during resolve process.
1853 public void AddScopeStatement (Statement s)
1855 if (scope_initializers == null)
1856 scope_initializers = new List<Statement> ();
1859 // Simple recursive helper, when resolve scope initializer another
1860 // new scope initializer can be added, this ensures it's initialized
1861 // before existing one. For now this can happen with expression trees
1862 // in base ctor initializer only
1864 if (resolving_init_idx.HasValue) {
1865 scope_initializers.Insert (resolving_init_idx.Value, s);
1866 ++resolving_init_idx;
1868 scope_initializers.Add (s);
1872 public void AddStatement (Statement s)
1875 flags |= Flags.BlockUsed;
1879 get { return (flags & Flags.BlockUsed) != 0; }
1884 flags |= Flags.BlockUsed;
1887 public bool HasRet {
1888 get { return (flags & Flags.HasRet) != 0; }
1891 public int AssignableSlots {
1894 // if ((flags & Flags.VariablesInitialized) == 0)
1895 // throw new Exception ("Variables have not been initialized yet");
1896 return assignable_slots;
1900 public IList<ToplevelBlock> AnonymousChildren {
1901 get { return anonymous_children; }
1904 public void AddAnonymousChild (ToplevelBlock b)
1906 if (anonymous_children == null)
1907 anonymous_children = new List<ToplevelBlock> ();
1909 anonymous_children.Add (b);
1912 void DoResolveConstants (BlockContext ec)
1914 if (constants == null)
1917 if (variables == null)
1918 throw new InternalErrorException ("cannot happen");
1920 foreach (var de in variables) {
1921 string name = de.Key;
1922 LocalInfo vi = de.Value;
1923 TypeSpec variable_type = vi.VariableType;
1925 if (variable_type == null) {
1926 if (vi.Type is VarExpr)
1927 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1933 if (!constants.TryGetValue (name, out cv))
1936 // Don't let 'const int Foo = Foo;' succeed.
1937 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1938 // which in turn causes the 'must be constant' error to be triggered.
1939 constants.Remove (name);
1941 if (!variable_type.IsConstantCompatible) {
1942 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1946 ec.CurrentBlock = this;
1948 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1949 e = cv.Resolve (ec);
1954 Constant ce = e as Constant;
1956 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1960 e = ce.ConvertImplicitly (ec, variable_type);
1962 if (TypeManager.IsReferenceType (variable_type))
1963 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1965 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1969 constants.Add (name, e);
1970 vi.IsConstant = true;
1974 protected void ResolveMeta (BlockContext ec, int offset)
1976 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1978 // If some parent block was unsafe, we remain unsafe even if this block
1979 // isn't explicitly marked as such.
1980 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
1981 flags |= Flags.VariablesInitialized;
1983 if (variables != null) {
1984 foreach (LocalInfo li in variables.Values) {
1985 if (!li.Resolve (ec))
1987 li.VariableInfo = new VariableInfo (li, offset);
1988 offset += li.VariableInfo.Length;
1991 assignable_slots = offset;
1993 DoResolveConstants (ec);
1995 if (children == null)
1997 foreach (Block b in children)
1998 b.ResolveMeta (ec, offset);
2003 // Emits the local variable declarations for a block
2005 public virtual void EmitMeta (EmitContext ec)
2007 if (variables != null){
2008 foreach (LocalInfo vi in variables.Values)
2009 vi.ResolveVariable (ec);
2012 if (temporary_variables != null) {
2013 for (int i = 0; i < temporary_variables.Count; i++)
2014 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2017 if (children != null) {
2018 for (int i = 0; i < children.Count; i++)
2019 ((Block)children[i]).EmitMeta(ec);
2023 void UsageWarning (BlockContext ec)
2025 if (variables == null || ec.Report.WarningLevel < 3)
2028 foreach (var de in variables) {
2029 LocalInfo vi = de.Value;
2032 string name = de.Key;
2034 // vi.VariableInfo can be null for 'catch' variables
2035 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2036 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2038 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2043 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2047 // Some statements are wrapped by a Block. Since
2048 // others' internal could be changed, here I treat
2049 // them as possibly wrapped by Block equally.
2050 Block b = s as Block;
2051 if (b != null && b.statements.Count == 1)
2052 s = (Statement) b.statements [0];
2055 body = ((Lock) s).Statement;
2057 body = ((For) s).Statement;
2058 else if (s is Foreach)
2059 body = ((Foreach) s).Statement;
2060 else if (s is While)
2061 body = ((While) s).Statement;
2062 else if (s is Fixed)
2063 body = ((Fixed) s).Statement;
2064 else if (s is Using)
2065 body = ((Using) s).EmbeddedStatement;
2066 else if (s is UsingTemporary)
2067 body = ((UsingTemporary) s).Statement;
2071 if (body == null || body is EmptyStatement)
2072 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2075 public override bool Resolve (BlockContext ec)
2077 Block prev_block = ec.CurrentBlock;
2080 int errors = ec.Report.Errors;
2082 ec.CurrentBlock = this;
2083 ec.StartFlowBranching (this);
2085 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2088 // Compiler generated scope statements
2090 if (scope_initializers != null) {
2091 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2092 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2095 resolving_init_idx = null;
2099 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2100 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2101 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2102 // responsible for handling the situation.
2104 int statement_count = statements.Count;
2105 for (int ix = 0; ix < statement_count; ix++){
2106 Statement s = statements [ix];
2107 // Check possible empty statement (CS0642)
2108 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2109 statements [ix + 1] is ExplicitBlock)
2110 CheckPossibleMistakenEmptyStatement (ec, s);
2113 // Warn if we detect unreachable code.
2116 if (s is EmptyStatement)
2119 if (!unreachable_shown && !(s is LabeledStatement)) {
2120 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2121 unreachable_shown = true;
2124 Block c_block = s as Block;
2125 if (c_block != null)
2126 c_block.unreachable = c_block.unreachable_shown = true;
2130 // Note that we're not using ResolveUnreachable() for unreachable
2131 // statements here. ResolveUnreachable() creates a temporary
2132 // flow branching and kills it afterwards. This leads to problems
2133 // if you have two unreachable statements where the first one
2134 // assigns a variable and the second one tries to access it.
2137 if (!s.Resolve (ec)) {
2139 if (ec.IsInProbingMode)
2142 statements [ix] = new EmptyStatement (s.loc);
2146 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2147 statements [ix] = new EmptyStatement (s.loc);
2149 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2150 if (unreachable && s is LabeledStatement)
2151 throw new InternalErrorException ("should not happen");
2154 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2155 ec.CurrentBranching, statement_count);
2157 while (ec.CurrentBranching is FlowBranchingLabeled)
2158 ec.EndFlowBranching ();
2160 bool flow_unreachable = ec.EndFlowBranching ();
2162 ec.CurrentBlock = prev_block;
2164 if (flow_unreachable)
2165 flags |= Flags.HasRet;
2167 // If we're a non-static `struct' constructor which doesn't have an
2168 // initializer, then we must initialize all of the struct's fields.
2169 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2172 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2173 foreach (LabeledStatement label in labels.Values)
2174 if (!label.HasBeenReferenced)
2175 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2178 if (ok && errors == ec.Report.Errors)
2184 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2186 unreachable_shown = true;
2190 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2192 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2193 bool ok = Resolve (ec);
2194 ec.KillFlowBranching ();
2199 protected override void DoEmit (EmitContext ec)
2201 for (int ix = 0; ix < statements.Count; ix++){
2202 statements [ix].Emit (ec);
2206 public override void Emit (EmitContext ec)
2208 if (scope_initializers != null)
2209 EmitScopeInitializers (ec);
2211 ec.Mark (StartLocation);
2214 if (SymbolWriter.HasSymbolWriter)
2215 EmitSymbolInfo (ec);
2218 protected void EmitScopeInitializers (EmitContext ec)
2220 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2222 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2223 foreach (Statement s in scope_initializers)
2227 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2230 protected virtual void EmitSymbolInfo (EmitContext ec)
2232 if (variables != null) {
2233 foreach (LocalInfo vi in variables.Values) {
2234 vi.EmitSymbolInfo (ec);
2239 public override string ToString ()
2241 return String.Format ("{0} ({1}:{2})", GetType (), this_id, StartLocation);
2244 protected override void CloneTo (CloneContext clonectx, Statement t)
2246 Block target = (Block) t;
2248 clonectx.AddBlockMap (this, target);
2250 target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2251 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2253 target.Parent = clonectx.RemapBlockCopy (Parent);
2255 if (variables != null){
2256 target.variables = new Dictionary<string, LocalInfo> ();
2258 foreach (var de in variables){
2259 LocalInfo newlocal = de.Value.Clone (clonectx);
2260 target.variables [de.Key] = newlocal;
2261 clonectx.AddVariableMap (de.Value, newlocal);
2265 target.statements = new List<Statement> (statements.Count);
2266 foreach (Statement s in statements)
2267 target.statements.Add (s.Clone (clonectx));
2269 if (target.children != null){
2270 target.children = new List<Block> (children.Count);
2271 foreach (Block b in children){
2272 target.children.Add (clonectx.LookupBlock (b));
2277 // TODO: labels, switch_block, constants (?), anonymous_children
2282 public class ExplicitBlock : Block
2284 Dictionary<string, IKnownVariable> known_variables;
2285 protected AnonymousMethodStorey am_storey;
2287 public ExplicitBlock (Block parent, Location start, Location end)
2288 : this (parent, (Flags) 0, start, end)
2292 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2293 : base (parent, flags, start, end)
2295 this.Explicit = this;
2299 // Marks a variable with name @name as being used in this or a child block.
2300 // If a variable name has been used in a child block, it's illegal to
2301 // declare a variable with the same name in the current block.
2303 internal void AddKnownVariable (string name, IKnownVariable info)
2305 if (known_variables == null)
2306 known_variables = new Dictionary<string, IKnownVariable> ();
2308 known_variables [name] = info;
2311 Parent.Explicit.AddKnownVariable (name, info);
2314 public AnonymousMethodStorey AnonymousMethodStorey {
2315 get { return am_storey; }
2319 // Creates anonymous method storey in current block
2321 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2324 // When referencing a variable in iterator storey from children anonymous method
2326 if (Toplevel.am_storey is IteratorStorey) {
2327 return Toplevel.am_storey;
2331 // An iterator has only 1 storey block
2333 if (ec.CurrentIterator != null)
2334 return ec.CurrentIterator.Storey;
2336 if (am_storey == null) {
2337 MemberBase mc = ec.MemberContext as MemberBase;
2338 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2341 // Creates anonymous method storey for this block
2343 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2349 public override void Emit (EmitContext ec)
2351 if (am_storey != null)
2352 am_storey.EmitStoreyInstantiation (ec);
2354 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2355 if (emit_debug_info)
2360 if (emit_debug_info)
2364 public override void EmitMeta (EmitContext ec)
2367 // Creates anonymous method storey
2369 if (am_storey != null) {
2370 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2372 // Creates parent storey reference when hoisted this is accessible
2374 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2375 ExplicitBlock parent = Toplevel.Parent.Explicit;
2378 // Hoisted this exists in top-level parent storey only
2380 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2381 parent = parent.Parent.Explicit;
2383 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2386 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2388 // TODO MemberCache: Review
2389 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2392 am_storey.CreateType ();
2393 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2394 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2396 am_storey.DefineType ();
2397 am_storey.ResolveTypeParameters ();
2398 am_storey.Define ();
2399 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2401 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2402 if (ref_blocks != null) {
2403 foreach (ExplicitBlock ref_block in ref_blocks) {
2404 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2405 if (b.am_storey != null) {
2406 b.am_storey.AddParentStoreyReference (ec, am_storey);
2408 // Stop propagation inside same top block
2409 if (b.Toplevel == Toplevel)
2414 b.HasCapturedVariable = true;
2423 public IKnownVariable GetKnownVariable (string name)
2425 if (known_variables == null)
2429 known_variables.TryGetValue (name, out kw);
2433 public bool HasCapturedThis
2435 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2436 get { return (flags & Flags.HasCapturedThis) != 0; }
2439 public bool HasCapturedVariable
2441 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2442 get { return (flags & Flags.HasCapturedVariable) != 0; }
2445 protected override void CloneTo (CloneContext clonectx, Statement t)
2447 ExplicitBlock target = (ExplicitBlock) t;
2448 target.known_variables = null;
2449 base.CloneTo (clonectx, t);
2453 public class ToplevelParameterInfo : IKnownVariable {
2454 public readonly ToplevelBlock Block;
2455 public readonly int Index;
2456 public VariableInfo VariableInfo;
2458 Block IKnownVariable.Block {
2459 get { return Block; }
2461 public Parameter Parameter {
2462 get { return Block.Parameters [Index]; }
2465 public TypeSpec ParameterType {
2466 get { return Block.Parameters.Types [Index]; }
2469 public Location Location {
2470 get { return Parameter.Location; }
2473 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2481 // A toplevel block contains extra information, the split is done
2482 // only to separate information that would otherwise bloat the more
2483 // lightweight Block.
2485 // In particular, this was introduced when the support for Anonymous
2486 // Methods was implemented.
2488 public class ToplevelBlock : ExplicitBlock
2491 // Block is converted to an expression
2493 sealed class BlockScopeExpression : Expression
2496 readonly ToplevelBlock block;
2498 public BlockScopeExpression (Expression child, ToplevelBlock block)
2504 public override Expression CreateExpressionTree (ResolveContext ec)
2506 throw new NotSupportedException ();
2509 protected override Expression DoResolve (ResolveContext ec)
2514 child = child.Resolve (ec);
2518 eclass = child.eclass;
2523 public override void Emit (EmitContext ec)
2525 block.EmitMeta (ec);
2526 block.EmitScopeInitializers (ec);
2531 protected ParametersCompiled parameters;
2532 protected ToplevelParameterInfo[] parameter_info;
2533 LocalInfo this_variable;
2536 CompilerContext compiler;
2538 public HoistedVariable HoistedThisVariable;
2540 public bool Resolved {
2547 // The parameters for the block.
2549 public ParametersCompiled Parameters {
2550 get { return parameters; }
2553 public Report Report {
2554 get { return compiler.Report; }
2557 public ToplevelBlock Container {
2558 get { return Parent == null ? null : Parent.Toplevel; }
2561 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2562 this (ctx, parent, (Flags) 0, parameters, start)
2566 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2567 this (ctx, null, (Flags) 0, parameters, start)
2571 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2572 this (ctx, null, flags, parameters, start)
2576 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2577 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2578 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2579 base (null, flags, start, Location.Null)
2581 this.compiler = ctx;
2582 this.Toplevel = this;
2584 this.parameters = parameters;
2585 this.Parent = parent;
2587 parent.AddAnonymousChild (this);
2589 if (!this.parameters.IsEmpty)
2590 ProcessParameters ();
2593 public ToplevelBlock (CompilerContext ctx, Location loc)
2594 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2598 protected override void CloneTo (CloneContext clonectx, Statement t)
2600 ToplevelBlock target = (ToplevelBlock) t;
2601 base.CloneTo (clonectx, t);
2603 if (parameters.Count != 0) {
2604 target.parameter_info = new ToplevelParameterInfo[parameters.Count];
2605 for (int i = 0; i < parameters.Count; ++i)
2606 target.parameter_info[i] = new ToplevelParameterInfo (target, i);
2610 public bool CheckError158 (string name, Location loc)
2612 if (AnonymousChildren != null) {
2613 foreach (ToplevelBlock child in AnonymousChildren) {
2614 if (!child.CheckError158 (name, loc))
2619 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2620 if (!c.DoCheckError158 (name, loc))
2627 void ProcessParameters ()
2629 int n = parameters.Count;
2630 parameter_info = new ToplevelParameterInfo [n];
2631 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2632 for (int i = 0; i < n; ++i) {
2633 parameter_info [i] = new ToplevelParameterInfo (this, i);
2635 Parameter p = parameters [i];
2639 string name = p.Name;
2640 if (CheckParentConflictName (top_parent, name, loc))
2641 AddKnownVariable (name, parameter_info [i]);
2644 // mark this block as "used" so that we create local declarations in a sub-block
2645 // FIXME: This appears to uncover a lot of bugs
2649 bool DoCheckError158 (string name, Location loc)
2651 LabeledStatement s = LookupLabel (name);
2653 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2654 Error_158 (name, loc);
2661 public override Expression CreateExpressionTree (ResolveContext ec)
2663 if (statements.Count == 1) {
2664 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2665 if (scope_initializers != null)
2666 expr = new BlockScopeExpression (expr, this);
2671 return base.CreateExpressionTree (ec);
2675 // Reformats this block to be top-level iterator block
2677 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2681 // Creates block with original statements
2682 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2684 source.statements = new List<Statement> (1);
2685 source.AddStatement (new Return (iterator, iterator.Location));
2686 source.IsIterator = false;
2688 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2689 source.am_storey = iterator_storey;
2690 return iterator_storey;
2694 // Returns a parameter reference expression for the given name,
2695 // or null if there is no such parameter
2697 public Expression GetParameterReference (string name, Location loc)
2699 for (ToplevelBlock t = this; t != null; t = t.Container) {
2700 if (t.parameters.IsEmpty)
2703 Expression expr = t.GetParameterReferenceExpression (name, loc);
2711 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2713 int idx = parameters.GetParameterIndexByName (name);
2715 null : new ParameterReference (parameter_info [idx], loc);
2718 public ToplevelBlock CheckParameterNameConflict (string name)
2720 for (ToplevelBlock t = this; t != null; t = t.Container) {
2721 if (t.HasParameterWithName (name))
2728 protected virtual bool HasParameterWithName (string name)
2730 return parameters.GetParameterIndexByName (name) >= 0;
2734 // Returns the "this" instance variable of this block.
2735 // See AddThisVariable() for more information.
2737 public LocalInfo ThisVariable {
2738 get { return this_variable; }
2742 // This is used by non-static `struct' constructors which do not have an
2743 // initializer - in this case, the constructor must initialize all of the
2744 // struct's fields. To do this, we add a "this" variable and use the flow
2745 // analysis code to ensure that it's been fully initialized before control
2746 // leaves the constructor.
2748 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2750 if (this_variable == null) {
2751 this_variable = new LocalInfo (ds, this, l);
2752 this_variable.Used = true;
2753 this_variable.IsThis = true;
2755 Variables.Add ("this", this_variable);
2758 return this_variable;
2761 public bool IsIterator {
2762 get { return (flags & Flags.IsIterator) != 0; }
2763 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2767 // Block has been converted to expression tree
2769 public bool IsExpressionTree {
2770 get { return (flags & Flags.IsExpressionTree) != 0; }
2773 public bool IsThisAssigned (BlockContext ec)
2775 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2778 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2785 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2786 flags |= Flags.IsExpressionTree;
2789 if (!ResolveMeta (rc, ip))
2792 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2793 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2798 unreachable = top_level.End ();
2800 } catch (Exception e) {
2801 if (e is CompletionResult || rc.Report.IsDisabled)
2804 if (rc.CurrentBlock != null) {
2805 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2807 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2810 if (Report.DebugFlags > 0)
2814 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2815 if (rc.CurrentAnonymousMethod == null) {
2816 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2818 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2819 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2820 rc.CurrentAnonymousMethod.GetSignatureForError ());
2828 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2830 int errors = ec.Report.Errors;
2831 int orig_count = parameters.Count;
2836 // Assert: orig_count != parameter.Count => orig_count == 0
2837 if (orig_count != 0 && orig_count != parameters.Count)
2838 throw new InternalErrorException ("parameter information mismatch");
2840 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2842 for (int i = 0; i < orig_count; ++i) {
2843 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2845 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2848 VariableInfo vi = new VariableInfo (ip, i, offset);
2849 parameter_info [i].VariableInfo = vi;
2850 offset += vi.Length;
2853 ResolveMeta (ec, offset);
2855 return ec.Report.Errors == errors;
2859 // Check whether all `out' parameters have been assigned.
2861 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2863 if (vector.IsUnreachable)
2866 int n = parameter_info == null ? 0 : parameter_info.Length;
2868 for (int i = 0; i < n; i++) {
2869 VariableInfo var = parameter_info [i].VariableInfo;
2874 if (vector.IsAssigned (var, false))
2877 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2882 public override void Emit (EmitContext ec)
2884 if (Report.Errors > 0)
2892 if (ec.HasReturnLabel)
2893 ec.ReturnLabel = ec.DefineLabel ();
2897 ec.Mark (EndLocation);
2899 if (ec.HasReturnLabel)
2900 ec.MarkLabel (ec.ReturnLabel);
2902 if (ec.return_value != null) {
2903 ec.Emit (OpCodes.Ldloc, ec.return_value);
2904 ec.Emit (OpCodes.Ret);
2907 // If `HasReturnLabel' is set, then we already emitted a
2908 // jump to the end of the method, so we must emit a `ret'
2911 // Unfortunately, System.Reflection.Emit automatically emits
2912 // a leave to the end of a finally block. This is a problem
2913 // if no code is following the try/finally block since we may
2914 // jump to a point after the end of the method.
2915 // As a workaround, we're always creating a return label in
2919 if (ec.HasReturnLabel || !unreachable) {
2920 if (ec.ReturnType != TypeManager.void_type)
2921 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2922 ec.Emit (OpCodes.Ret);
2927 } catch (Exception e){
2928 Console.WriteLine ("Exception caught by the compiler while emitting:");
2929 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2931 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2937 public override void EmitMeta (EmitContext ec)
2939 // Avoid declaring an IL variable for this_variable since it is not accessed
2940 // from the generated IL
2941 if (this_variable != null)
2942 Variables.Remove ("this");
2946 protected override void EmitSymbolInfo (EmitContext ec)
2948 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2949 if ((ae != null) && (ae.Storey != null))
2950 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2952 base.EmitSymbolInfo (ec);
2956 public class SwitchLabel {
2963 Label il_label_code;
2964 bool il_label_code_set;
2966 public static readonly object NullStringCase = new object ();
2969 // if expr == null, then it is the default case.
2971 public SwitchLabel (Expression expr, Location l)
2977 public Expression Label {
2983 public Location Location {
2987 public object Converted {
2993 public Label GetILLabel (EmitContext ec)
2996 il_label = ec.DefineLabel ();
2997 il_label_set = true;
3002 public Label GetILLabelCode (EmitContext ec)
3004 if (!il_label_code_set){
3005 il_label_code = ec.DefineLabel ();
3006 il_label_code_set = true;
3008 return il_label_code;
3012 // Resolves the expression, reduces it to a literal if possible
3013 // and then converts it to the requested type.
3015 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3017 Expression e = label.Resolve (ec);
3022 Constant c = e as Constant;
3024 ec.Report.Error (150, loc, "A constant value is expected");
3028 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3029 converted = NullStringCase;
3033 if (allow_nullable && c.GetValue () == null) {
3034 converted = NullStringCase;
3038 c = c.ImplicitConversionRequired (ec, required_type, loc);
3042 converted = c.GetValue ();
3046 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3049 if (converted == null)
3051 else if (converted == NullStringCase)
3054 label = converted.ToString ();
3056 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3057 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3060 public SwitchLabel Clone (CloneContext clonectx)
3062 return new SwitchLabel (label.Clone (clonectx), loc);
3066 public class SwitchSection {
3067 // An array of SwitchLabels.
3068 public readonly List<SwitchLabel> Labels;
3069 public readonly Block Block;
3071 public SwitchSection (List<SwitchLabel> labels, Block block)
3077 public SwitchSection Clone (CloneContext clonectx)
3079 var cloned_labels = new List<SwitchLabel> ();
3081 foreach (SwitchLabel sl in cloned_labels)
3082 cloned_labels.Add (sl.Clone (clonectx));
3084 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3088 public class Switch : Statement {
3089 public List<SwitchSection> Sections;
3090 public Expression Expr;
3093 /// Maps constants whose type type SwitchType to their SwitchLabels.
3095 public IDictionary<object, SwitchLabel> Elements;
3098 /// The governing switch type
3100 public TypeSpec SwitchType;
3105 Label default_target;
3107 Expression new_expr;
3110 SwitchSection constant_section;
3111 SwitchSection default_section;
3113 ExpressionStatement string_dictionary;
3114 FieldExpr switch_cache_field;
3115 static int unique_counter;
3118 // Nullable Types support
3120 Nullable.Unwrap unwrap;
3122 protected bool HaveUnwrap {
3123 get { return unwrap != null; }
3127 // The types allowed to be implicitly cast from
3128 // on the governing type
3130 static TypeSpec [] allowed_types;
3132 public Switch (Expression e, List<SwitchSection> sects, Location l)
3139 public bool GotDefault {
3141 return default_section != null;
3145 public Label DefaultTarget {
3147 return default_target;
3152 // Determines the governing type for a switch. The returned
3153 // expression might be the expression from the switch, or an
3154 // expression that includes any potential conversions to the
3155 // integral types or to string.
3157 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3159 TypeSpec t = expr.Type;
3161 if (t == TypeManager.byte_type ||
3162 t == TypeManager.sbyte_type ||
3163 t == TypeManager.ushort_type ||
3164 t == TypeManager.short_type ||
3165 t == TypeManager.uint32_type ||
3166 t == TypeManager.int32_type ||
3167 t == TypeManager.uint64_type ||
3168 t == TypeManager.int64_type ||
3169 t == TypeManager.char_type ||
3170 t == TypeManager.string_type ||
3171 t == TypeManager.bool_type ||
3172 TypeManager.IsEnumType (t))
3175 if (allowed_types == null){
3176 allowed_types = new TypeSpec [] {
3177 TypeManager.sbyte_type,
3178 TypeManager.byte_type,
3179 TypeManager.short_type,
3180 TypeManager.ushort_type,
3181 TypeManager.int32_type,
3182 TypeManager.uint32_type,
3183 TypeManager.int64_type,
3184 TypeManager.uint64_type,
3185 TypeManager.char_type,
3186 TypeManager.string_type
3191 // Try to find a *user* defined implicit conversion.
3193 // If there is no implicit conversion, or if there are multiple
3194 // conversions, we have to report an error
3196 Expression converted = null;
3197 foreach (TypeSpec tt in allowed_types){
3200 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3205 // Ignore over-worked ImplicitUserConversions that do
3206 // an implicit conversion in addition to the user conversion.
3208 if (!(e is UserCast))
3211 if (converted != null){
3212 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3222 // Performs the basic sanity checks on the switch statement
3223 // (looks for duplicate keys and non-constant expressions).
3225 // It also returns a hashtable with the keys that we will later
3226 // use to compute the switch tables
3228 bool CheckSwitch (ResolveContext ec)
3231 Elements = new Dictionary<object, SwitchLabel> ();
3233 foreach (SwitchSection ss in Sections){
3234 foreach (SwitchLabel sl in ss.Labels){
3235 if (sl.Label == null){
3236 if (default_section != null){
3237 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3240 default_section = ss;
3244 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3249 object key = sl.Converted;
3250 if (key == SwitchLabel.NullStringCase)
3251 has_null_case = true;
3254 Elements.Add (key, sl);
3255 } catch (ArgumentException) {
3256 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3264 void EmitObjectInteger (EmitContext ec, object k)
3267 ec.EmitInt ((int) k);
3268 else if (k is Constant) {
3269 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3272 ec.EmitInt (unchecked ((int) (uint) k));
3275 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3277 ec.EmitInt ((int) (long) k);
3278 ec.Emit (OpCodes.Conv_I8);
3281 ec.EmitLong ((long) k);
3283 else if (k is ulong)
3285 ulong ul = (ulong) k;
3288 ec.EmitInt (unchecked ((int) ul));
3289 ec.Emit (OpCodes.Conv_U8);
3293 ec.EmitLong (unchecked ((long) ul));
3297 ec.EmitInt ((int) ((char) k));
3298 else if (k is sbyte)
3299 ec.EmitInt ((int) ((sbyte) k));
3301 ec.EmitInt ((int) ((byte) k));
3302 else if (k is short)
3303 ec.EmitInt ((int) ((short) k));
3304 else if (k is ushort)
3305 ec.EmitInt ((int) ((ushort) k));
3307 ec.EmitInt (((bool) k) ? 1 : 0);
3309 throw new Exception ("Unhandled case");
3312 // structure used to hold blocks of keys while calculating table switch
3313 class KeyBlock : IComparable
3315 public KeyBlock (long _first)
3317 first = last = _first;
3321 public List<object> element_keys;
3322 // how many items are in the bucket
3323 public int Size = 1;
3326 get { return (int) (last - first + 1); }
3328 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3330 return kb_last.last - kb_first.first + 1;
3332 public int CompareTo (object obj)
3334 KeyBlock kb = (KeyBlock) obj;
3335 int nLength = Length;
3336 int nLengthOther = kb.Length;
3337 if (nLengthOther == nLength)
3338 return (int) (kb.first - first);
3339 return nLength - nLengthOther;
3344 /// This method emits code for a lookup-based switch statement (non-string)
3345 /// Basically it groups the cases into blocks that are at least half full,
3346 /// and then spits out individual lookup opcodes for each block.
3347 /// It emits the longest blocks first, and short blocks are just
3348 /// handled with direct compares.
3350 /// <param name="ec"></param>
3351 /// <param name="val"></param>
3352 /// <returns></returns>
3353 void TableSwitchEmit (EmitContext ec, Expression val)
3355 int element_count = Elements.Count;
3356 object [] element_keys = new object [element_count];
3357 Elements.Keys.CopyTo (element_keys, 0);
3358 Array.Sort (element_keys);
3360 // initialize the block list with one element per key
3361 var key_blocks = new List<KeyBlock> (element_count);
3362 foreach (object key in element_keys)
3363 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3365 KeyBlock current_kb;
3366 // iteratively merge the blocks while they are at least half full
3367 // there's probably a really cool way to do this with a tree...
3368 while (key_blocks.Count > 1)
3370 var key_blocks_new = new List<KeyBlock> ();
3371 current_kb = (KeyBlock) key_blocks [0];
3372 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3374 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3375 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3378 current_kb.last = kb.last;
3379 current_kb.Size += kb.Size;
3383 // start a new block
3384 key_blocks_new.Add (current_kb);
3388 key_blocks_new.Add (current_kb);
3389 if (key_blocks.Count == key_blocks_new.Count)
3391 key_blocks = key_blocks_new;
3394 // initialize the key lists
3395 foreach (KeyBlock kb in key_blocks)
3396 kb.element_keys = new List<object> ();
3398 // fill the key lists
3400 if (key_blocks.Count > 0) {
3401 current_kb = (KeyBlock) key_blocks [0];
3402 foreach (object key in element_keys)
3404 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3405 System.Convert.ToInt64 (key) > current_kb.last;
3407 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3408 current_kb.element_keys.Add (key);
3412 // sort the blocks so we can tackle the largest ones first
3415 // okay now we can start...
3416 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3417 Label lbl_default = default_target;
3419 Type type_keys = null;
3420 if (element_keys.Length > 0)
3421 type_keys = element_keys [0].GetType (); // used for conversions
3423 TypeSpec compare_type;
3425 if (TypeManager.IsEnumType (SwitchType))
3426 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3428 compare_type = SwitchType;
3430 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3432 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3433 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3436 foreach (object key in kb.element_keys) {
3437 SwitchLabel sl = (SwitchLabel) Elements [key];
3438 if (key is int && (int) key == 0) {
3439 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3442 EmitObjectInteger (ec, key);
3443 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3449 // TODO: if all the keys in the block are the same and there are
3450 // no gaps/defaults then just use a range-check.
3451 if (compare_type == TypeManager.int64_type ||
3452 compare_type == TypeManager.uint64_type)
3454 // TODO: optimize constant/I4 cases
3456 // check block range (could be > 2^31)
3458 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3459 ec.Emit (OpCodes.Blt, lbl_default);
3461 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3462 ec.Emit (OpCodes.Bgt, lbl_default);
3468 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3469 ec.Emit (OpCodes.Sub);
3471 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3477 int first = (int) kb.first;
3481 ec.Emit (OpCodes.Sub);
3485 ec.EmitInt (-first);
3486 ec.Emit (OpCodes.Add);
3490 // first, build the list of labels for the switch
3492 int cJumps = kb.Length;
3493 Label [] switch_labels = new Label [cJumps];
3494 for (int iJump = 0; iJump < cJumps; iJump++)
3496 object key = kb.element_keys [iKey];
3497 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3499 SwitchLabel sl = (SwitchLabel) Elements [key];
3500 switch_labels [iJump] = sl.GetILLabel (ec);
3504 switch_labels [iJump] = lbl_default;
3506 // emit the switch opcode
3507 ec.Emit (OpCodes.Switch, switch_labels);
3510 // mark the default for this block
3512 ec.MarkLabel (lbl_default);
3515 // TODO: find the default case and emit it here,
3516 // to prevent having to do the following jump.
3517 // make sure to mark other labels in the default section
3519 // the last default just goes to the end
3520 if (element_keys.Length > 0)
3521 ec.Emit (OpCodes.Br, lbl_default);
3523 // now emit the code for the sections
3524 bool found_default = false;
3526 foreach (SwitchSection ss in Sections) {
3527 foreach (SwitchLabel sl in ss.Labels) {
3528 if (sl.Converted == SwitchLabel.NullStringCase) {
3529 ec.MarkLabel (null_target);
3530 } else if (sl.Label == null) {
3531 ec.MarkLabel (lbl_default);
3532 found_default = true;
3534 ec.MarkLabel (null_target);
3536 ec.MarkLabel (sl.GetILLabel (ec));
3537 ec.MarkLabel (sl.GetILLabelCode (ec));
3542 if (!found_default) {
3543 ec.MarkLabel (lbl_default);
3544 if (!has_null_case) {
3545 ec.MarkLabel (null_target);
3549 ec.MarkLabel (lbl_end);
3552 SwitchSection FindSection (SwitchLabel label)
3554 foreach (SwitchSection ss in Sections){
3555 foreach (SwitchLabel sl in ss.Labels){
3564 public static void Reset ()
3567 allowed_types = null;
3570 public override bool Resolve (BlockContext ec)
3572 Expr = Expr.Resolve (ec);
3576 new_expr = SwitchGoverningType (ec, Expr);
3578 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3579 unwrap = Nullable.Unwrap.Create (Expr, false);
3583 new_expr = SwitchGoverningType (ec, unwrap);
3586 if (new_expr == null){
3587 ec.Report.Error (151, loc,
3588 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3589 TypeManager.CSharpName (Expr.Type));
3594 SwitchType = new_expr.Type;
3596 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3597 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3601 if (!CheckSwitch (ec))
3605 Elements.Remove (SwitchLabel.NullStringCase);
3607 Switch old_switch = ec.Switch;
3609 ec.Switch.SwitchType = SwitchType;
3611 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3612 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3614 var constant = new_expr as Constant;
3615 if (constant != null) {
3617 object key = constant.GetValue ();
3619 if (Elements.TryGetValue (key, out label))
3620 constant_section = FindSection (label);
3622 if (constant_section == null)
3623 constant_section = default_section;
3628 foreach (SwitchSection ss in Sections){
3630 ec.CurrentBranching.CreateSibling (
3631 null, FlowBranching.SiblingType.SwitchSection);
3635 if (is_constant && (ss != constant_section)) {
3636 // If we're a constant switch, we're only emitting
3637 // one single section - mark all the others as
3639 ec.CurrentBranching.CurrentUsageVector.Goto ();
3640 if (!ss.Block.ResolveUnreachable (ec, true)) {
3644 if (!ss.Block.Resolve (ec))
3649 if (default_section == null)
3650 ec.CurrentBranching.CreateSibling (
3651 null, FlowBranching.SiblingType.SwitchSection);
3653 ec.EndFlowBranching ();
3654 ec.Switch = old_switch;
3656 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3661 if (SwitchType == TypeManager.string_type && !is_constant) {
3662 // TODO: Optimize single case, and single+default case
3663 ResolveStringSwitchMap (ec);
3669 void ResolveStringSwitchMap (ResolveContext ec)
3671 FullNamedExpression string_dictionary_type;
3672 if (TypeManager.generic_ienumerable_type != null) {
3673 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3674 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3676 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3678 new TypeExpression (TypeManager.string_type, loc),
3679 new TypeExpression (TypeManager.int32_type, loc)), loc);
3681 MemberAccess system_collections_generic = new MemberAccess (
3682 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3684 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3687 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3688 Field field = new Field (ctype, string_dictionary_type,
3689 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3690 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3691 if (!field.Define ())
3693 ctype.AddField (field);
3695 var init = new List<Expression> ();
3698 string value = null;
3699 foreach (SwitchSection section in Sections) {
3700 int last_count = init.Count;
3701 foreach (SwitchLabel sl in section.Labels) {
3702 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3705 value = (string) sl.Converted;
3706 var init_args = new List<Expression> (2);
3707 init_args.Add (new StringLiteral (value, sl.Location));
3708 init_args.Add (new IntConstant (counter, loc));
3709 init.Add (new CollectionElementInitializer (init_args, loc));
3713 // Don't add empty sections
3715 if (last_count == init.Count)
3718 Elements.Add (counter, section.Labels [0]);
3722 Arguments args = new Arguments (1);
3723 args.Add (new Argument (new IntConstant (init.Count, loc)));
3724 Expression initializer = new NewInitialize (string_dictionary_type, args,
3725 new CollectionOrObjectInitializers (init, loc), loc);
3727 switch_cache_field = new FieldExpr (field, loc);
3728 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3731 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3733 Label l_initialized = ec.DefineLabel ();
3736 // Skip initialization when value is null
3738 value.EmitBranchable (ec, null_target, false);
3741 // Check if string dictionary is initialized and initialize
3743 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3744 string_dictionary.EmitStatement (ec);
3745 ec.MarkLabel (l_initialized);
3747 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3749 ResolveContext rc = new ResolveContext (ec.MemberContext);
3751 if (TypeManager.generic_ienumerable_type != null) {
3752 Arguments get_value_args = new Arguments (2);
3753 get_value_args.Add (new Argument (value));
3754 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3755 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3756 if (get_item == null)
3760 // A value was not found, go to default case
3762 get_item.EmitBranchable (ec, default_target, false);
3764 Arguments get_value_args = new Arguments (1);
3765 get_value_args.Add (new Argument (value));
3767 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args, loc), loc).Resolve (rc);
3768 if (get_item == null)
3771 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3772 get_item_object.EmitAssign (ec, get_item, true, false);
3773 ec.Emit (OpCodes.Brfalse, default_target);
3775 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3776 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3778 get_item_int.EmitStatement (ec);
3779 get_item_object.Release (ec);
3782 TableSwitchEmit (ec, string_switch_variable);
3783 string_switch_variable.Release (ec);
3786 protected override void DoEmit (EmitContext ec)
3788 default_target = ec.DefineLabel ();
3789 null_target = ec.DefineLabel ();
3791 // Store variable for comparission purposes
3792 // TODO: Don't duplicate non-captured VariableReference
3793 LocalTemporary value;
3795 value = new LocalTemporary (SwitchType);
3796 unwrap.EmitCheck (ec);
3797 ec.Emit (OpCodes.Brfalse, null_target);
3800 } else if (!is_constant) {
3801 value = new LocalTemporary (SwitchType);
3808 // Setup the codegen context
3810 Label old_end = ec.LoopEnd;
3811 Switch old_switch = ec.Switch;
3813 ec.LoopEnd = ec.DefineLabel ();
3818 if (constant_section != null)
3819 constant_section.Block.Emit (ec);
3820 } else if (string_dictionary != null) {
3821 DoEmitStringSwitch (value, ec);
3823 TableSwitchEmit (ec, value);
3829 // Restore context state.
3830 ec.MarkLabel (ec.LoopEnd);
3833 // Restore the previous context
3835 ec.LoopEnd = old_end;
3836 ec.Switch = old_switch;
3839 protected override void CloneTo (CloneContext clonectx, Statement t)
3841 Switch target = (Switch) t;
3843 target.Expr = Expr.Clone (clonectx);
3844 target.Sections = new List<SwitchSection> ();
3845 foreach (SwitchSection ss in Sections){
3846 target.Sections.Add (ss.Clone (clonectx));
3851 // A place where execution can restart in an iterator
3852 public abstract class ResumableStatement : Statement
3855 protected Label resume_point;
3857 public Label PrepareForEmit (EmitContext ec)
3861 resume_point = ec.DefineLabel ();
3863 return resume_point;
3866 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3870 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3875 // Base class for statements that are implemented in terms of try...finally
3876 public abstract class ExceptionStatement : ResumableStatement
3880 List<ResumableStatement> resume_points;
3881 int first_resume_pc;
3883 protected abstract void EmitPreTryBody (EmitContext ec);
3884 protected abstract void EmitTryBody (EmitContext ec);
3885 protected abstract void EmitFinallyBody (EmitContext ec);
3887 protected sealed override void DoEmit (EmitContext ec)
3889 EmitPreTryBody (ec);
3891 if (resume_points != null) {
3892 ec.EmitInt ((int) Iterator.State.Running);
3893 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3896 ec.BeginExceptionBlock ();
3898 if (resume_points != null) {
3899 ec.MarkLabel (resume_point);
3901 // For normal control flow, we want to fall-through the Switch
3902 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3903 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3904 ec.EmitInt (first_resume_pc);
3905 ec.Emit (OpCodes.Sub);
3907 Label [] labels = new Label [resume_points.Count];
3908 for (int i = 0; i < resume_points.Count; ++i)
3909 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3910 ec.Emit (OpCodes.Switch, labels);
3915 ec.BeginFinallyBlock ();
3917 Label start_finally = ec.DefineLabel ();
3918 if (resume_points != null) {
3919 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3920 ec.Emit (OpCodes.Brfalse_S, start_finally);
3921 ec.Emit (OpCodes.Endfinally);
3924 ec.MarkLabel (start_finally);
3925 EmitFinallyBody (ec);
3927 ec.EndExceptionBlock ();
3930 public void SomeCodeFollows ()
3932 code_follows = true;
3935 public override bool Resolve (BlockContext ec)
3937 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3938 // So, ensure there's some IL code after this statement.
3939 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3940 ec.NeedReturnLabel ();
3942 iter = ec.CurrentIterator;
3946 public void AddResumePoint (ResumableStatement stmt, int pc)
3948 if (resume_points == null) {
3949 resume_points = new List<ResumableStatement> ();
3950 first_resume_pc = pc;
3953 if (pc != first_resume_pc + resume_points.Count)
3954 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3956 resume_points.Add (stmt);
3959 Label dispose_try_block;
3960 bool prepared_for_dispose, emitted_dispose;
3961 public override Label PrepareForDispose (EmitContext ec, Label end)
3963 if (!prepared_for_dispose) {
3964 prepared_for_dispose = true;
3965 dispose_try_block = ec.DefineLabel ();
3967 return dispose_try_block;
3970 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3972 if (emitted_dispose)
3975 emitted_dispose = true;
3977 Label end_of_try = ec.DefineLabel ();
3979 // Ensure that the only way we can get into this code is through a dispatcher
3980 if (have_dispatcher)
3981 ec.Emit (OpCodes.Br, end);
3983 ec.BeginExceptionBlock ();
3985 ec.MarkLabel (dispose_try_block);
3987 Label [] labels = null;
3988 for (int i = 0; i < resume_points.Count; ++i) {
3989 ResumableStatement s = (ResumableStatement) resume_points [i];
3990 Label ret = s.PrepareForDispose (ec, end_of_try);
3991 if (ret.Equals (end_of_try) && labels == null)
3993 if (labels == null) {
3994 labels = new Label [resume_points.Count];
3995 for (int j = 0; j < i; ++j)
3996 labels [j] = end_of_try;
4001 if (labels != null) {
4003 for (j = 1; j < labels.Length; ++j)
4004 if (!labels [0].Equals (labels [j]))
4006 bool emit_dispatcher = j < labels.Length;
4008 if (emit_dispatcher) {
4009 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4010 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4011 ec.EmitInt (first_resume_pc);
4012 ec.Emit (OpCodes.Sub);
4013 ec.Emit (OpCodes.Switch, labels);
4014 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4017 foreach (ResumableStatement s in resume_points)
4018 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4021 ec.MarkLabel (end_of_try);
4023 ec.BeginFinallyBlock ();
4025 EmitFinallyBody (ec);
4027 ec.EndExceptionBlock ();
4031 public class Lock : ExceptionStatement {
4033 public Statement Statement;
4034 TemporaryVariable temp;
4036 public Lock (Expression expr, Statement stmt, Location l)
4043 public override bool Resolve (BlockContext ec)
4045 expr = expr.Resolve (ec);
4049 if (!TypeManager.IsReferenceType (expr.Type)){
4050 ec.Report.Error (185, loc,
4051 "`{0}' is not a reference type as required by the lock statement",
4052 TypeManager.CSharpName (expr.Type));
4056 ec.StartFlowBranching (this);
4057 bool ok = Statement.Resolve (ec);
4058 ec.EndFlowBranching ();
4060 ok &= base.Resolve (ec);
4062 // Avoid creating libraries that reference the internal
4064 TypeSpec t = expr.Type;
4065 if (t == TypeManager.null_type)
4066 t = TypeManager.object_type;
4068 temp = new TemporaryVariable (t, loc);
4071 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4072 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4073 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4074 monitor_type, "Enter", loc, TypeManager.object_type);
4075 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4076 monitor_type, "Exit", loc, TypeManager.object_type);
4082 protected override void EmitPreTryBody (EmitContext ec)
4084 temp.EmitAssign (ec, expr);
4086 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4089 protected override void EmitTryBody (EmitContext ec)
4091 Statement.Emit (ec);
4094 protected override void EmitFinallyBody (EmitContext ec)
4097 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4100 protected override void CloneTo (CloneContext clonectx, Statement t)
4102 Lock target = (Lock) t;
4104 target.expr = expr.Clone (clonectx);
4105 target.Statement = Statement.Clone (clonectx);
4109 public class Unchecked : Statement {
4112 public Unchecked (Block b, Location loc)
4119 public override bool Resolve (BlockContext ec)
4121 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4122 return Block.Resolve (ec);
4125 protected override void DoEmit (EmitContext ec)
4127 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4131 protected override void CloneTo (CloneContext clonectx, Statement t)
4133 Unchecked target = (Unchecked) t;
4135 target.Block = clonectx.LookupBlock (Block);
4139 public class Checked : Statement {
4142 public Checked (Block b, Location loc)
4145 b.Unchecked = false;
4149 public override bool Resolve (BlockContext ec)
4151 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4152 return Block.Resolve (ec);
4155 protected override void DoEmit (EmitContext ec)
4157 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4161 protected override void CloneTo (CloneContext clonectx, Statement t)
4163 Checked target = (Checked) t;
4165 target.Block = clonectx.LookupBlock (Block);
4169 public class Unsafe : Statement {
4172 public Unsafe (Block b, Location loc)
4175 Block.Unsafe = true;
4179 public override bool Resolve (BlockContext ec)
4181 if (ec.CurrentIterator != null)
4182 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4184 using (ec.Set (ResolveContext.Options.UnsafeScope))
4185 return Block.Resolve (ec);
4188 protected override void DoEmit (EmitContext ec)
4193 protected override void CloneTo (CloneContext clonectx, Statement t)
4195 Unsafe target = (Unsafe) t;
4197 target.Block = clonectx.LookupBlock (Block);
4204 public class Fixed : Statement {
4206 List<KeyValuePair<LocalInfo, Expression>> declarators;
4207 Statement statement;
4212 abstract class Emitter
4214 protected LocalInfo vi;
4215 protected Expression converted;
4217 protected Emitter (Expression expr, LocalInfo li)
4223 public abstract void Emit (EmitContext ec);
4224 public abstract void EmitExit (EmitContext ec);
4227 class ExpressionEmitter : Emitter {
4228 public ExpressionEmitter (Expression converted, LocalInfo li) :
4229 base (converted, li)
4233 public override void Emit (EmitContext ec) {
4235 // Store pointer in pinned location
4237 converted.Emit (ec);
4241 public override void EmitExit (EmitContext ec)
4243 ec.Emit (OpCodes.Ldc_I4_0);
4244 ec.Emit (OpCodes.Conv_U);
4249 class StringEmitter : Emitter
4251 LocalInfo pinned_string;
4253 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4256 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4257 pinned_string.Pinned = true;
4260 public StringEmitter Resolve (ResolveContext rc)
4262 pinned_string.Resolve (rc);
4264 if (TypeManager.int_get_offset_to_string_data == null) {
4265 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4266 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4272 public override void Emit (EmitContext ec)
4274 pinned_string.ResolveVariable (ec);
4276 converted.Emit (ec);
4277 pinned_string.EmitAssign (ec);
4279 // TODO: Should use Binary::Add
4280 pinned_string.Emit (ec);
4281 ec.Emit (OpCodes.Conv_I);
4283 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4284 //pe.InstanceExpression = pinned_string;
4285 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4287 ec.Emit (OpCodes.Add);
4291 public override void EmitExit (EmitContext ec)
4293 ec.Emit (OpCodes.Ldnull);
4294 pinned_string.EmitAssign (ec);
4298 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4301 declarators = decls;
4306 public Statement Statement {
4307 get { return statement; }
4310 public override bool Resolve (BlockContext ec)
4313 Expression.UnsafeError (ec, loc);
4317 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4318 if (texpr == null) {
4319 if (type is VarExpr)
4320 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4325 expr_type = texpr.Type;
4327 data = new Emitter [declarators.Count];
4329 if (!expr_type.IsPointer){
4330 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4335 foreach (var p in declarators){
4336 LocalInfo vi = p.Key;
4337 Expression e = p.Value;
4339 vi.VariableInfo.SetAssigned (ec);
4340 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4343 // The rules for the possible declarators are pretty wise,
4344 // but the production on the grammar is more concise.
4346 // So we have to enforce these rules here.
4348 // We do not resolve before doing the case 1 test,
4349 // because the grammar is explicit in that the token &
4350 // is present, so we need to test for this particular case.
4354 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4358 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4368 if (e.Type.IsArray){
4369 TypeSpec array_type = TypeManager.GetElementType (e.Type);
4372 // Provided that array_type is unmanaged,
4374 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4378 // and T* is implicitly convertible to the
4379 // pointer type given in the fixed statement.
4381 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4383 Expression converted = Convert.ImplicitConversionRequired (
4384 ec, array_ptr, vi.VariableType, loc);
4385 if (converted == null)
4389 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4391 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4392 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4393 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4394 new NullPointer (loc),
4397 converted = converted.Resolve (ec);
4399 data [i] = new ExpressionEmitter (converted, vi);
4408 if (e.Type == TypeManager.string_type){
4409 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4414 // Case 4: fixed buffer
4415 if (e is FixedBufferPtr) {
4416 data [i++] = new ExpressionEmitter (e, vi);
4421 // Case 1: & object.
4423 Unary u = e as Unary;
4424 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4425 IVariableReference vr = u.Expr as IVariableReference;
4426 if (vr == null || !vr.IsFixed) {
4427 data [i] = new ExpressionEmitter (e, vi);
4431 if (data [i++] == null)
4432 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4434 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4437 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4438 bool ok = statement.Resolve (ec);
4439 bool flow_unreachable = ec.EndFlowBranching ();
4440 has_ret = flow_unreachable;
4445 protected override void DoEmit (EmitContext ec)
4447 for (int i = 0; i < data.Length; i++) {
4451 statement.Emit (ec);
4457 // Clear the pinned variable
4459 for (int i = 0; i < data.Length; i++) {
4460 data [i].EmitExit (ec);
4464 protected override void CloneTo (CloneContext clonectx, Statement t)
4466 Fixed target = (Fixed) t;
4468 target.type = type.Clone (clonectx);
4469 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4470 foreach (var p in declarators) {
4471 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4472 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4475 target.statement = statement.Clone (clonectx);
4479 public class Catch : Statement {
4480 public readonly string Name;
4482 public Block VarBlock;
4484 Expression type_expr;
4487 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4492 VarBlock = var_block;
4496 public TypeSpec CatchType {
4502 public bool IsGeneral {
4504 return type_expr == null;
4508 protected override void DoEmit (EmitContext ec)
4510 if (CatchType != null)
4511 ec.BeginCatchBlock (CatchType);
4513 ec.BeginCatchBlock (TypeManager.object_type);
4515 if (VarBlock != null)
4519 // TODO: Move to resolve
4520 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4521 lvr.Resolve (new ResolveContext (ec.MemberContext));
4523 // Only to make verifier happy
4524 if (TypeManager.IsGenericParameter (lvr.Type))
4525 ec.Emit (OpCodes.Unbox_Any, lvr.Type);
4528 if (lvr.IsHoisted) {
4529 LocalTemporary lt = new LocalTemporary (lvr.Type);
4533 // Variable is at the top of the stack
4534 source = EmptyExpression.Null;
4537 lvr.EmitAssign (ec, source, false, false);
4539 ec.Emit (OpCodes.Pop);
4544 public override bool Resolve (BlockContext ec)
4546 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4547 if (type_expr != null) {
4548 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4554 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4555 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4561 if (!Block.Resolve (ec))
4564 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4565 // emit the "unused variable" warnings.
4566 if (VarBlock != null)
4567 return VarBlock.Resolve (ec);
4573 protected override void CloneTo (CloneContext clonectx, Statement t)
4575 Catch target = (Catch) t;
4577 if (type_expr != null)
4578 target.type_expr = type_expr.Clone (clonectx);
4579 if (VarBlock != null)
4580 target.VarBlock = clonectx.LookupBlock (VarBlock);
4581 target.Block = clonectx.LookupBlock (Block);
4585 public class TryFinally : ExceptionStatement {
4589 public TryFinally (Statement stmt, Block fini, Location l)
4596 public override bool Resolve (BlockContext ec)
4600 ec.StartFlowBranching (this);
4602 if (!stmt.Resolve (ec))
4606 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4607 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4608 if (!fini.Resolve (ec))
4612 ec.EndFlowBranching ();
4614 ok &= base.Resolve (ec);
4619 protected override void EmitPreTryBody (EmitContext ec)
4623 protected override void EmitTryBody (EmitContext ec)
4628 protected override void EmitFinallyBody (EmitContext ec)
4633 protected override void CloneTo (CloneContext clonectx, Statement t)
4635 TryFinally target = (TryFinally) t;
4637 target.stmt = (Statement) stmt.Clone (clonectx);
4639 target.fini = clonectx.LookupBlock (fini);
4643 public class TryCatch : Statement {
4645 public List<Catch> Specific;
4646 public Catch General;
4647 bool inside_try_finally, code_follows;
4649 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4652 this.Specific = catch_clauses;
4653 this.inside_try_finally = inside_try_finally;
4655 Catch c = catch_clauses [0];
4658 catch_clauses.RemoveAt (0);
4664 public override bool Resolve (BlockContext ec)
4668 ec.StartFlowBranching (this);
4670 if (!Block.Resolve (ec))
4673 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4675 foreach (Catch c in Specific){
4676 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4678 if (c.Name != null) {
4679 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4681 throw new Exception ();
4683 vi.VariableInfo = null;
4686 if (!c.Resolve (ec)) {
4691 TypeSpec resolved_type = c.CatchType;
4692 for (int ii = 0; ii < last_index; ++ii) {
4693 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4694 ec.Report.Error (160, c.loc,
4695 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4696 TypeManager.CSharpName (prev_catches [ii]));
4701 prev_catches [last_index++] = resolved_type;
4704 if (General != null) {
4705 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4706 foreach (Catch c in Specific){
4707 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4708 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'");
4713 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4715 if (!General.Resolve (ec))
4719 ec.EndFlowBranching ();
4721 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4722 // So, ensure there's some IL code after this statement
4723 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4724 ec.NeedReturnLabel ();
4729 public void SomeCodeFollows ()
4731 code_follows = true;
4734 protected override void DoEmit (EmitContext ec)
4736 if (!inside_try_finally)
4737 ec.BeginExceptionBlock ();
4741 foreach (Catch c in Specific)
4744 if (General != null)
4747 if (!inside_try_finally)
4748 ec.EndExceptionBlock ();
4751 protected override void CloneTo (CloneContext clonectx, Statement t)
4753 TryCatch target = (TryCatch) t;
4755 target.Block = clonectx.LookupBlock (Block);
4756 if (General != null)
4757 target.General = (Catch) General.Clone (clonectx);
4758 if (Specific != null){
4759 target.Specific = new List<Catch> ();
4760 foreach (Catch c in Specific)
4761 target.Specific.Add ((Catch) c.Clone (clonectx));
4766 // FIXME: Why is it almost exact copy of Using ??
4767 public class UsingTemporary : ExceptionStatement
4769 protected TemporaryVariable local_copy;
4770 Statement statement;
4772 protected Statement dispose_call;
4774 public UsingTemporary (Expression expr, Statement stmt, Location l)
4782 public Expression Expression {
4788 public Statement Statement {
4796 protected virtual bool DoResolve (BlockContext ec)
4798 expr = expr.Resolve (ec);
4802 if (!expr.Type.ImplementsInterface (TypeManager.idisposable_type) &&
4803 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4804 if (expr.Type != InternalType.Dynamic) {
4805 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4809 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4812 var expr_type = expr.Type;
4814 local_copy = new TemporaryVariable (expr_type, loc);
4815 local_copy.Resolve (ec);
4817 if (TypeManager.void_dispose_void == null) {
4818 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4819 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4822 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
4823 dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
4824 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
4827 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
4829 // Add conditional call when disposing possible null variable
4830 if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
4831 dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
4833 return dispose_call.Resolve (ec);
4836 public override bool Resolve (BlockContext ec)
4838 bool ok = DoResolve (ec);
4840 ec.StartFlowBranching (this);
4842 ok &= statement.Resolve (ec);
4844 ec.EndFlowBranching ();
4846 ok &= base.Resolve (ec);
4851 protected override void EmitPreTryBody (EmitContext ec)
4853 local_copy.EmitAssign (ec, expr);
4856 protected override void EmitTryBody (EmitContext ec)
4858 statement.Emit (ec);
4861 protected override void EmitFinallyBody (EmitContext ec)
4863 dispose_call.Emit (ec);
4866 protected override void CloneTo (CloneContext clonectx, Statement t)
4868 UsingTemporary target = (UsingTemporary) t;
4870 target.expr = expr.Clone (clonectx);
4871 target.statement = statement.Clone (clonectx);
4875 public class Using : ExceptionStatement {
4877 public Statement EmbeddedStatement {
4878 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4884 ExpressionStatement assign;
4886 public Using (Expression var, Expression init, Statement stmt, Location l)
4894 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4896 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4897 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4898 TypeManager.CSharpName (expr.Type));
4901 protected override void EmitPreTryBody (EmitContext ec)
4903 assign.EmitStatement (ec);
4906 protected override void EmitTryBody (EmitContext ec)
4911 protected override void EmitFinallyBody (EmitContext ec)
4913 Label skip = ec.DefineLabel ();
4915 bool emit_null_check = !TypeManager.IsValueType (var.Type);
4916 if (emit_null_check) {
4918 ec.Emit (OpCodes.Brfalse, skip);
4921 Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
4923 if (emit_null_check)
4924 ec.MarkLabel (skip);
4927 public override bool Resolve (BlockContext ec)
4929 if (!ResolveVariable (ec))
4932 ec.StartFlowBranching (this);
4934 bool ok = stmt.Resolve (ec);
4936 ec.EndFlowBranching ();
4938 ok &= base.Resolve (ec);
4940 if (TypeManager.void_dispose_void == null) {
4941 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4942 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4948 bool ResolveVariable (BlockContext ec)
4950 assign = new SimpleAssign (var, init, loc);
4951 assign = assign.ResolveStatement (ec);
4955 if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4959 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
4961 if (assign.Type == InternalType.Dynamic) {
4962 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
4963 var = new TemporaryVariable (e.Type, loc);
4964 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
4968 Error_IsNotConvertibleToIDisposable (ec, var);
4972 throw new NotImplementedException ("covariance?");
4975 protected override void CloneTo (CloneContext clonectx, Statement t)
4977 Using target = (Using) t;
4979 target.var = var.Clone (clonectx);
4980 target.init = init.Clone (clonectx);
4981 target.stmt = stmt.Clone (clonectx);
4986 /// Implementation of the foreach C# statement
4988 public class Foreach : Statement {
4990 sealed class ArrayForeach : Statement
4992 class ArrayCounter : TemporaryVariable
4994 StatementExpression increment;
4996 public ArrayCounter (Location loc)
4997 : base (TypeManager.int32_type, loc)
5001 public void ResolveIncrement (BlockContext ec)
5003 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5004 increment.Resolve (ec);
5007 public void EmitIncrement (EmitContext ec)
5009 increment.Emit (ec);
5013 readonly Foreach for_each;
5014 readonly Statement statement;
5017 TemporaryVariable[] lengths;
5018 Expression [] length_exprs;
5019 ArrayCounter[] counter;
5021 TemporaryVariable copy;
5024 public ArrayForeach (Foreach @foreach, int rank)
5026 for_each = @foreach;
5027 statement = for_each.statement;
5030 counter = new ArrayCounter [rank];
5031 length_exprs = new Expression [rank];
5034 // Only use temporary length variables when dealing with
5035 // multi-dimensional arrays
5038 lengths = new TemporaryVariable [rank];
5041 protected override void CloneTo (CloneContext clonectx, Statement target)
5043 throw new NotImplementedException ();
5046 public override bool Resolve (BlockContext ec)
5048 copy = new TemporaryVariable (for_each.expr.Type, loc);
5051 int rank = length_exprs.Length;
5052 Arguments list = new Arguments (rank);
5053 for (int i = 0; i < rank; i++) {
5054 counter [i] = new ArrayCounter (loc);
5055 counter [i].ResolveIncrement (ec);
5058 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5060 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5061 lengths [i].Resolve (ec);
5063 Arguments args = new Arguments (1);
5064 args.Add (new Argument (new IntConstant (i, loc)));
5065 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5068 list.Add (new Argument (counter [i]));
5071 access = new ElementAccess (copy, list, loc).Resolve (ec);
5075 Expression var_type = for_each.type;
5076 VarExpr ve = var_type as VarExpr;
5078 // Infer implicitly typed local variable from foreach array type
5079 var_type = new TypeExpression (access.Type, ve.Location);
5082 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5083 if (var_type == null)
5086 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5092 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5093 ec.CurrentBranching.CreateSibling ();
5095 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5096 if (for_each.variable == null)
5099 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5100 if (!statement.Resolve (ec))
5102 ec.EndFlowBranching ();
5104 // There's no direct control flow from the end of the embedded statement to the end of the loop
5105 ec.CurrentBranching.CurrentUsageVector.Goto ();
5107 ec.EndFlowBranching ();
5112 protected override void DoEmit (EmitContext ec)
5114 copy.EmitAssign (ec, for_each.expr);
5116 int rank = length_exprs.Length;
5117 Label[] test = new Label [rank];
5118 Label[] loop = new Label [rank];
5120 for (int i = 0; i < rank; i++) {
5121 test [i] = ec.DefineLabel ();
5122 loop [i] = ec.DefineLabel ();
5124 if (lengths != null)
5125 lengths [i].EmitAssign (ec, length_exprs [i]);
5128 IntConstant zero = new IntConstant (0, loc);
5129 for (int i = 0; i < rank; i++) {
5130 counter [i].EmitAssign (ec, zero);
5132 ec.Emit (OpCodes.Br, test [i]);
5133 ec.MarkLabel (loop [i]);
5136 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5138 statement.Emit (ec);
5140 ec.MarkLabel (ec.LoopBegin);
5142 for (int i = rank - 1; i >= 0; i--){
5143 counter [i].EmitIncrement (ec);
5145 ec.MarkLabel (test [i]);
5146 counter [i].Emit (ec);
5148 if (lengths != null)
5149 lengths [i].Emit (ec);
5151 length_exprs [i].Emit (ec);
5153 ec.Emit (OpCodes.Blt, loop [i]);
5156 ec.MarkLabel (ec.LoopEnd);
5160 sealed class CollectionForeach : Statement, MethodGroupExpr.IErrorHandler
5162 class Body : Statement
5165 Expression variable, current, conv;
5166 Statement statement;
5169 public Body (TypeSpec type, Expression variable,
5170 Expression current, Statement statement,
5174 this.variable = variable;
5175 this.current = current;
5176 this.statement = statement;
5180 protected override void CloneTo (CloneContext clonectx, Statement target)
5182 throw new NotImplementedException ();
5185 public override bool Resolve (BlockContext ec)
5187 current = current.Resolve (ec);
5188 if (current == null)
5191 conv = Convert.ExplicitConversion (ec, current, type, loc);
5195 assign = new SimpleAssign (variable, conv, loc);
5196 if (assign.Resolve (ec) == null)
5199 if (!statement.Resolve (ec))
5205 protected override void DoEmit (EmitContext ec)
5207 assign.EmitStatement (ec);
5208 statement.Emit (ec);
5212 class Dispose : UsingTemporary
5214 LocalTemporary dispose;
5216 public Dispose (TemporaryVariable variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
5217 : base (expr, statement, loc)
5219 base.local_copy = variable;
5220 this.dispose = dispose;
5223 protected override bool DoResolve (BlockContext ec)
5225 if (TypeManager.void_dispose_void == null) {
5226 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5227 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5230 Expression dispose_var = (Expression) dispose ?? local_copy;
5232 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5233 dispose_mg.InstanceExpression = dispose_var;
5235 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
5237 if (!dispose_var.Type.IsStruct)
5238 dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
5240 return dispose_call.Resolve (ec);
5243 protected override void EmitFinallyBody (EmitContext ec)
5245 Label call_dispose = ec.DefineLabel ();
5246 if (dispose != null) {
5247 local_copy.Emit (ec, false);
5248 ec.Emit (OpCodes.Isinst, dispose.Type);
5252 base.EmitFinallyBody (ec);
5254 if (dispose != null) {
5255 ec.MarkLabel (call_dispose);
5256 dispose.Release (ec);
5261 Expression variable, expr;
5262 Statement statement;
5263 Expression var_type;
5264 ExpressionStatement init;
5266 public CollectionForeach (Expression var_type, Expression var,
5267 Expression expr, Statement stmt, Location l)
5269 this.var_type = var_type;
5270 this.variable = var;
5276 protected override void CloneTo (CloneContext clonectx, Statement target)
5278 throw new NotImplementedException ();
5281 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5283 rc.Report.SymbolRelatedToPreviousError (enumerator);
5284 rc.Report.Error (202, loc,
5285 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5286 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5289 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5292 // Option 1: Try to match by name GetEnumerator first
5294 var mexpr = Expression.MemberLookup (rc.Compiler, rc.CurrentType, null, expr.Type, "GetEnumerator", -1,
5295 MemberKind.All, BindingRestriction.DefaultMemberLookup | BindingRestriction.AccessibleOnly, loc);
5297 var mg = mexpr as MethodGroupExpr;
5299 mg.InstanceExpression = expr;
5300 mg.CustomErrorHandler = this;
5301 Arguments args = new Arguments (0);
5302 mg = mg.OverloadResolve (rc, ref args, false, loc);
5304 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5310 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5312 TypeSpec iface_candidate = null;
5313 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5314 var ifaces = t.Interfaces;
5315 if (ifaces != null) {
5316 foreach (var iface in ifaces) {
5317 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5318 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5319 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5320 rc.Report.Error(1640, loc,
5321 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5322 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5327 iface_candidate = iface;
5331 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5332 iface_candidate = iface;
5338 if (iface_candidate == null) {
5339 rc.Report.Error (1579, loc,
5340 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is not accessible",
5341 expr.Type.GetSignatureForError (), "GetEnumerator");
5346 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5347 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5352 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5353 mg.InstanceExpression = expr;
5357 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5359 var ms = MemberCache.FindMember (enumerator.ReturnType,
5360 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5361 BindingRestriction.InstanceOnly) as MethodSpec;
5363 if (ms == null || !ms.IsPublic) {
5364 Error_WrongEnumerator (rc, enumerator);
5368 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5371 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5373 var ps = MemberCache.FindMember (enumerator.ReturnType,
5374 MemberFilter.Property ("Current", null),
5375 BindingRestriction.InstanceOnly) as PropertySpec;
5377 if (ps == null || !ps.IsPublic) {
5378 Error_WrongEnumerator (rc, enumerator);
5385 public override bool Resolve (BlockContext ec)
5387 bool is_dynamic = expr.Type == InternalType.Dynamic;
5389 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5391 var get_enumerator_mg = ResolveGetEnumerator (ec);
5392 if (get_enumerator_mg == null) {
5396 var get_enumerator = get_enumerator_mg.BestCandidate;
5397 var enumerator = new TemporaryVariable (get_enumerator.ReturnType, loc);
5398 enumerator.Resolve (ec);
5400 // Prepare bool MoveNext ()
5401 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5402 if (move_next_mg == null) {
5406 move_next_mg.InstanceExpression = enumerator;
5408 // Prepare ~T~ Current { get; }
5409 var current_prop = ResolveCurrent (ec, get_enumerator);
5410 if (current_prop == null) {
5414 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator }.Resolve (ec);
5415 if (current_pe == null)
5418 VarExpr ve = var_type as VarExpr;
5420 // Infer implicitly typed local variable from foreach enumerable type
5421 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5424 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5425 if (var_type == null)
5428 var init = new Invocation (get_enumerator_mg, null);
5431 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5432 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5434 var enum_type = enumerator.Type;
5437 // Add Dispose method call when enumerator can be IDisposable
5439 if (!enumerator.Type.ImplementsInterface (TypeManager.idisposable_type)) {
5440 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5442 // Runtime Dispose check
5444 var tv = new LocalTemporary (TypeManager.idisposable_type);
5445 statement = new Dispose (enumerator, tv, init, statement, loc);
5448 // No Dispose call needed
5450 this.init = new SimpleAssign (enumerator, init);
5451 this.init.Resolve (ec);
5455 // Static Dispose check
5457 statement = new Dispose (enumerator, null, init, statement, loc);
5460 return statement.Resolve (ec);
5463 protected override void DoEmit (EmitContext ec)
5466 init.EmitStatement (ec);
5468 statement.Emit (ec);
5471 #region IErrorHandler Members
5473 bool MethodGroupExpr.IErrorHandler.AmbiguousCall (ResolveContext ec, MethodGroupExpr mg, MethodSpec ambiguous)
5475 ec.Report.SymbolRelatedToPreviousError (mg.BestCandidate);
5476 ec.Report.Warning (278, 2, loc,
5477 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5478 mg.DeclaringType.GetSignatureForError (), "enumerable",
5479 mg.BestCandidate.GetSignatureForError (), ambiguous.GetSignatureForError ());
5484 bool MethodGroupExpr.IErrorHandler.NoExactMatch (ResolveContext ec, MethodSpec method)
5493 Expression variable;
5495 Statement statement;
5497 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5498 Statement stmt, Location l)
5501 this.variable = var;
5507 public Statement Statement {
5508 get { return statement; }
5511 public override bool Resolve (BlockContext ec)
5513 expr = expr.Resolve (ec);
5518 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5522 if (expr.Type == TypeManager.string_type) {
5523 statement = new ArrayForeach (this, 1);
5524 } else if (expr.Type is ArrayContainer) {
5525 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5527 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5528 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5529 expr.ExprClassName);
5533 statement = new CollectionForeach (type, variable, expr, statement, loc);
5536 return statement.Resolve (ec);
5539 protected override void DoEmit (EmitContext ec)
5541 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5542 ec.LoopBegin = ec.DefineLabel ();
5543 ec.LoopEnd = ec.DefineLabel ();
5545 statement.Emit (ec);
5547 ec.LoopBegin = old_begin;
5548 ec.LoopEnd = old_end;
5551 protected override void CloneTo (CloneContext clonectx, Statement t)
5553 Foreach target = (Foreach) t;
5555 target.type = type.Clone (clonectx);
5556 target.variable = variable.Clone (clonectx);
5557 target.expr = expr.Clone (clonectx);
5558 target.statement = statement.Clone (clonectx);