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);
635 // A 'return' or a 'yield break'
636 public abstract class ExitStatement : Statement
638 protected bool unwind_protect;
639 protected abstract bool DoResolve (BlockContext ec);
641 public virtual void Error_FinallyClause (Report Report)
643 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
646 public sealed override bool Resolve (BlockContext ec)
651 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
653 ec.NeedReturnLabel ();
654 ec.CurrentBranching.CurrentUsageVector.Goto ();
660 /// Implements the return statement
662 public class Return : ExitStatement {
663 protected Expression Expr;
664 public Return (Expression expr, Location l)
670 protected override bool DoResolve (BlockContext ec)
673 if (ec.ReturnType == TypeManager.void_type)
676 ec.Report.Error (126, loc,
677 "An object of a type convertible to `{0}' is required for the return statement",
678 TypeManager.CSharpName (ec.ReturnType));
682 if (ec.CurrentBlock.Toplevel.IsIterator) {
683 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
684 "statement to return a value, or yield break to end the iteration");
687 AnonymousExpression am = ec.CurrentAnonymousMethod;
688 if (am == null && ec.ReturnType == TypeManager.void_type) {
689 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
690 ec.GetSignatureForError ());
693 Expr = Expr.Resolve (ec);
697 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
698 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
702 if (Expr.Type != ec.ReturnType) {
703 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
707 ec.Report.Error (1662, loc,
708 "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",
709 am.ContainerType, am.GetSignatureForError ());
718 protected override void DoEmit (EmitContext ec)
724 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
728 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
730 ec.Emit (OpCodes.Ret);
733 protected override void CloneTo (CloneContext clonectx, Statement t)
735 Return target = (Return) t;
736 // It's null for simple return;
738 target.Expr = Expr.Clone (clonectx);
742 public class Goto : Statement {
744 LabeledStatement label;
747 public override bool Resolve (BlockContext ec)
749 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
750 ec.CurrentBranching.CurrentUsageVector.Goto ();
754 public Goto (string label, Location l)
760 public string Target {
761 get { return target; }
764 public void SetResolvedTarget (LabeledStatement label)
767 label.AddReference ();
770 protected override void CloneTo (CloneContext clonectx, Statement target)
775 protected override void DoEmit (EmitContext ec)
778 throw new InternalErrorException ("goto emitted before target resolved");
779 Label l = label.LabelTarget (ec);
780 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
784 public class LabeledStatement : Statement {
790 FlowBranching.UsageVector vectors;
792 public LabeledStatement (string name, Location l)
798 public Label LabelTarget (EmitContext ec)
803 label = ec.DefineLabel ();
812 public bool IsDefined {
813 get { return defined; }
816 public bool HasBeenReferenced {
817 get { return referenced; }
820 public FlowBranching.UsageVector JumpOrigins {
821 get { return vectors; }
824 public void AddUsageVector (FlowBranching.UsageVector vector)
826 vector = vector.Clone ();
827 vector.Next = vectors;
831 protected override void CloneTo (CloneContext clonectx, Statement target)
836 public override bool Resolve (BlockContext ec)
838 // this flow-branching will be terminated when the surrounding block ends
839 ec.StartFlowBranching (this);
843 protected override void DoEmit (EmitContext ec)
846 ec.MarkLabel (label);
849 public void AddReference ()
857 /// `goto default' statement
859 public class GotoDefault : Statement {
861 public GotoDefault (Location l)
866 protected override void CloneTo (CloneContext clonectx, Statement target)
871 public override bool Resolve (BlockContext ec)
873 ec.CurrentBranching.CurrentUsageVector.Goto ();
875 if (ec.Switch == null) {
876 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
880 if (!ec.Switch.GotDefault) {
881 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
888 protected override void DoEmit (EmitContext ec)
890 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
895 /// `goto case' statement
897 public class GotoCase : Statement {
901 public GotoCase (Expression e, Location l)
907 public override bool Resolve (BlockContext ec)
909 if (ec.Switch == null){
910 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
914 ec.CurrentBranching.CurrentUsageVector.Goto ();
916 expr = expr.Resolve (ec);
920 Constant c = expr as Constant;
922 ec.Report.Error (150, expr.Location, "A constant value is expected");
926 TypeSpec type = ec.Switch.SwitchType;
927 Constant res = c.TryReduce (ec, type, c.Location);
929 c.Error_ValueCannotBeConverted (ec, loc, type, true);
933 if (!Convert.ImplicitStandardConversionExists (c, type))
934 ec.Report.Warning (469, 2, loc,
935 "The `goto case' value is not implicitly convertible to type `{0}'",
936 TypeManager.CSharpName (type));
938 object val = res.GetValue ();
940 val = SwitchLabel.NullStringCase;
942 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
943 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
944 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
951 protected override void DoEmit (EmitContext ec)
953 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
956 protected override void CloneTo (CloneContext clonectx, Statement t)
958 GotoCase target = (GotoCase) t;
960 target.expr = expr.Clone (clonectx);
964 public class Throw : Statement {
967 public Throw (Expression expr, Location l)
973 public override bool Resolve (BlockContext ec)
976 ec.CurrentBranching.CurrentUsageVector.Goto ();
977 return ec.CurrentBranching.CheckRethrow (loc);
980 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
981 ec.CurrentBranching.CurrentUsageVector.Goto ();
986 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
987 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
989 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
994 protected override void DoEmit (EmitContext ec)
997 ec.Emit (OpCodes.Rethrow);
1001 ec.Emit (OpCodes.Throw);
1005 protected override void CloneTo (CloneContext clonectx, Statement t)
1007 Throw target = (Throw) t;
1010 target.expr = expr.Clone (clonectx);
1014 public class Break : Statement {
1016 public Break (Location l)
1021 bool unwind_protect;
1023 public override bool Resolve (BlockContext ec)
1025 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1026 ec.CurrentBranching.CurrentUsageVector.Goto ();
1030 protected override void DoEmit (EmitContext ec)
1032 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1035 protected override void CloneTo (CloneContext clonectx, Statement t)
1041 public class Continue : Statement {
1043 public Continue (Location l)
1048 bool unwind_protect;
1050 public override bool Resolve (BlockContext ec)
1052 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1053 ec.CurrentBranching.CurrentUsageVector.Goto ();
1057 protected override void DoEmit (EmitContext ec)
1059 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1062 protected override void CloneTo (CloneContext clonectx, Statement t)
1068 public interface ILocalVariable
1070 void Emit (EmitContext ec);
1071 void EmitAssign (EmitContext ec);
1072 void EmitAddressOf (EmitContext ec);
1075 public interface IKnownVariable {
1076 Block Block { get; }
1077 Location Location { get; }
1081 // The information about a user-perceived local variable
1083 public class LocalInfo : IKnownVariable, ILocalVariable {
1084 public readonly FullNamedExpression Type;
1086 public TypeSpec VariableType;
1087 public readonly string Name;
1088 public readonly Location Location;
1089 public readonly Block Block;
1091 public VariableInfo VariableInfo;
1092 HoistedVariable hoisted_variant;
1101 CompilerGenerated = 64,
1105 public enum ReadOnlyContext: byte {
1112 ReadOnlyContext ro_context;
1113 LocalBuilder builder;
1115 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1123 public LocalInfo (TypeContainer ds, Block block, Location l)
1125 VariableType = ds.IsGeneric ? ds.CurrentType : ds.Definition;
1130 public void ResolveVariable (EmitContext ec)
1132 if (HoistedVariant != null)
1135 if (builder == null) {
1136 builder = ec.DeclareLocal (VariableType, Pinned);
1140 public void Emit (EmitContext ec)
1142 ec.Emit (OpCodes.Ldloc, builder);
1145 public void EmitAssign (EmitContext ec)
1147 ec.Emit (OpCodes.Stloc, builder);
1150 public void EmitAddressOf (EmitContext ec)
1152 ec.Emit (OpCodes.Ldloca, builder);
1155 public void EmitSymbolInfo (EmitContext ec)
1157 if (builder != null)
1158 ec.DefineLocalVariable (Name, builder);
1162 // Hoisted local variable variant
1164 public HoistedVariable HoistedVariant {
1166 return hoisted_variant;
1169 hoisted_variant = value;
1173 public bool IsThisAssigned (BlockContext ec, Block block)
1175 if (VariableInfo == null)
1176 throw new Exception ();
1178 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1181 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1184 public bool IsAssigned (BlockContext ec)
1186 if (VariableInfo == null)
1187 throw new Exception ();
1189 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1192 public bool Resolve (ResolveContext ec)
1194 if (VariableType != null)
1197 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1201 VariableType = texpr.Type;
1203 if (VariableType.IsStatic) {
1204 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1208 if (VariableType.IsPointer && !ec.IsUnsafe)
1209 Expression.UnsafeError (ec, Location);
1214 public bool IsConstant {
1215 get { return (flags & Flags.IsConstant) != 0; }
1216 set { flags |= Flags.IsConstant; }
1219 public bool AddressTaken {
1220 get { return (flags & Flags.AddressTaken) != 0; }
1221 set { flags |= Flags.AddressTaken; }
1224 public bool CompilerGenerated {
1225 get { return (flags & Flags.CompilerGenerated) != 0; }
1226 set { flags |= Flags.CompilerGenerated; }
1229 public override string ToString ()
1231 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1232 Name, Type, VariableInfo, Location);
1236 get { return (flags & Flags.Used) != 0; }
1237 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1240 public bool ReadOnly {
1241 get { return (flags & Flags.ReadOnly) != 0; }
1244 public void SetReadOnlyContext (ReadOnlyContext context)
1246 flags |= Flags.ReadOnly;
1247 ro_context = context;
1250 public string GetReadOnlyContext ()
1253 throw new InternalErrorException ("Variable is not readonly");
1255 switch (ro_context) {
1256 case ReadOnlyContext.Fixed:
1257 return "fixed variable";
1258 case ReadOnlyContext.Foreach:
1259 return "foreach iteration variable";
1260 case ReadOnlyContext.Using:
1261 return "using variable";
1263 throw new NotImplementedException ();
1267 // Whether the variable is pinned, if Pinned the variable has been
1268 // allocated in a pinned slot with DeclareLocal.
1270 public bool Pinned {
1271 get { return (flags & Flags.Pinned) != 0; }
1272 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1275 public bool IsThis {
1276 get { return (flags & Flags.IsThis) != 0; }
1277 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1280 Block IKnownVariable.Block {
1281 get { return Block; }
1284 Location IKnownVariable.Location {
1285 get { return Location; }
1288 public LocalInfo Clone (CloneContext clonectx)
1291 // Variables in anonymous block are not resolved yet
1293 if (VariableType == null)
1294 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1297 // Variables in method block are resolved
1299 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1300 li.VariableType = VariableType;
1306 /// Block represents a C# block.
1310 /// This class is used in a number of places: either to represent
1311 /// explicit blocks that the programmer places or implicit blocks.
1313 /// Implicit blocks are used as labels or to introduce variable
1316 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1317 /// they contain extra information that is not necessary on normal blocks.
1319 public class Block : Statement {
1320 public Block Parent;
1321 public Location StartLocation;
1322 public Location EndLocation = Location.Null;
1324 public ExplicitBlock Explicit;
1325 public ToplevelBlock Toplevel; // TODO: Use Explicit
1332 VariablesInitialized = 4,
1336 HasCapturedVariable = 64,
1337 HasCapturedThis = 1 << 7,
1338 IsExpressionTree = 1 << 8
1341 protected Flags flags;
1343 public bool Unchecked {
1344 get { return (flags & Flags.Unchecked) != 0; }
1345 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1348 public bool Unsafe {
1349 get { return (flags & Flags.Unsafe) != 0; }
1350 set { flags |= Flags.Unsafe; }
1354 // The statements in this block
1356 protected List<Statement> statements;
1359 // An array of Blocks. We keep track of children just
1360 // to generate the local variable declarations.
1362 // Statements and child statements are handled through the
1365 List<Block> children;
1368 // Labels. (label, block) pairs.
1370 protected Dictionary<string, LabeledStatement> labels;
1373 // Keeps track of (name, type) pairs
1375 Dictionary<string, LocalInfo> variables;
1378 // Keeps track of constants
1379 Dictionary<string, Expression> constants;
1382 // Temporary variables.
1384 List<LocalInfo> temporary_variables;
1387 // If this is a switch section, the enclosing switch block.
1391 protected List<Statement> scope_initializers;
1393 List<ToplevelBlock> anonymous_children;
1395 int? resolving_init_idx;
1397 protected static int id;
1401 int assignable_slots;
1402 bool unreachable_shown;
1405 public Block (Block parent)
1406 : this (parent, (Flags) 0, Location.Null, Location.Null)
1409 public Block (Block parent, Flags flags)
1410 : this (parent, flags, Location.Null, Location.Null)
1413 public Block (Block parent, Location start, Location end)
1414 : this (parent, (Flags) 0, start, end)
1418 // Useful when TopLevel block is downgraded to normal block
1420 public Block (ToplevelBlock parent, ToplevelBlock source)
1421 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1423 statements = source.statements;
1424 children = source.children;
1425 labels = source.labels;
1426 variables = source.variables;
1427 constants = source.constants;
1428 switch_block = source.switch_block;
1431 public Block (Block parent, Flags flags, Location start, Location end)
1433 if (parent != null) {
1434 parent.AddChild (this);
1436 // the appropriate constructors will fixup these fields
1437 Toplevel = parent.Toplevel;
1438 Explicit = parent.Explicit;
1441 this.Parent = parent;
1443 this.StartLocation = start;
1444 this.EndLocation = end;
1447 statements = new List<Statement> (4);
1450 public Block CreateSwitchBlock (Location start)
1452 // FIXME: should this be implicit?
1453 Block new_block = new ExplicitBlock (this, start, start);
1454 new_block.switch_block = this;
1459 get { return this_id; }
1462 public IDictionary<string, LocalInfo> Variables {
1464 if (variables == null)
1465 variables = new Dictionary<string, LocalInfo> ();
1470 void AddChild (Block b)
1472 if (children == null)
1473 children = new List<Block> (1);
1478 public void SetEndLocation (Location loc)
1483 protected void Error_158 (string name, Location loc)
1485 Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1486 "by the same name in a contained scope", name);
1490 /// Adds a label to the current block.
1494 /// false if the name already exists in this block. true
1498 public bool AddLabel (LabeledStatement target)
1500 if (switch_block != null)
1501 return switch_block.AddLabel (target);
1503 string name = target.Name;
1506 while (cur != null) {
1507 LabeledStatement s = cur.DoLookupLabel (name);
1509 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1510 Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1514 if (this == Explicit)
1520 while (cur != null) {
1521 if (cur.DoLookupLabel (name) != null) {
1522 Error_158 (name, target.loc);
1526 if (children != null) {
1527 foreach (Block b in children) {
1528 LabeledStatement s = b.DoLookupLabel (name);
1532 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1533 Error_158 (name, target.loc);
1541 Toplevel.CheckError158 (name, target.loc);
1544 labels = new Dictionary<string, LabeledStatement> ();
1546 labels.Add (name, target);
1550 public LabeledStatement LookupLabel (string name)
1552 LabeledStatement s = DoLookupLabel (name);
1556 if (children == null)
1559 foreach (Block child in children) {
1560 if (Explicit != child.Explicit)
1563 s = child.LookupLabel (name);
1571 LabeledStatement DoLookupLabel (string name)
1573 if (switch_block != null)
1574 return switch_block.LookupLabel (name);
1577 if (labels.ContainsKey (name))
1578 return labels [name];
1583 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1586 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1587 while (kvi == null) {
1588 b = b.Explicit.Parent;
1591 kvi = b.Explicit.GetKnownVariable (name);
1597 // Is kvi.Block nested inside 'b'
1598 if (b.Explicit != kvi.Block.Explicit) {
1600 // If a variable by the same name it defined in a nested block of this
1601 // block, we violate the invariant meaning in a block.
1604 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1605 Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1610 // It's ok if the definition is in a nested subblock of b, but not
1611 // nested inside this block -- a definition in a sibling block
1612 // should not affect us.
1618 // Block 'b' and kvi.Block are the same textual block.
1619 // However, different variables are extant.
1621 // Check if the variable is in scope in both blocks. We use
1622 // an indirect check that depends on AddVariable doing its
1623 // part in maintaining the invariant-meaning-in-block property.
1625 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1628 if (this is ToplevelBlock) {
1629 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1630 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1635 // Even though we detected the error when the name is used, we
1636 // treat it as if the variable declaration was in error.
1638 Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1639 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1643 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1645 LocalInfo vi = GetLocalInfo (name);
1647 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1648 if (Explicit == vi.Block.Explicit) {
1649 Error_AlreadyDeclared (l, name, null);
1651 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1652 "parent or current" : "parent");
1657 if (block != null) {
1658 Expression e = block.GetParameterReference (name, Location.Null);
1660 ParameterReference pr = e as ParameterReference;
1661 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1662 Error_AlreadyDeclared (loc, name);
1664 Error_AlreadyDeclared (loc, name, "parent or current");
1672 public LocalInfo AddVariable (Expression type, string name, Location l)
1674 if (!CheckParentConflictName (Toplevel, name, l))
1677 if (Toplevel.GenericMethod != null) {
1678 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1679 if (tp.Name == name) {
1680 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1681 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1687 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1689 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1690 Error_AlreadyDeclared (l, name, "child");
1694 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1697 if ((flags & Flags.VariablesInitialized) != 0)
1698 throw new InternalErrorException ("block has already been resolved");
1703 protected virtual void AddVariable (LocalInfo li)
1705 Variables.Add (li.Name, li);
1706 Explicit.AddKnownVariable (li.Name, li);
1709 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1711 if (reason == null) {
1712 Error_AlreadyDeclared (loc, var);
1716 Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1717 "in this scope because it would give a different meaning " +
1718 "to `{0}', which is already used in a `{1}' scope " +
1719 "to denote something else", var, reason);
1722 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1724 Toplevel.Report.Error (128, loc,
1725 "A local variable named `{0}' is already defined in this scope", name);
1728 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1730 r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1734 public bool AddConstant (Expression type, string name, Expression value, Location l)
1736 if (AddVariable (type, name, l) == null)
1739 if (constants == null)
1740 constants = new Dictionary<string, Expression> ();
1742 constants.Add (name, value);
1744 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1749 static int next_temp_id = 0;
1751 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1753 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1755 if (temporary_variables == null)
1756 temporary_variables = new List<LocalInfo> ();
1758 int id = ++next_temp_id;
1759 string name = "$s_" + id.ToString ();
1761 LocalInfo li = new LocalInfo (te, name, this, loc);
1762 li.CompilerGenerated = true;
1763 temporary_variables.Add (li);
1767 public LocalInfo GetLocalInfo (string name)
1770 for (Block b = this; b != null; b = b.Parent) {
1771 if (b.variables != null) {
1772 if (b.variables.TryGetValue (name, out ret))
1780 public Expression GetVariableType (string name)
1782 LocalInfo vi = GetLocalInfo (name);
1783 return vi == null ? null : vi.Type;
1786 public Expression GetConstantExpression (string name)
1789 for (Block b = this; b != null; b = b.Parent) {
1790 if (b.constants != null) {
1791 if (b.constants.TryGetValue (name, out ret))
1799 // It should be used by expressions which require to
1800 // register a statement during resolve process.
1802 public void AddScopeStatement (Statement s)
1804 if (scope_initializers == null)
1805 scope_initializers = new List<Statement> ();
1808 // Simple recursive helper, when resolve scope initializer another
1809 // new scope initializer can be added, this ensures it's initialized
1810 // before existing one. For now this can happen with expression trees
1811 // in base ctor initializer only
1813 if (resolving_init_idx.HasValue) {
1814 scope_initializers.Insert (resolving_init_idx.Value, s);
1815 ++resolving_init_idx;
1817 scope_initializers.Add (s);
1821 public void AddStatement (Statement s)
1824 flags |= Flags.BlockUsed;
1828 get { return (flags & Flags.BlockUsed) != 0; }
1833 flags |= Flags.BlockUsed;
1836 public bool HasRet {
1837 get { return (flags & Flags.HasRet) != 0; }
1840 public int AssignableSlots {
1843 // if ((flags & Flags.VariablesInitialized) == 0)
1844 // throw new Exception ("Variables have not been initialized yet");
1845 return assignable_slots;
1849 public IList<ToplevelBlock> AnonymousChildren {
1850 get { return anonymous_children; }
1853 public void AddAnonymousChild (ToplevelBlock b)
1855 if (anonymous_children == null)
1856 anonymous_children = new List<ToplevelBlock> ();
1858 anonymous_children.Add (b);
1861 void DoResolveConstants (BlockContext ec)
1863 if (constants == null)
1866 if (variables == null)
1867 throw new InternalErrorException ("cannot happen");
1869 foreach (var de in variables) {
1870 string name = de.Key;
1871 LocalInfo vi = de.Value;
1872 TypeSpec variable_type = vi.VariableType;
1874 if (variable_type == null) {
1875 if (vi.Type is VarExpr)
1876 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1882 if (!constants.TryGetValue (name, out cv))
1885 // Don't let 'const int Foo = Foo;' succeed.
1886 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1887 // which in turn causes the 'must be constant' error to be triggered.
1888 constants.Remove (name);
1890 if (!variable_type.IsConstantCompatible) {
1891 Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1895 ec.CurrentBlock = this;
1897 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1898 e = cv.Resolve (ec);
1903 Constant ce = e as Constant;
1905 e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1909 e = ce.ConvertImplicitly (ec, variable_type);
1911 if (TypeManager.IsReferenceType (variable_type))
1912 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1914 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1918 constants.Add (name, e);
1919 vi.IsConstant = true;
1923 protected void ResolveMeta (BlockContext ec, int offset)
1925 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1927 // If some parent block was unsafe, we remain unsafe even if this block
1928 // isn't explicitly marked as such.
1929 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
1930 flags |= Flags.VariablesInitialized;
1932 if (variables != null) {
1933 foreach (LocalInfo li in variables.Values) {
1934 if (!li.Resolve (ec))
1936 li.VariableInfo = new VariableInfo (li, offset);
1937 offset += li.VariableInfo.Length;
1940 assignable_slots = offset;
1942 DoResolveConstants (ec);
1944 if (children == null)
1946 foreach (Block b in children)
1947 b.ResolveMeta (ec, offset);
1952 // Emits the local variable declarations for a block
1954 public virtual void EmitMeta (EmitContext ec)
1956 if (variables != null){
1957 foreach (LocalInfo vi in variables.Values)
1958 vi.ResolveVariable (ec);
1961 if (temporary_variables != null) {
1962 for (int i = 0; i < temporary_variables.Count; i++)
1963 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
1966 if (children != null) {
1967 for (int i = 0; i < children.Count; i++)
1968 ((Block)children[i]).EmitMeta(ec);
1972 void UsageWarning (BlockContext ec)
1974 if (variables == null || ec.Report.WarningLevel < 3)
1977 foreach (var de in variables) {
1978 LocalInfo vi = de.Value;
1981 string name = de.Key;
1983 // vi.VariableInfo can be null for 'catch' variables
1984 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
1985 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
1987 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
1992 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
1996 // Some statements are wrapped by a Block. Since
1997 // others' internal could be changed, here I treat
1998 // them as possibly wrapped by Block equally.
1999 Block b = s as Block;
2000 if (b != null && b.statements.Count == 1)
2001 s = (Statement) b.statements [0];
2004 body = ((Lock) s).Statement;
2006 body = ((For) s).Statement;
2007 else if (s is Foreach)
2008 body = ((Foreach) s).Statement;
2009 else if (s is While)
2010 body = ((While) s).Statement;
2011 else if (s is Fixed)
2012 body = ((Fixed) s).Statement;
2013 else if (s is Using)
2014 body = ((Using) s).EmbeddedStatement;
2015 else if (s is UsingTemporary)
2016 body = ((UsingTemporary) s).Statement;
2020 if (body == null || body is EmptyStatement)
2021 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2024 public override bool Resolve (BlockContext ec)
2026 Block prev_block = ec.CurrentBlock;
2029 int errors = ec.Report.Errors;
2031 ec.CurrentBlock = this;
2032 ec.StartFlowBranching (this);
2034 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2037 // Compiler generated scope statements
2039 if (scope_initializers != null) {
2040 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2041 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2044 resolving_init_idx = null;
2048 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2049 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2050 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2051 // responsible for handling the situation.
2053 int statement_count = statements.Count;
2054 for (int ix = 0; ix < statement_count; ix++){
2055 Statement s = statements [ix];
2056 // Check possible empty statement (CS0642)
2057 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2058 statements [ix + 1] is ExplicitBlock)
2059 CheckPossibleMistakenEmptyStatement (ec, s);
2062 // Warn if we detect unreachable code.
2065 if (s is EmptyStatement)
2068 if (!unreachable_shown && !(s is LabeledStatement)) {
2069 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2070 unreachable_shown = true;
2073 Block c_block = s as Block;
2074 if (c_block != null)
2075 c_block.unreachable = c_block.unreachable_shown = true;
2079 // Note that we're not using ResolveUnreachable() for unreachable
2080 // statements here. ResolveUnreachable() creates a temporary
2081 // flow branching and kills it afterwards. This leads to problems
2082 // if you have two unreachable statements where the first one
2083 // assigns a variable and the second one tries to access it.
2086 if (!s.Resolve (ec)) {
2088 if (ec.IsInProbingMode)
2091 statements [ix] = new EmptyStatement (s.loc);
2095 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2096 statements [ix] = new EmptyStatement (s.loc);
2098 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2099 if (unreachable && s is LabeledStatement)
2100 throw new InternalErrorException ("should not happen");
2103 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2104 ec.CurrentBranching, statement_count);
2106 while (ec.CurrentBranching is FlowBranchingLabeled)
2107 ec.EndFlowBranching ();
2109 bool flow_unreachable = ec.EndFlowBranching ();
2111 ec.CurrentBlock = prev_block;
2113 if (flow_unreachable)
2114 flags |= Flags.HasRet;
2116 // If we're a non-static `struct' constructor which doesn't have an
2117 // initializer, then we must initialize all of the struct's fields.
2118 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2121 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2122 foreach (LabeledStatement label in labels.Values)
2123 if (!label.HasBeenReferenced)
2124 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2127 if (ok && errors == ec.Report.Errors)
2133 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2135 unreachable_shown = true;
2139 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2141 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2142 bool ok = Resolve (ec);
2143 ec.KillFlowBranching ();
2148 protected override void DoEmit (EmitContext ec)
2150 for (int ix = 0; ix < statements.Count; ix++){
2151 Statement s = (Statement) statements [ix];
2156 public override void Emit (EmitContext ec)
2158 if (scope_initializers != null)
2159 EmitScopeInitializers (ec);
2161 ec.Mark (StartLocation);
2164 if (SymbolWriter.HasSymbolWriter)
2165 EmitSymbolInfo (ec);
2168 protected void EmitScopeInitializers (EmitContext ec)
2170 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2172 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2173 foreach (Statement s in scope_initializers)
2177 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2180 protected virtual void EmitSymbolInfo (EmitContext ec)
2182 if (variables != null) {
2183 foreach (LocalInfo vi in variables.Values) {
2184 vi.EmitSymbolInfo (ec);
2189 public override string ToString ()
2191 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2194 protected override void CloneTo (CloneContext clonectx, Statement t)
2196 Block target = (Block) t;
2198 clonectx.AddBlockMap (this, target);
2200 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2201 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2203 target.Parent = clonectx.RemapBlockCopy (Parent);
2205 if (variables != null){
2206 target.variables = new Dictionary<string, LocalInfo> ();
2208 foreach (var de in variables){
2209 LocalInfo newlocal = de.Value.Clone (clonectx);
2210 target.variables [de.Key] = newlocal;
2211 clonectx.AddVariableMap (de.Value, newlocal);
2215 target.statements = new List<Statement> (statements.Count);
2216 foreach (Statement s in statements)
2217 target.statements.Add (s.Clone (clonectx));
2219 if (target.children != null){
2220 target.children = new List<Block> (children.Count);
2221 foreach (Block b in children){
2222 target.children.Add (clonectx.LookupBlock (b));
2227 // TODO: labels, switch_block, constants (?), anonymous_children
2232 public class ExplicitBlock : Block
2234 Dictionary<string, IKnownVariable> known_variables;
2235 protected AnonymousMethodStorey am_storey;
2237 public ExplicitBlock (Block parent, Location start, Location end)
2238 : this (parent, (Flags) 0, start, end)
2242 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2243 : base (parent, flags, start, end)
2245 this.Explicit = this;
2249 // Marks a variable with name @name as being used in this or a child block.
2250 // If a variable name has been used in a child block, it's illegal to
2251 // declare a variable with the same name in the current block.
2253 internal void AddKnownVariable (string name, IKnownVariable info)
2255 if (known_variables == null)
2256 known_variables = new Dictionary<string, IKnownVariable> ();
2258 known_variables [name] = info;
2261 Parent.Explicit.AddKnownVariable (name, info);
2264 public AnonymousMethodStorey AnonymousMethodStorey {
2265 get { return am_storey; }
2269 // Creates anonymous method storey in current block
2271 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2274 // When referencing a variable in iterator storey from children anonymous method
2276 if (Toplevel.am_storey is IteratorStorey) {
2277 return Toplevel.am_storey;
2281 // An iterator has only 1 storey block
2283 if (ec.CurrentIterator != null)
2284 return ec.CurrentIterator.Storey;
2286 if (am_storey == null) {
2287 MemberBase mc = ec.MemberContext as MemberBase;
2288 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2291 // Creates anonymous method storey for this block
2293 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2299 public override void Emit (EmitContext ec)
2301 if (am_storey != null)
2302 am_storey.EmitStoreyInstantiation (ec);
2304 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2305 if (emit_debug_info)
2310 if (emit_debug_info)
2314 public override void EmitMeta (EmitContext ec)
2317 // Creates anonymous method storey
2319 if (am_storey != null) {
2320 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2322 // Creates parent storey reference when hoisted this is accessible
2324 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2325 ExplicitBlock parent = Toplevel.Parent.Explicit;
2328 // Hoisted this exists in top-level parent storey only
2330 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2331 parent = parent.Parent.Explicit;
2333 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2336 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2338 // TODO MemberCache: Review
2339 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2342 am_storey.CreateType ();
2343 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2344 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2346 am_storey.DefineType ();
2347 am_storey.ResolveTypeParameters ();
2348 am_storey.Define ();
2349 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2351 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2352 if (ref_blocks != null) {
2353 foreach (ExplicitBlock ref_block in ref_blocks) {
2354 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2355 if (b.am_storey != null) {
2356 b.am_storey.AddParentStoreyReference (ec, am_storey);
2358 // Stop propagation inside same top block
2359 if (b.Toplevel == Toplevel)
2364 b.HasCapturedVariable = true;
2373 internal IKnownVariable GetKnownVariable (string name)
2375 if (known_variables == null)
2379 if (!known_variables.TryGetValue (name, out kw))
2385 public bool HasCapturedThis
2387 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2388 get { return (flags & Flags.HasCapturedThis) != 0; }
2391 public bool HasCapturedVariable
2393 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2394 get { return (flags & Flags.HasCapturedVariable) != 0; }
2397 protected override void CloneTo (CloneContext clonectx, Statement t)
2399 ExplicitBlock target = (ExplicitBlock) t;
2400 target.known_variables = null;
2401 base.CloneTo (clonectx, t);
2405 public class ToplevelParameterInfo : IKnownVariable {
2406 public readonly ToplevelBlock Block;
2407 public readonly int Index;
2408 public VariableInfo VariableInfo;
2410 Block IKnownVariable.Block {
2411 get { return Block; }
2413 public Parameter Parameter {
2414 get { return Block.Parameters [Index]; }
2417 public TypeSpec ParameterType {
2418 get { return Block.Parameters.Types [Index]; }
2421 public Location Location {
2422 get { return Parameter.Location; }
2425 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2433 // A toplevel block contains extra information, the split is done
2434 // only to separate information that would otherwise bloat the more
2435 // lightweight Block.
2437 // In particular, this was introduced when the support for Anonymous
2438 // Methods was implemented.
2440 public class ToplevelBlock : ExplicitBlock
2443 // Block is converted to an expression
2445 sealed class BlockScopeExpression : Expression
2448 readonly ToplevelBlock block;
2450 public BlockScopeExpression (Expression child, ToplevelBlock block)
2456 public override Expression CreateExpressionTree (ResolveContext ec)
2458 throw new NotSupportedException ();
2461 protected override Expression DoResolve (ResolveContext ec)
2466 child = child.Resolve (ec);
2470 eclass = child.eclass;
2475 public override void Emit (EmitContext ec)
2477 block.EmitMeta (ec);
2478 block.EmitScopeInitializers (ec);
2483 GenericMethod generic;
2484 protected ParametersCompiled parameters;
2485 ToplevelParameterInfo[] parameter_info;
2486 LocalInfo this_variable;
2489 CompilerContext compiler;
2491 public HoistedVariable HoistedThisVariable;
2493 public bool Resolved {
2500 // The parameters for the block.
2502 public ParametersCompiled Parameters {
2503 get { return parameters; }
2506 public Report Report {
2507 get { return compiler.Report; }
2510 public GenericMethod GenericMethod {
2511 get { return generic; }
2514 public ToplevelBlock Container {
2515 get { return Parent == null ? null : Parent.Toplevel; }
2518 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2519 this (ctx, parent, (Flags) 0, parameters, start)
2523 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2524 this (ctx, parent, parameters, start)
2526 this.generic = generic;
2529 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2530 this (ctx, null, (Flags) 0, parameters, start)
2534 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2535 this (ctx, null, flags, parameters, start)
2539 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2540 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2541 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2542 base (null, flags, start, Location.Null)
2544 this.compiler = ctx;
2545 this.Toplevel = this;
2547 this.parameters = parameters;
2548 this.Parent = parent;
2550 parent.AddAnonymousChild (this);
2552 if (!this.parameters.IsEmpty)
2553 ProcessParameters ();
2556 public ToplevelBlock (CompilerContext ctx, Location loc)
2557 : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2561 protected override void CloneTo (CloneContext clonectx, Statement t)
2563 ToplevelBlock target = (ToplevelBlock) t;
2564 base.CloneTo (clonectx, t);
2566 if (parameters.Count != 0)
2567 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2568 for (int i = 0; i < parameters.Count; ++i)
2569 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2572 public bool CheckError158 (string name, Location loc)
2574 if (AnonymousChildren != null) {
2575 foreach (ToplevelBlock child in AnonymousChildren) {
2576 if (!child.CheckError158 (name, loc))
2581 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2582 if (!c.DoCheckError158 (name, loc))
2589 void ProcessParameters ()
2591 int n = parameters.Count;
2592 parameter_info = new ToplevelParameterInfo [n];
2593 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2594 for (int i = 0; i < n; ++i) {
2595 parameter_info [i] = new ToplevelParameterInfo (this, i);
2597 Parameter p = parameters [i];
2601 string name = p.Name;
2602 if (CheckParentConflictName (top_parent, name, loc))
2603 AddKnownVariable (name, parameter_info [i]);
2606 // mark this block as "used" so that we create local declarations in a sub-block
2607 // FIXME: This appears to uncover a lot of bugs
2611 bool DoCheckError158 (string name, Location loc)
2613 LabeledStatement s = LookupLabel (name);
2615 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2616 Error_158 (name, loc);
2623 public override Expression CreateExpressionTree (ResolveContext ec)
2625 if (statements.Count == 1) {
2626 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2627 if (scope_initializers != null)
2628 expr = new BlockScopeExpression (expr, this);
2633 return base.CreateExpressionTree (ec);
2637 // Reformats this block to be top-level iterator block
2639 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2643 // Creates block with original statements
2644 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2646 source.statements = new List<Statement> (1);
2647 source.AddStatement (new Return (iterator, iterator.Location));
2648 source.IsIterator = false;
2650 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2651 source.am_storey = iterator_storey;
2652 return iterator_storey;
2656 // Returns a parameter reference expression for the given name,
2657 // or null if there is no such parameter
2659 public Expression GetParameterReference (string name, Location loc)
2661 for (ToplevelBlock t = this; t != null; t = t.Container) {
2662 Expression expr = t.GetParameterReferenceExpression (name, loc);
2670 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2672 int idx = parameters.GetParameterIndexByName (name);
2674 null : new ParameterReference (parameter_info [idx], loc);
2678 // Returns the "this" instance variable of this block.
2679 // See AddThisVariable() for more information.
2681 public LocalInfo ThisVariable {
2682 get { return this_variable; }
2686 // This is used by non-static `struct' constructors which do not have an
2687 // initializer - in this case, the constructor must initialize all of the
2688 // struct's fields. To do this, we add a "this" variable and use the flow
2689 // analysis code to ensure that it's been fully initialized before control
2690 // leaves the constructor.
2692 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2694 if (this_variable == null) {
2695 this_variable = new LocalInfo (ds, this, l);
2696 this_variable.Used = true;
2697 this_variable.IsThis = true;
2699 Variables.Add ("this", this_variable);
2702 return this_variable;
2705 public bool IsIterator {
2706 get { return (flags & Flags.IsIterator) != 0; }
2707 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2711 // Block has been converted to expression tree
2713 public bool IsExpressionTree {
2714 get { return (flags & Flags.IsExpressionTree) != 0; }
2717 public bool IsThisAssigned (BlockContext ec)
2719 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2722 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2729 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2730 flags |= Flags.IsExpressionTree;
2733 if (!ResolveMeta (rc, ip))
2736 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2737 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2742 unreachable = top_level.End ();
2744 } catch (Exception) {
2746 if (rc.CurrentBlock != null) {
2747 ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2749 ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2755 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2756 if (rc.CurrentAnonymousMethod == null) {
2757 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2759 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2760 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2761 rc.CurrentAnonymousMethod.GetSignatureForError ());
2769 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2771 int errors = ec.Report.Errors;
2772 int orig_count = parameters.Count;
2777 // Assert: orig_count != parameter.Count => orig_count == 0
2778 if (orig_count != 0 && orig_count != parameters.Count)
2779 throw new InternalErrorException ("parameter information mismatch");
2781 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2783 for (int i = 0; i < orig_count; ++i) {
2784 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2786 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2789 VariableInfo vi = new VariableInfo (ip, i, offset);
2790 parameter_info [i].VariableInfo = vi;
2791 offset += vi.Length;
2794 ResolveMeta (ec, offset);
2796 return ec.Report.Errors == errors;
2800 // Check whether all `out' parameters have been assigned.
2802 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2804 if (vector.IsUnreachable)
2807 int n = parameter_info == null ? 0 : parameter_info.Length;
2809 for (int i = 0; i < n; i++) {
2810 VariableInfo var = parameter_info [i].VariableInfo;
2815 if (vector.IsAssigned (var, false))
2818 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2823 public override void Emit (EmitContext ec)
2825 if (Report.Errors > 0)
2833 if (ec.HasReturnLabel)
2834 ec.ReturnLabel = ec.DefineLabel ();
2838 ec.Mark (EndLocation);
2840 if (ec.HasReturnLabel)
2841 ec.MarkLabel (ec.ReturnLabel);
2843 if (ec.return_value != null) {
2844 ec.Emit (OpCodes.Ldloc, ec.return_value);
2845 ec.Emit (OpCodes.Ret);
2848 // If `HasReturnLabel' is set, then we already emitted a
2849 // jump to the end of the method, so we must emit a `ret'
2852 // Unfortunately, System.Reflection.Emit automatically emits
2853 // a leave to the end of a finally block. This is a problem
2854 // if no code is following the try/finally block since we may
2855 // jump to a point after the end of the method.
2856 // As a workaround, we're always creating a return label in
2860 if (ec.HasReturnLabel || !unreachable) {
2861 if (ec.ReturnType != TypeManager.void_type)
2862 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2863 ec.Emit (OpCodes.Ret);
2868 } catch (Exception e){
2869 Console.WriteLine ("Exception caught by the compiler while emitting:");
2870 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2872 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2878 public override void EmitMeta (EmitContext ec)
2880 // Avoid declaring an IL variable for this_variable since it is not accessed
2881 // from the generated IL
2882 if (this_variable != null)
2883 Variables.Remove ("this");
2887 protected override void EmitSymbolInfo (EmitContext ec)
2889 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2890 if ((ae != null) && (ae.Storey != null))
2891 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2893 base.EmitSymbolInfo (ec);
2897 public class SwitchLabel {
2904 Label il_label_code;
2905 bool il_label_code_set;
2907 public static readonly object NullStringCase = new object ();
2910 // if expr == null, then it is the default case.
2912 public SwitchLabel (Expression expr, Location l)
2918 public Expression Label {
2924 public Location Location {
2928 public object Converted {
2934 public Label GetILLabel (EmitContext ec)
2937 il_label = ec.DefineLabel ();
2938 il_label_set = true;
2943 public Label GetILLabelCode (EmitContext ec)
2945 if (!il_label_code_set){
2946 il_label_code = ec.DefineLabel ();
2947 il_label_code_set = true;
2949 return il_label_code;
2953 // Resolves the expression, reduces it to a literal if possible
2954 // and then converts it to the requested type.
2956 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
2958 Expression e = label.Resolve (ec);
2963 Constant c = e as Constant;
2965 ec.Report.Error (150, loc, "A constant value is expected");
2969 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2970 converted = NullStringCase;
2974 if (allow_nullable && c.GetValue () == null) {
2975 converted = NullStringCase;
2979 c = c.ImplicitConversionRequired (ec, required_type, loc);
2983 converted = c.GetValue ();
2987 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
2990 if (converted == null)
2992 else if (converted == NullStringCase)
2995 label = converted.ToString ();
2997 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2998 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3001 public SwitchLabel Clone (CloneContext clonectx)
3003 return new SwitchLabel (label.Clone (clonectx), loc);
3007 public class SwitchSection {
3008 // An array of SwitchLabels.
3009 public readonly List<SwitchLabel> Labels;
3010 public readonly Block Block;
3012 public SwitchSection (List<SwitchLabel> labels, Block block)
3018 public SwitchSection Clone (CloneContext clonectx)
3020 var cloned_labels = new List<SwitchLabel> ();
3022 foreach (SwitchLabel sl in cloned_labels)
3023 cloned_labels.Add (sl.Clone (clonectx));
3025 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3029 public class Switch : Statement {
3030 public List<SwitchSection> Sections;
3031 public Expression Expr;
3034 /// Maps constants whose type type SwitchType to their SwitchLabels.
3036 public IDictionary<object, SwitchLabel> Elements;
3039 /// The governing switch type
3041 public TypeSpec SwitchType;
3046 Label default_target;
3048 Expression new_expr;
3051 SwitchSection constant_section;
3052 SwitchSection default_section;
3054 ExpressionStatement string_dictionary;
3055 FieldExpr switch_cache_field;
3056 static int unique_counter;
3059 // Nullable Types support
3061 Nullable.Unwrap unwrap;
3063 protected bool HaveUnwrap {
3064 get { return unwrap != null; }
3068 // The types allowed to be implicitly cast from
3069 // on the governing type
3071 static TypeSpec [] allowed_types;
3073 public Switch (Expression e, List<SwitchSection> sects, Location l)
3080 public bool GotDefault {
3082 return default_section != null;
3086 public Label DefaultTarget {
3088 return default_target;
3093 // Determines the governing type for a switch. The returned
3094 // expression might be the expression from the switch, or an
3095 // expression that includes any potential conversions to the
3096 // integral types or to string.
3098 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3100 TypeSpec t = expr.Type;
3102 if (t == TypeManager.byte_type ||
3103 t == TypeManager.sbyte_type ||
3104 t == TypeManager.ushort_type ||
3105 t == TypeManager.short_type ||
3106 t == TypeManager.uint32_type ||
3107 t == TypeManager.int32_type ||
3108 t == TypeManager.uint64_type ||
3109 t == TypeManager.int64_type ||
3110 t == TypeManager.char_type ||
3111 t == TypeManager.string_type ||
3112 t == TypeManager.bool_type ||
3113 TypeManager.IsEnumType (t))
3116 if (allowed_types == null){
3117 allowed_types = new TypeSpec [] {
3118 TypeManager.sbyte_type,
3119 TypeManager.byte_type,
3120 TypeManager.short_type,
3121 TypeManager.ushort_type,
3122 TypeManager.int32_type,
3123 TypeManager.uint32_type,
3124 TypeManager.int64_type,
3125 TypeManager.uint64_type,
3126 TypeManager.char_type,
3127 TypeManager.string_type
3132 // Try to find a *user* defined implicit conversion.
3134 // If there is no implicit conversion, or if there are multiple
3135 // conversions, we have to report an error
3137 Expression converted = null;
3138 foreach (TypeSpec tt in allowed_types){
3141 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3146 // Ignore over-worked ImplicitUserConversions that do
3147 // an implicit conversion in addition to the user conversion.
3149 if (!(e is UserCast))
3152 if (converted != null){
3153 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3163 // Performs the basic sanity checks on the switch statement
3164 // (looks for duplicate keys and non-constant expressions).
3166 // It also returns a hashtable with the keys that we will later
3167 // use to compute the switch tables
3169 bool CheckSwitch (ResolveContext ec)
3172 Elements = new Dictionary<object, SwitchLabel> ();
3174 foreach (SwitchSection ss in Sections){
3175 foreach (SwitchLabel sl in ss.Labels){
3176 if (sl.Label == null){
3177 if (default_section != null){
3178 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3181 default_section = ss;
3185 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3190 object key = sl.Converted;
3191 if (key == SwitchLabel.NullStringCase)
3192 has_null_case = true;
3195 Elements.Add (key, sl);
3196 } catch (ArgumentException) {
3197 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3205 void EmitObjectInteger (EmitContext ec, object k)
3208 ec.EmitInt ((int) k);
3209 else if (k is Constant) {
3210 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3213 ec.EmitInt (unchecked ((int) (uint) k));
3216 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3218 ec.EmitInt ((int) (long) k);
3219 ec.Emit (OpCodes.Conv_I8);
3222 ec.EmitLong ((long) k);
3224 else if (k is ulong)
3226 ulong ul = (ulong) k;
3229 ec.EmitInt (unchecked ((int) ul));
3230 ec.Emit (OpCodes.Conv_U8);
3234 ec.EmitLong (unchecked ((long) ul));
3238 ec.EmitInt ((int) ((char) k));
3239 else if (k is sbyte)
3240 ec.EmitInt ((int) ((sbyte) k));
3242 ec.EmitInt ((int) ((byte) k));
3243 else if (k is short)
3244 ec.EmitInt ((int) ((short) k));
3245 else if (k is ushort)
3246 ec.EmitInt ((int) ((ushort) k));
3248 ec.EmitInt (((bool) k) ? 1 : 0);
3250 throw new Exception ("Unhandled case");
3253 // structure used to hold blocks of keys while calculating table switch
3254 class KeyBlock : IComparable
3256 public KeyBlock (long _first)
3258 first = last = _first;
3262 public List<object> element_keys;
3263 // how many items are in the bucket
3264 public int Size = 1;
3267 get { return (int) (last - first + 1); }
3269 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3271 return kb_last.last - kb_first.first + 1;
3273 public int CompareTo (object obj)
3275 KeyBlock kb = (KeyBlock) obj;
3276 int nLength = Length;
3277 int nLengthOther = kb.Length;
3278 if (nLengthOther == nLength)
3279 return (int) (kb.first - first);
3280 return nLength - nLengthOther;
3285 /// This method emits code for a lookup-based switch statement (non-string)
3286 /// Basically it groups the cases into blocks that are at least half full,
3287 /// and then spits out individual lookup opcodes for each block.
3288 /// It emits the longest blocks first, and short blocks are just
3289 /// handled with direct compares.
3291 /// <param name="ec"></param>
3292 /// <param name="val"></param>
3293 /// <returns></returns>
3294 void TableSwitchEmit (EmitContext ec, Expression val)
3296 int element_count = Elements.Count;
3297 object [] element_keys = new object [element_count];
3298 Elements.Keys.CopyTo (element_keys, 0);
3299 Array.Sort (element_keys);
3301 // initialize the block list with one element per key
3302 var key_blocks = new List<KeyBlock> (element_count);
3303 foreach (object key in element_keys)
3304 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3306 KeyBlock current_kb;
3307 // iteratively merge the blocks while they are at least half full
3308 // there's probably a really cool way to do this with a tree...
3309 while (key_blocks.Count > 1)
3311 var key_blocks_new = new List<KeyBlock> ();
3312 current_kb = (KeyBlock) key_blocks [0];
3313 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3315 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3316 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3319 current_kb.last = kb.last;
3320 current_kb.Size += kb.Size;
3324 // start a new block
3325 key_blocks_new.Add (current_kb);
3329 key_blocks_new.Add (current_kb);
3330 if (key_blocks.Count == key_blocks_new.Count)
3332 key_blocks = key_blocks_new;
3335 // initialize the key lists
3336 foreach (KeyBlock kb in key_blocks)
3337 kb.element_keys = new List<object> ();
3339 // fill the key lists
3341 if (key_blocks.Count > 0) {
3342 current_kb = (KeyBlock) key_blocks [0];
3343 foreach (object key in element_keys)
3345 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3346 System.Convert.ToInt64 (key) > current_kb.last;
3348 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3349 current_kb.element_keys.Add (key);
3353 // sort the blocks so we can tackle the largest ones first
3356 // okay now we can start...
3357 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3358 Label lbl_default = default_target;
3360 Type type_keys = null;
3361 if (element_keys.Length > 0)
3362 type_keys = element_keys [0].GetType (); // used for conversions
3364 TypeSpec compare_type;
3366 if (TypeManager.IsEnumType (SwitchType))
3367 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3369 compare_type = SwitchType;
3371 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3373 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3374 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3377 foreach (object key in kb.element_keys) {
3378 SwitchLabel sl = (SwitchLabel) Elements [key];
3379 if (key is int && (int) key == 0) {
3380 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3383 EmitObjectInteger (ec, key);
3384 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3390 // TODO: if all the keys in the block are the same and there are
3391 // no gaps/defaults then just use a range-check.
3392 if (compare_type == TypeManager.int64_type ||
3393 compare_type == TypeManager.uint64_type)
3395 // TODO: optimize constant/I4 cases
3397 // check block range (could be > 2^31)
3399 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3400 ec.Emit (OpCodes.Blt, lbl_default);
3402 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3403 ec.Emit (OpCodes.Bgt, lbl_default);
3409 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3410 ec.Emit (OpCodes.Sub);
3412 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3418 int first = (int) kb.first;
3422 ec.Emit (OpCodes.Sub);
3426 ec.EmitInt (-first);
3427 ec.Emit (OpCodes.Add);
3431 // first, build the list of labels for the switch
3433 int cJumps = kb.Length;
3434 Label [] switch_labels = new Label [cJumps];
3435 for (int iJump = 0; iJump < cJumps; iJump++)
3437 object key = kb.element_keys [iKey];
3438 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3440 SwitchLabel sl = (SwitchLabel) Elements [key];
3441 switch_labels [iJump] = sl.GetILLabel (ec);
3445 switch_labels [iJump] = lbl_default;
3447 // emit the switch opcode
3448 ec.Emit (OpCodes.Switch, switch_labels);
3451 // mark the default for this block
3453 ec.MarkLabel (lbl_default);
3456 // TODO: find the default case and emit it here,
3457 // to prevent having to do the following jump.
3458 // make sure to mark other labels in the default section
3460 // the last default just goes to the end
3461 if (element_keys.Length > 0)
3462 ec.Emit (OpCodes.Br, lbl_default);
3464 // now emit the code for the sections
3465 bool found_default = false;
3467 foreach (SwitchSection ss in Sections) {
3468 foreach (SwitchLabel sl in ss.Labels) {
3469 if (sl.Converted == SwitchLabel.NullStringCase) {
3470 ec.MarkLabel (null_target);
3471 } else if (sl.Label == null) {
3472 ec.MarkLabel (lbl_default);
3473 found_default = true;
3475 ec.MarkLabel (null_target);
3477 ec.MarkLabel (sl.GetILLabel (ec));
3478 ec.MarkLabel (sl.GetILLabelCode (ec));
3483 if (!found_default) {
3484 ec.MarkLabel (lbl_default);
3485 if (!has_null_case) {
3486 ec.MarkLabel (null_target);
3490 ec.MarkLabel (lbl_end);
3493 SwitchSection FindSection (SwitchLabel label)
3495 foreach (SwitchSection ss in Sections){
3496 foreach (SwitchLabel sl in ss.Labels){
3505 public static void Reset ()
3508 allowed_types = null;
3511 public override bool Resolve (BlockContext ec)
3513 Expr = Expr.Resolve (ec);
3517 new_expr = SwitchGoverningType (ec, Expr);
3519 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3520 unwrap = Nullable.Unwrap.Create (Expr, false);
3524 new_expr = SwitchGoverningType (ec, unwrap);
3527 if (new_expr == null){
3528 ec.Report.Error (151, loc,
3529 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3530 TypeManager.CSharpName (Expr.Type));
3535 SwitchType = new_expr.Type;
3537 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3538 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3542 if (!CheckSwitch (ec))
3546 Elements.Remove (SwitchLabel.NullStringCase);
3548 Switch old_switch = ec.Switch;
3550 ec.Switch.SwitchType = SwitchType;
3552 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3553 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3555 var constant = new_expr as Constant;
3556 if (constant != null) {
3558 object key = constant.GetValue ();
3560 if (Elements.TryGetValue (key, out label))
3561 constant_section = FindSection (label);
3563 if (constant_section == null)
3564 constant_section = default_section;
3569 foreach (SwitchSection ss in Sections){
3571 ec.CurrentBranching.CreateSibling (
3572 null, FlowBranching.SiblingType.SwitchSection);
3576 if (is_constant && (ss != constant_section)) {
3577 // If we're a constant switch, we're only emitting
3578 // one single section - mark all the others as
3580 ec.CurrentBranching.CurrentUsageVector.Goto ();
3581 if (!ss.Block.ResolveUnreachable (ec, true)) {
3585 if (!ss.Block.Resolve (ec))
3590 if (default_section == null)
3591 ec.CurrentBranching.CreateSibling (
3592 null, FlowBranching.SiblingType.SwitchSection);
3594 ec.EndFlowBranching ();
3595 ec.Switch = old_switch;
3597 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3602 if (SwitchType == TypeManager.string_type && !is_constant) {
3603 // TODO: Optimize single case, and single+default case
3604 ResolveStringSwitchMap (ec);
3610 void ResolveStringSwitchMap (ResolveContext ec)
3612 FullNamedExpression string_dictionary_type;
3613 if (TypeManager.generic_ienumerable_type != null) {
3614 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3615 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3617 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3619 new TypeExpression (TypeManager.string_type, loc),
3620 new TypeExpression (TypeManager.int32_type, loc)), loc);
3622 MemberAccess system_collections_generic = new MemberAccess (
3623 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3625 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3628 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3629 Field field = new Field (ctype, string_dictionary_type,
3630 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3631 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3632 if (!field.Define ())
3634 ctype.AddField (field);
3636 var init = new List<Expression> ();
3639 string value = null;
3640 foreach (SwitchSection section in Sections) {
3641 int last_count = init.Count;
3642 foreach (SwitchLabel sl in section.Labels) {
3643 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3646 value = (string) sl.Converted;
3647 var init_args = new List<Expression> (2);
3648 init_args.Add (new StringLiteral (value, sl.Location));
3649 init_args.Add (new IntConstant (counter, loc));
3650 init.Add (new CollectionElementInitializer (init_args, loc));
3654 // Don't add empty sections
3656 if (last_count == init.Count)
3659 Elements.Add (counter, section.Labels [0]);
3663 Arguments args = new Arguments (1);
3664 args.Add (new Argument (new IntConstant (init.Count, loc)));
3665 Expression initializer = new NewInitialize (string_dictionary_type, args,
3666 new CollectionOrObjectInitializers (init, loc), loc);
3668 switch_cache_field = new FieldExpr (field, loc);
3669 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3672 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3674 Label l_initialized = ec.DefineLabel ();
3677 // Skip initialization when value is null
3679 value.EmitBranchable (ec, null_target, false);
3682 // Check if string dictionary is initialized and initialize
3684 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3685 string_dictionary.EmitStatement (ec);
3686 ec.MarkLabel (l_initialized);
3688 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3690 ResolveContext rc = new ResolveContext (ec.MemberContext);
3692 if (TypeManager.generic_ienumerable_type != null) {
3693 Arguments get_value_args = new Arguments (2);
3694 get_value_args.Add (new Argument (value));
3695 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3696 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3697 if (get_item == null)
3701 // A value was not found, go to default case
3703 get_item.EmitBranchable (ec, default_target, false);
3705 Arguments get_value_args = new Arguments (1);
3706 get_value_args.Add (new Argument (value));
3708 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3709 if (get_item == null)
3712 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3713 get_item_object.EmitAssign (ec, get_item, true, false);
3714 ec.Emit (OpCodes.Brfalse, default_target);
3716 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3717 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3719 get_item_int.EmitStatement (ec);
3720 get_item_object.Release (ec);
3723 TableSwitchEmit (ec, string_switch_variable);
3724 string_switch_variable.Release (ec);
3727 protected override void DoEmit (EmitContext ec)
3729 default_target = ec.DefineLabel ();
3730 null_target = ec.DefineLabel ();
3732 // Store variable for comparission purposes
3733 // TODO: Don't duplicate non-captured VariableReference
3734 LocalTemporary value;
3736 value = new LocalTemporary (SwitchType);
3737 unwrap.EmitCheck (ec);
3738 ec.Emit (OpCodes.Brfalse, null_target);
3741 } else if (!is_constant) {
3742 value = new LocalTemporary (SwitchType);
3749 // Setup the codegen context
3751 Label old_end = ec.LoopEnd;
3752 Switch old_switch = ec.Switch;
3754 ec.LoopEnd = ec.DefineLabel ();
3759 if (constant_section != null)
3760 constant_section.Block.Emit (ec);
3761 } else if (string_dictionary != null) {
3762 DoEmitStringSwitch (value, ec);
3764 TableSwitchEmit (ec, value);
3770 // Restore context state.
3771 ec.MarkLabel (ec.LoopEnd);
3774 // Restore the previous context
3776 ec.LoopEnd = old_end;
3777 ec.Switch = old_switch;
3780 protected override void CloneTo (CloneContext clonectx, Statement t)
3782 Switch target = (Switch) t;
3784 target.Expr = Expr.Clone (clonectx);
3785 target.Sections = new List<SwitchSection> ();
3786 foreach (SwitchSection ss in Sections){
3787 target.Sections.Add (ss.Clone (clonectx));
3792 // A place where execution can restart in an iterator
3793 public abstract class ResumableStatement : Statement
3796 protected Label resume_point;
3798 public Label PrepareForEmit (EmitContext ec)
3802 resume_point = ec.DefineLabel ();
3804 return resume_point;
3807 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3811 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3816 // Base class for statements that are implemented in terms of try...finally
3817 public abstract class ExceptionStatement : ResumableStatement
3821 List<ResumableStatement> resume_points;
3822 int first_resume_pc;
3824 protected abstract void EmitPreTryBody (EmitContext ec);
3825 protected abstract void EmitTryBody (EmitContext ec);
3826 protected abstract void EmitFinallyBody (EmitContext ec);
3828 protected sealed override void DoEmit (EmitContext ec)
3830 EmitPreTryBody (ec);
3832 if (resume_points != null) {
3833 ec.EmitInt ((int) Iterator.State.Running);
3834 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3837 ec.BeginExceptionBlock ();
3839 if (resume_points != null) {
3840 ec.MarkLabel (resume_point);
3842 // For normal control flow, we want to fall-through the Switch
3843 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3844 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3845 ec.EmitInt (first_resume_pc);
3846 ec.Emit (OpCodes.Sub);
3848 Label [] labels = new Label [resume_points.Count];
3849 for (int i = 0; i < resume_points.Count; ++i)
3850 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3851 ec.Emit (OpCodes.Switch, labels);
3856 ec.BeginFinallyBlock ();
3858 Label start_finally = ec.DefineLabel ();
3859 if (resume_points != null) {
3860 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3861 ec.Emit (OpCodes.Brfalse_S, start_finally);
3862 ec.Emit (OpCodes.Endfinally);
3865 ec.MarkLabel (start_finally);
3866 EmitFinallyBody (ec);
3868 ec.EndExceptionBlock ();
3871 public void SomeCodeFollows ()
3873 code_follows = true;
3876 public override bool Resolve (BlockContext ec)
3878 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3879 // So, ensure there's some IL code after this statement.
3880 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3881 ec.NeedReturnLabel ();
3883 iter = ec.CurrentIterator;
3887 public void AddResumePoint (ResumableStatement stmt, int pc)
3889 if (resume_points == null) {
3890 resume_points = new List<ResumableStatement> ();
3891 first_resume_pc = pc;
3894 if (pc != first_resume_pc + resume_points.Count)
3895 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3897 resume_points.Add (stmt);
3900 Label dispose_try_block;
3901 bool prepared_for_dispose, emitted_dispose;
3902 public override Label PrepareForDispose (EmitContext ec, Label end)
3904 if (!prepared_for_dispose) {
3905 prepared_for_dispose = true;
3906 dispose_try_block = ec.DefineLabel ();
3908 return dispose_try_block;
3911 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3913 if (emitted_dispose)
3916 emitted_dispose = true;
3918 Label end_of_try = ec.DefineLabel ();
3920 // Ensure that the only way we can get into this code is through a dispatcher
3921 if (have_dispatcher)
3922 ec.Emit (OpCodes.Br, end);
3924 ec.BeginExceptionBlock ();
3926 ec.MarkLabel (dispose_try_block);
3928 Label [] labels = null;
3929 for (int i = 0; i < resume_points.Count; ++i) {
3930 ResumableStatement s = (ResumableStatement) resume_points [i];
3931 Label ret = s.PrepareForDispose (ec, end_of_try);
3932 if (ret.Equals (end_of_try) && labels == null)
3934 if (labels == null) {
3935 labels = new Label [resume_points.Count];
3936 for (int j = 0; j < i; ++j)
3937 labels [j] = end_of_try;
3942 if (labels != null) {
3944 for (j = 1; j < labels.Length; ++j)
3945 if (!labels [0].Equals (labels [j]))
3947 bool emit_dispatcher = j < labels.Length;
3949 if (emit_dispatcher) {
3950 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3951 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3952 ec.EmitInt (first_resume_pc);
3953 ec.Emit (OpCodes.Sub);
3954 ec.Emit (OpCodes.Switch, labels);
3955 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3958 foreach (ResumableStatement s in resume_points)
3959 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3962 ec.MarkLabel (end_of_try);
3964 ec.BeginFinallyBlock ();
3966 EmitFinallyBody (ec);
3968 ec.EndExceptionBlock ();
3972 public class Lock : ExceptionStatement {
3974 public Statement Statement;
3975 TemporaryVariable temp;
3977 public Lock (Expression expr, Statement stmt, Location l)
3984 public override bool Resolve (BlockContext ec)
3986 expr = expr.Resolve (ec);
3990 if (!TypeManager.IsReferenceType (expr.Type)){
3991 ec.Report.Error (185, loc,
3992 "`{0}' is not a reference type as required by the lock statement",
3993 TypeManager.CSharpName (expr.Type));
3997 ec.StartFlowBranching (this);
3998 bool ok = Statement.Resolve (ec);
3999 ec.EndFlowBranching ();
4001 ok &= base.Resolve (ec);
4003 // Avoid creating libraries that reference the internal
4005 TypeSpec t = expr.Type;
4006 if (t == TypeManager.null_type)
4007 t = TypeManager.object_type;
4009 temp = new TemporaryVariable (t, loc);
4012 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4013 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4014 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4015 monitor_type, "Enter", loc, TypeManager.object_type);
4016 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4017 monitor_type, "Exit", loc, TypeManager.object_type);
4023 protected override void EmitPreTryBody (EmitContext ec)
4025 temp.EmitAssign (ec, expr);
4027 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4030 protected override void EmitTryBody (EmitContext ec)
4032 Statement.Emit (ec);
4035 protected override void EmitFinallyBody (EmitContext ec)
4038 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4041 protected override void CloneTo (CloneContext clonectx, Statement t)
4043 Lock target = (Lock) t;
4045 target.expr = expr.Clone (clonectx);
4046 target.Statement = Statement.Clone (clonectx);
4050 public class Unchecked : Statement {
4053 public Unchecked (Block b)
4059 public override bool Resolve (BlockContext ec)
4061 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4062 return Block.Resolve (ec);
4065 protected override void DoEmit (EmitContext ec)
4067 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4071 protected override void CloneTo (CloneContext clonectx, Statement t)
4073 Unchecked target = (Unchecked) t;
4075 target.Block = clonectx.LookupBlock (Block);
4079 public class Checked : Statement {
4082 public Checked (Block b)
4085 b.Unchecked = false;
4088 public override bool Resolve (BlockContext ec)
4090 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4091 return Block.Resolve (ec);
4094 protected override void DoEmit (EmitContext ec)
4096 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4100 protected override void CloneTo (CloneContext clonectx, Statement t)
4102 Checked target = (Checked) t;
4104 target.Block = clonectx.LookupBlock (Block);
4108 public class Unsafe : Statement {
4111 public Unsafe (Block b)
4114 Block.Unsafe = true;
4115 loc = b.StartLocation;
4118 public override bool Resolve (BlockContext ec)
4120 if (ec.CurrentIterator != null)
4121 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4123 using (ec.Set (ResolveContext.Options.UnsafeScope))
4124 return Block.Resolve (ec);
4127 protected override void DoEmit (EmitContext ec)
4132 protected override void CloneTo (CloneContext clonectx, Statement t)
4134 Unsafe target = (Unsafe) t;
4136 target.Block = clonectx.LookupBlock (Block);
4143 public class Fixed : Statement {
4145 List<KeyValuePair<LocalInfo, Expression>> declarators;
4146 Statement statement;
4151 abstract class Emitter
4153 protected LocalInfo vi;
4154 protected Expression converted;
4156 protected Emitter (Expression expr, LocalInfo li)
4162 public abstract void Emit (EmitContext ec);
4163 public abstract void EmitExit (EmitContext ec);
4166 class ExpressionEmitter : Emitter {
4167 public ExpressionEmitter (Expression converted, LocalInfo li) :
4168 base (converted, li)
4172 public override void Emit (EmitContext ec) {
4174 // Store pointer in pinned location
4176 converted.Emit (ec);
4180 public override void EmitExit (EmitContext ec)
4182 ec.Emit (OpCodes.Ldc_I4_0);
4183 ec.Emit (OpCodes.Conv_U);
4188 class StringEmitter : Emitter
4190 LocalInfo pinned_string;
4192 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4195 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4196 pinned_string.Pinned = true;
4199 public StringEmitter Resolve (ResolveContext rc)
4201 pinned_string.Resolve (rc);
4203 if (TypeManager.int_get_offset_to_string_data == null) {
4204 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4205 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4211 public override void Emit (EmitContext ec)
4213 pinned_string.ResolveVariable (ec);
4215 converted.Emit (ec);
4216 pinned_string.EmitAssign (ec);
4218 // TODO: Should use Binary::Add
4219 pinned_string.Emit (ec);
4220 ec.Emit (OpCodes.Conv_I);
4222 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4223 //pe.InstanceExpression = pinned_string;
4224 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4226 ec.Emit (OpCodes.Add);
4230 public override void EmitExit (EmitContext ec)
4232 ec.Emit (OpCodes.Ldnull);
4233 pinned_string.EmitAssign (ec);
4237 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4240 declarators = decls;
4245 public Statement Statement {
4246 get { return statement; }
4249 public override bool Resolve (BlockContext ec)
4252 Expression.UnsafeError (ec, loc);
4256 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4257 if (texpr == null) {
4258 if (type is VarExpr)
4259 ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4264 expr_type = texpr.Type;
4266 data = new Emitter [declarators.Count];
4268 if (!expr_type.IsPointer){
4269 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4274 foreach (var p in declarators){
4275 LocalInfo vi = p.Key;
4276 Expression e = p.Value;
4278 vi.VariableInfo.SetAssigned (ec);
4279 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4282 // The rules for the possible declarators are pretty wise,
4283 // but the production on the grammar is more concise.
4285 // So we have to enforce these rules here.
4287 // We do not resolve before doing the case 1 test,
4288 // because the grammar is explicit in that the token &
4289 // is present, so we need to test for this particular case.
4293 ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4297 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4307 if (e.Type.IsArray){
4308 TypeSpec array_type = TypeManager.GetElementType (e.Type);
4311 // Provided that array_type is unmanaged,
4313 if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4317 // and T* is implicitly convertible to the
4318 // pointer type given in the fixed statement.
4320 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4322 Expression converted = Convert.ImplicitConversionRequired (
4323 ec, array_ptr, vi.VariableType, loc);
4324 if (converted == null)
4328 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4330 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4331 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4332 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4333 new NullPointer (loc),
4336 converted = converted.Resolve (ec);
4338 data [i] = new ExpressionEmitter (converted, vi);
4347 if (e.Type == TypeManager.string_type){
4348 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4353 // Case 4: fixed buffer
4354 if (e is FixedBufferPtr) {
4355 data [i++] = new ExpressionEmitter (e, vi);
4360 // Case 1: & object.
4362 Unary u = e as Unary;
4363 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4364 IVariableReference vr = u.Expr as IVariableReference;
4365 if (vr == null || !vr.IsFixed) {
4366 data [i] = new ExpressionEmitter (e, vi);
4370 if (data [i++] == null)
4371 ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4373 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4376 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4377 bool ok = statement.Resolve (ec);
4378 bool flow_unreachable = ec.EndFlowBranching ();
4379 has_ret = flow_unreachable;
4384 protected override void DoEmit (EmitContext ec)
4386 for (int i = 0; i < data.Length; i++) {
4390 statement.Emit (ec);
4396 // Clear the pinned variable
4398 for (int i = 0; i < data.Length; i++) {
4399 data [i].EmitExit (ec);
4403 protected override void CloneTo (CloneContext clonectx, Statement t)
4405 Fixed target = (Fixed) t;
4407 target.type = type.Clone (clonectx);
4408 target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4409 foreach (var p in declarators) {
4410 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4411 clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4414 target.statement = statement.Clone (clonectx);
4418 public class Catch : Statement {
4419 public readonly string Name;
4421 public Block VarBlock;
4423 Expression type_expr;
4426 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4431 VarBlock = var_block;
4435 public TypeSpec CatchType {
4441 public bool IsGeneral {
4443 return type_expr == null;
4447 protected override void DoEmit (EmitContext ec)
4449 if (CatchType != null)
4450 ec.BeginCatchBlock (CatchType);
4452 ec.BeginCatchBlock (TypeManager.object_type);
4454 if (VarBlock != null)
4458 // TODO: Move to resolve
4459 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4460 lvr.Resolve (new ResolveContext (ec.MemberContext));
4462 // Only to make verifier happy
4463 if (TypeManager.IsGenericParameter (lvr.Type))
4464 ec.Emit (OpCodes.Unbox_Any, lvr.Type);
4467 if (lvr.IsHoisted) {
4468 LocalTemporary lt = new LocalTemporary (lvr.Type);
4472 // Variable is at the top of the stack
4473 source = EmptyExpression.Null;
4476 lvr.EmitAssign (ec, source, false, false);
4478 ec.Emit (OpCodes.Pop);
4483 public override bool Resolve (BlockContext ec)
4485 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4486 if (type_expr != null) {
4487 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4493 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4494 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4500 if (!Block.Resolve (ec))
4503 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4504 // emit the "unused variable" warnings.
4505 if (VarBlock != null)
4506 return VarBlock.Resolve (ec);
4512 protected override void CloneTo (CloneContext clonectx, Statement t)
4514 Catch target = (Catch) t;
4516 if (type_expr != null)
4517 target.type_expr = type_expr.Clone (clonectx);
4518 if (VarBlock != null)
4519 target.VarBlock = clonectx.LookupBlock (VarBlock);
4520 target.Block = clonectx.LookupBlock (Block);
4524 public class TryFinally : ExceptionStatement {
4528 public TryFinally (Statement stmt, Block fini, Location l)
4535 public override bool Resolve (BlockContext ec)
4539 ec.StartFlowBranching (this);
4541 if (!stmt.Resolve (ec))
4545 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4546 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4547 if (!fini.Resolve (ec))
4551 ec.EndFlowBranching ();
4553 ok &= base.Resolve (ec);
4558 protected override void EmitPreTryBody (EmitContext ec)
4562 protected override void EmitTryBody (EmitContext ec)
4567 protected override void EmitFinallyBody (EmitContext ec)
4572 protected override void CloneTo (CloneContext clonectx, Statement t)
4574 TryFinally target = (TryFinally) t;
4576 target.stmt = (Statement) stmt.Clone (clonectx);
4578 target.fini = clonectx.LookupBlock (fini);
4582 public class TryCatch : Statement {
4584 public List<Catch> Specific;
4585 public Catch General;
4586 bool inside_try_finally, code_follows;
4588 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4591 this.Specific = catch_clauses;
4592 this.inside_try_finally = inside_try_finally;
4594 Catch c = catch_clauses [0];
4597 catch_clauses.RemoveAt (0);
4603 public override bool Resolve (BlockContext ec)
4607 ec.StartFlowBranching (this);
4609 if (!Block.Resolve (ec))
4612 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4614 foreach (Catch c in Specific){
4615 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4617 if (c.Name != null) {
4618 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4620 throw new Exception ();
4622 vi.VariableInfo = null;
4625 if (!c.Resolve (ec)) {
4630 TypeSpec resolved_type = c.CatchType;
4631 for (int ii = 0; ii < last_index; ++ii) {
4632 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4633 ec.Report.Error (160, c.loc,
4634 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4635 TypeManager.CSharpName (prev_catches [ii]));
4640 prev_catches [last_index++] = resolved_type;
4643 if (General != null) {
4644 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4645 foreach (Catch c in Specific){
4646 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4647 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'");
4652 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4654 if (!General.Resolve (ec))
4658 ec.EndFlowBranching ();
4660 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4661 // So, ensure there's some IL code after this statement
4662 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4663 ec.NeedReturnLabel ();
4668 public void SomeCodeFollows ()
4670 code_follows = true;
4673 protected override void DoEmit (EmitContext ec)
4675 if (!inside_try_finally)
4676 ec.BeginExceptionBlock ();
4680 foreach (Catch c in Specific)
4683 if (General != null)
4686 if (!inside_try_finally)
4687 ec.EndExceptionBlock ();
4690 protected override void CloneTo (CloneContext clonectx, Statement t)
4692 TryCatch target = (TryCatch) t;
4694 target.Block = clonectx.LookupBlock (Block);
4695 if (General != null)
4696 target.General = (Catch) General.Clone (clonectx);
4697 if (Specific != null){
4698 target.Specific = new List<Catch> ();
4699 foreach (Catch c in Specific)
4700 target.Specific.Add ((Catch) c.Clone (clonectx));
4705 // FIXME: Why is it almost exact copy of Using ??
4706 public class UsingTemporary : ExceptionStatement {
4707 TemporaryVariable local_copy;
4708 public Statement Statement;
4712 public UsingTemporary (Expression expr, Statement stmt, Location l)
4719 public override bool Resolve (BlockContext ec)
4721 expr = expr.Resolve (ec);
4725 expr_type = expr.Type;
4727 if (!expr_type.ImplementsInterface (TypeManager.idisposable_type) &&
4728 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4729 if (expr_type != InternalType.Dynamic) {
4730 Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4734 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4735 expr_type = expr.Type;
4738 local_copy = new TemporaryVariable (expr_type, loc);
4739 local_copy.Resolve (ec);
4741 ec.StartFlowBranching (this);
4743 bool ok = Statement.Resolve (ec);
4745 ec.EndFlowBranching ();
4747 ok &= base.Resolve (ec);
4749 if (TypeManager.void_dispose_void == null) {
4750 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4751 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4757 protected override void EmitPreTryBody (EmitContext ec)
4759 local_copy.EmitAssign (ec, expr);
4762 protected override void EmitTryBody (EmitContext ec)
4764 Statement.Emit (ec);
4767 protected override void EmitFinallyBody (EmitContext ec)
4769 if (!TypeManager.IsStruct (expr_type)) {
4770 Label skip = ec.DefineLabel ();
4771 local_copy.Emit (ec);
4772 ec.Emit (OpCodes.Brfalse, skip);
4773 local_copy.Emit (ec);
4774 ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4775 ec.MarkLabel (skip);
4779 MethodSpec ms = MemberCache.FindMember (expr_type,
4780 MemberFilter.Method ("Dispose", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.void_type),
4781 BindingRestriction.InstanceOnly) as MethodSpec;
4784 local_copy.Emit (ec);
4785 ec.Emit (OpCodes.Box, expr_type);
4786 ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4790 local_copy.AddressOf (ec, AddressOp.Load);
4791 ec.Emit (OpCodes.Call, ms);
4794 protected override void CloneTo (CloneContext clonectx, Statement t)
4796 UsingTemporary target = (UsingTemporary) t;
4798 target.expr = expr.Clone (clonectx);
4799 target.Statement = Statement.Clone (clonectx);
4803 public class Using : ExceptionStatement {
4805 public Statement EmbeddedStatement {
4806 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4812 ExpressionStatement assign;
4814 public Using (Expression var, Expression init, Statement stmt, Location l)
4822 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4824 ec.Report.SymbolRelatedToPreviousError (expr.Type);
4825 ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4826 TypeManager.CSharpName (expr.Type));
4829 protected override void EmitPreTryBody (EmitContext ec)
4831 assign.EmitStatement (ec);
4834 protected override void EmitTryBody (EmitContext ec)
4839 protected override void EmitFinallyBody (EmitContext ec)
4841 Label skip = ec.DefineLabel ();
4843 bool emit_null_check = !TypeManager.IsValueType (var.Type);
4844 if (emit_null_check) {
4846 ec.Emit (OpCodes.Brfalse, skip);
4849 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
4851 if (emit_null_check)
4852 ec.MarkLabel (skip);
4855 public override bool Resolve (BlockContext ec)
4857 if (!ResolveVariable (ec))
4860 ec.StartFlowBranching (this);
4862 bool ok = stmt.Resolve (ec);
4864 ec.EndFlowBranching ();
4866 ok &= base.Resolve (ec);
4868 if (TypeManager.void_dispose_void == null) {
4869 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4870 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4876 bool ResolveVariable (BlockContext ec)
4878 assign = new SimpleAssign (var, init, loc);
4879 assign = assign.ResolveStatement (ec);
4883 if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4887 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
4889 if (assign.Type == InternalType.Dynamic) {
4890 e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
4891 var = new TemporaryVariable (e.Type, loc);
4892 assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
4896 Error_IsNotConvertibleToIDisposable (ec, var);
4900 throw new NotImplementedException ("covariance?");
4903 protected override void CloneTo (CloneContext clonectx, Statement t)
4905 Using target = (Using) t;
4907 target.var = var.Clone (clonectx);
4908 target.init = init.Clone (clonectx);
4909 target.stmt = stmt.Clone (clonectx);
4914 /// Implementation of the foreach C# statement
4916 public class Foreach : Statement {
4918 sealed class ArrayForeach : Statement
4920 class ArrayCounter : TemporaryVariable
4922 StatementExpression increment;
4924 public ArrayCounter (Location loc)
4925 : base (TypeManager.int32_type, loc)
4929 public void ResolveIncrement (BlockContext ec)
4931 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
4932 increment.Resolve (ec);
4935 public void EmitIncrement (EmitContext ec)
4937 increment.Emit (ec);
4941 readonly Foreach for_each;
4942 readonly Statement statement;
4945 TemporaryVariable[] lengths;
4946 Expression [] length_exprs;
4947 ArrayCounter[] counter;
4949 TemporaryVariable copy;
4952 public ArrayForeach (Foreach @foreach, int rank)
4954 for_each = @foreach;
4955 statement = for_each.statement;
4958 counter = new ArrayCounter [rank];
4959 length_exprs = new Expression [rank];
4962 // Only use temporary length variables when dealing with
4963 // multi-dimensional arrays
4966 lengths = new TemporaryVariable [rank];
4969 protected override void CloneTo (CloneContext clonectx, Statement target)
4971 throw new NotImplementedException ();
4974 public override bool Resolve (BlockContext ec)
4976 copy = new TemporaryVariable (for_each.expr.Type, loc);
4979 int rank = length_exprs.Length;
4980 Arguments list = new Arguments (rank);
4981 for (int i = 0; i < rank; i++) {
4982 counter [i] = new ArrayCounter (loc);
4983 counter [i].ResolveIncrement (ec);
4986 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
4988 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4989 lengths [i].Resolve (ec);
4991 Arguments args = new Arguments (1);
4992 args.Add (new Argument (new IntConstant (i, loc)));
4993 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
4996 list.Add (new Argument (counter [i]));
4999 access = new ElementAccess (copy, list).Resolve (ec);
5003 Expression var_type = for_each.type;
5004 VarExpr ve = var_type as VarExpr;
5006 // Infer implicitly typed local variable from foreach array type
5007 var_type = new TypeExpression (access.Type, ve.Location);
5010 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5011 if (var_type == null)
5014 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5020 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5021 ec.CurrentBranching.CreateSibling ();
5023 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5024 if (for_each.variable == null)
5027 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5028 if (!statement.Resolve (ec))
5030 ec.EndFlowBranching ();
5032 // There's no direct control flow from the end of the embedded statement to the end of the loop
5033 ec.CurrentBranching.CurrentUsageVector.Goto ();
5035 ec.EndFlowBranching ();
5040 protected override void DoEmit (EmitContext ec)
5042 copy.EmitAssign (ec, for_each.expr);
5044 int rank = length_exprs.Length;
5045 Label[] test = new Label [rank];
5046 Label[] loop = new Label [rank];
5048 for (int i = 0; i < rank; i++) {
5049 test [i] = ec.DefineLabel ();
5050 loop [i] = ec.DefineLabel ();
5052 if (lengths != null)
5053 lengths [i].EmitAssign (ec, length_exprs [i]);
5056 IntConstant zero = new IntConstant (0, loc);
5057 for (int i = 0; i < rank; i++) {
5058 counter [i].EmitAssign (ec, zero);
5060 ec.Emit (OpCodes.Br, test [i]);
5061 ec.MarkLabel (loop [i]);
5064 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5066 statement.Emit (ec);
5068 ec.MarkLabel (ec.LoopBegin);
5070 for (int i = rank - 1; i >= 0; i--){
5071 counter [i].EmitIncrement (ec);
5073 ec.MarkLabel (test [i]);
5074 counter [i].Emit (ec);
5076 if (lengths != null)
5077 lengths [i].Emit (ec);
5079 length_exprs [i].Emit (ec);
5081 ec.Emit (OpCodes.Blt, loop [i]);
5084 ec.MarkLabel (ec.LoopEnd);
5088 sealed class CollectionForeach : Statement
5090 class CollectionForeachStatement : Statement
5093 Expression variable, current, conv;
5094 Statement statement;
5097 public CollectionForeachStatement (TypeSpec type, Expression variable,
5098 Expression current, Statement statement,
5102 this.variable = variable;
5103 this.current = current;
5104 this.statement = statement;
5108 protected override void CloneTo (CloneContext clonectx, Statement target)
5110 throw new NotImplementedException ();
5113 public override bool Resolve (BlockContext ec)
5115 current = current.Resolve (ec);
5116 if (current == null)
5119 conv = Convert.ExplicitConversion (ec, current, type, loc);
5123 assign = new SimpleAssign (variable, conv, loc);
5124 if (assign.Resolve (ec) == null)
5127 if (!statement.Resolve (ec))
5133 protected override void DoEmit (EmitContext ec)
5135 assign.EmitStatement (ec);
5136 statement.Emit (ec);
5140 Expression variable, expr;
5141 Statement statement;
5143 TemporaryVariable enumerator;
5148 MethodGroupExpr get_enumerator;
5149 PropertyExpr get_current;
5150 MethodSpec move_next;
5151 Expression var_type;
5152 TypeSpec enumerator_type;
5153 bool enumerator_found;
5155 public CollectionForeach (Expression var_type, Expression var,
5156 Expression expr, Statement stmt, Location l)
5158 this.var_type = var_type;
5159 this.variable = var;
5165 protected override void CloneTo (CloneContext clonectx, Statement target)
5167 throw new NotImplementedException ();
5170 bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi)
5172 TypeSpec return_type = mi.ReturnType;
5175 // Ok, we can access it, now make sure that we can do something
5176 // with this `GetEnumerator'
5179 if (return_type == TypeManager.ienumerator_type ||
5180 return_type.ImplementsInterface (TypeManager.ienumerator_type)) {
5182 // If it is not an interface, lets try to find the methods ourselves.
5183 // For example, if we have:
5184 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5185 // We can avoid the iface call. This is a runtime perf boost.
5186 // even bigger if we have a ValueType, because we avoid the cost
5189 // We have to make sure that both methods exist for us to take
5190 // this path. If one of the methods does not exist, we will just
5191 // use the interface. Sadly, this complex if statement is the only
5192 // way I could do this without a goto
5195 if (TypeManager.bool_movenext_void == null) {
5196 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5197 TypeManager.ienumerator_type, "MoveNext", loc, TypeSpec.EmptyTypes);
5200 if (TypeManager.ienumerator_getcurrent == null) {
5201 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5202 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5206 // Prefer a generic enumerator over a non-generic one.
5208 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5209 enumerator_type = return_type;
5210 if (!FetchGetCurrent (ec, return_type))
5211 get_current = new PropertyExpr (
5212 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5213 if (!FetchMoveNext (return_type))
5214 move_next = TypeManager.bool_movenext_void;
5218 if (return_type.IsInterface ||
5219 !FetchMoveNext (return_type) ||
5220 !FetchGetCurrent (ec, return_type)) {
5221 enumerator_type = return_type;
5222 move_next = TypeManager.bool_movenext_void;
5223 get_current = new PropertyExpr (
5224 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5229 // Ok, so they dont return an IEnumerable, we will have to
5230 // find if they support the GetEnumerator pattern.
5233 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5234 ec.Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5235 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5240 enumerator_type = return_type;
5246 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5248 bool FetchMoveNext (TypeSpec t)
5250 move_next = MemberCache.FindMember (t,
5251 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5252 BindingRestriction.InstanceOnly) as MethodSpec;
5254 return move_next != null && (move_next.Modifiers & Modifiers.PUBLIC) != 0;
5258 // Retrieves a `public T get_Current ()' method from the Type `t'
5260 bool FetchGetCurrent (ResolveContext ec, TypeSpec t)
5262 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5263 ec.CurrentType, t, "Current", 0, MemberKind.Property,
5264 BindingRestriction.AccessibleOnly, loc) as PropertyExpr;
5272 void Error_Enumerator (BlockContext ec)
5274 if (enumerator_found) {
5278 ec.Report.Error (1579, loc,
5279 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5280 TypeManager.CSharpName (expr.Type));
5283 bool TryType (ResolveContext ec, TypeSpec t)
5285 var mg = Expression.MemberLookup (ec.Compiler, ec.CurrentType, null, t, "GetEnumerator", 0,
5286 MemberKind.Method, BindingRestriction.NoOverrides | BindingRestriction.InstanceOnly, loc) as MethodGroupExpr;
5291 MethodSpec result = null;
5292 MethodSpec tmp_move_next = null;
5293 PropertyExpr tmp_get_cur = null;
5294 TypeSpec tmp_enumerator_type = enumerator_type;
5295 foreach (MethodSpec mi in mg.Methods) {
5296 if (!mi.Parameters.IsEmpty)
5299 // Check whether GetEnumerator is public
5300 if ((mi.Modifiers & Modifiers.AccessibilityMask) != Modifiers.PUBLIC)
5303 enumerator_found = true;
5305 if (!GetEnumeratorFilter (ec, mi))
5308 if (result != null) {
5309 if (TypeManager.IsGenericType (result.ReturnType)) {
5310 if (!TypeManager.IsGenericType (mi.ReturnType))
5313 ec.Report.SymbolRelatedToPreviousError (t);
5314 ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5315 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5316 TypeManager.CSharpName (t), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5320 // Always prefer generics enumerators
5321 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5322 if (mi.DeclaringType.ImplementsInterface (result.DeclaringType) ||
5323 result.DeclaringType.ImplementsInterface (mi.DeclaringType))
5326 ec.Report.SymbolRelatedToPreviousError (result);
5327 ec.Report.SymbolRelatedToPreviousError (mi);
5328 ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5329 TypeManager.CSharpName (t), "enumerable", result.GetSignatureForError (), mi.GetSignatureForError ());
5334 tmp_move_next = move_next;
5335 tmp_get_cur = get_current;
5336 tmp_enumerator_type = enumerator_type;
5337 if (mi.DeclaringType == t)
5341 if (result != null) {
5342 move_next = tmp_move_next;
5343 get_current = tmp_get_cur;
5344 enumerator_type = tmp_enumerator_type;
5345 get_enumerator = new MethodGroupExpr (result, enumerator_type, loc);
5347 if (t != expr.Type) {
5348 expr = Convert.ExplicitConversion (
5351 throw new InternalErrorException ();
5354 get_enumerator.InstanceExpression = expr;
5355 get_enumerator.IsBase = t != expr.Type;
5363 bool ProbeCollectionType (ResolveContext ec, TypeSpec t)
5365 int errors = ec.Report.Errors;
5366 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type;){
5367 if (TryType (ec, tt))
5372 if (ec.Report.Errors > errors)
5376 // Now try to find the method in the interfaces
5378 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type; ) {
5379 if (tt.Interfaces != null) {
5380 foreach (TypeSpec i in tt.Interfaces) {
5381 if (TryType (ec, i))
5391 public override bool Resolve (BlockContext ec)
5393 enumerator_type = TypeManager.ienumerator_type;
5395 bool is_dynamic = expr.Type == InternalType.Dynamic;
5397 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5399 if (!ProbeCollectionType (ec, expr.Type)) {
5400 Error_Enumerator (ec);
5404 VarExpr ve = var_type as VarExpr;
5406 // Infer implicitly typed local variable from foreach enumerable type
5407 var_type = new TypeExpression (
5408 is_dynamic ? InternalType.Dynamic : get_current.Type,
5412 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5413 if (var_type == null)
5416 enumerator = new TemporaryVariable (enumerator_type, loc);
5417 enumerator.Resolve (ec);
5419 init = new Invocation (get_enumerator, null);
5420 init = init.Resolve (ec);
5424 Expression move_next_expr;
5426 var mi = new List<MemberSpec> (1) { move_next };
5427 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5428 mg.InstanceExpression = enumerator;
5430 move_next_expr = new Invocation (mg, null);
5433 get_current.InstanceExpression = enumerator;
5435 Statement block = new CollectionForeachStatement (
5436 var_type.Type, variable, get_current, statement, loc);
5438 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5441 bool implements_idisposable = enumerator_type.ImplementsInterface (TypeManager.idisposable_type);
5442 if (implements_idisposable || !enumerator_type.IsSealed) {
5443 wrapper = new DisposableWrapper (this, implements_idisposable);
5445 wrapper = new NonDisposableWrapper (this);
5448 return wrapper.Resolve (ec);
5451 protected override void DoEmit (EmitContext ec)
5456 class NonDisposableWrapper : Statement {
5457 CollectionForeach parent;
5459 internal NonDisposableWrapper (CollectionForeach parent)
5461 this.parent = parent;
5464 protected override void CloneTo (CloneContext clonectx, Statement target)
5466 throw new NotSupportedException ();
5469 public override bool Resolve (BlockContext ec)
5471 return parent.ResolveLoop (ec);
5474 protected override void DoEmit (EmitContext ec)
5476 parent.EmitLoopInit (ec);
5477 parent.EmitLoopBody (ec);
5481 sealed class DisposableWrapper : ExceptionStatement
5483 CollectionForeach parent;
5484 bool implements_idisposable;
5486 internal DisposableWrapper (CollectionForeach parent, bool implements)
5488 this.parent = parent;
5489 this.implements_idisposable = implements;
5492 protected override void CloneTo (CloneContext clonectx, Statement target)
5494 throw new NotSupportedException ();
5497 public override bool Resolve (BlockContext ec)
5501 ec.StartFlowBranching (this);
5503 if (!parent.ResolveLoop (ec))
5506 ec.EndFlowBranching ();
5508 ok &= base.Resolve (ec);
5510 if (TypeManager.void_dispose_void == null) {
5511 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5512 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5517 protected override void EmitPreTryBody (EmitContext ec)
5519 parent.EmitLoopInit (ec);
5522 protected override void EmitTryBody (EmitContext ec)
5524 parent.EmitLoopBody (ec);
5527 protected override void EmitFinallyBody (EmitContext ec)
5529 Expression instance = parent.enumerator;
5530 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5532 parent.enumerator.Emit (ec);
5534 Label call_dispose = ec.DefineLabel ();
5536 if (!implements_idisposable) {
5537 ec.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5538 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5544 ec.Emit (OpCodes.Brtrue_S, call_dispose);
5546 // using 'endfinally' to empty the evaluation stack
5547 ec.Emit (OpCodes.Endfinally);
5548 ec.MarkLabel (call_dispose);
5551 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5555 bool ResolveLoop (BlockContext ec)
5557 return loop.Resolve (ec);
5560 void EmitLoopInit (EmitContext ec)
5562 enumerator.EmitAssign (ec, init);
5565 void EmitLoopBody (EmitContext ec)
5572 Expression variable;
5574 Statement statement;
5576 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5577 Statement stmt, Location l)
5580 this.variable = var;
5586 public Statement Statement {
5587 get { return statement; }
5590 public override bool Resolve (BlockContext ec)
5592 expr = expr.Resolve (ec);
5597 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5601 if (expr.Type == TypeManager.string_type) {
5602 statement = new ArrayForeach (this, 1);
5603 } else if (expr.Type is ArrayContainer) {
5604 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5606 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5607 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5608 expr.ExprClassName);
5612 statement = new CollectionForeach (type, variable, expr, statement, loc);
5615 return statement.Resolve (ec);
5618 protected override void DoEmit (EmitContext ec)
5620 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5621 ec.LoopBegin = ec.DefineLabel ();
5622 ec.LoopEnd = ec.DefineLabel ();
5624 statement.Emit (ec);
5626 ec.LoopBegin = old_begin;
5627 ec.LoopEnd = old_end;
5630 protected override void CloneTo (CloneContext clonectx, Statement t)
5632 Foreach target = (Foreach) t;
5634 target.type = type.Clone (clonectx);
5635 target.variable = variable.Clone (clonectx);
5636 target.expr = expr.Clone (clonectx);
5637 target.statement = statement.Clone (clonectx);