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.
14 using System.Collections.Generic;
17 using IKVM.Reflection.Emit;
19 using System.Reflection.Emit;
22 namespace Mono.CSharp {
24 public abstract class Statement {
28 /// Resolves the statement, true means that all sub-statements
31 public virtual bool Resolve (BlockContext ec)
37 /// We already know that the statement is unreachable, but we still
38 /// need to resolve it to catch errors.
40 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
43 // This conflicts with csc's way of doing this, but IMHO it's
44 // the right thing to do.
46 // If something is unreachable, we still check whether it's
47 // correct. This means that you cannot use unassigned variables
48 // in unreachable code, for instance.
52 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
54 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
55 bool ok = Resolve (ec);
56 ec.KillFlowBranching ();
62 /// Return value indicates whether all code paths emitted return.
64 protected abstract void DoEmit (EmitContext ec);
66 public virtual void Emit (EmitContext ec)
73 // This routine must be overrided in derived classes and make copies
74 // of all the data that might be modified if resolved
76 protected abstract void CloneTo (CloneContext clonectx, Statement target);
78 public Statement Clone (CloneContext clonectx)
80 Statement s = (Statement) this.MemberwiseClone ();
81 CloneTo (clonectx, s);
85 public virtual Expression CreateExpressionTree (ResolveContext ec)
87 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
91 public Statement PerformClone ()
93 CloneContext clonectx = new CloneContext ();
95 return Clone (clonectx);
99 public sealed class EmptyStatement : Statement
101 public EmptyStatement (Location loc)
106 public override bool Resolve (BlockContext ec)
111 public override bool ResolveUnreachable (BlockContext ec, bool warn)
116 public override void Emit (EmitContext ec)
120 protected override void DoEmit (EmitContext ec)
122 throw new NotSupportedException ();
125 protected override void CloneTo (CloneContext clonectx, Statement target)
131 public class If : Statement {
133 public Statement TrueStatement;
134 public Statement FalseStatement;
138 public If (Expression bool_expr, Statement true_statement, Location l)
139 : this (bool_expr, true_statement, null, l)
143 public If (Expression bool_expr,
144 Statement true_statement,
145 Statement false_statement,
148 this.expr = bool_expr;
149 TrueStatement = true_statement;
150 FalseStatement = false_statement;
154 public override bool Resolve (BlockContext ec)
158 Report.Debug (1, "START IF BLOCK", loc);
160 expr = expr.Resolve (ec);
165 // Dead code elimination
167 if (expr is Constant) {
168 bool take = !((Constant) expr).IsDefaultValue;
171 if (!TrueStatement.Resolve (ec))
174 if ((FalseStatement != null) &&
175 !FalseStatement.ResolveUnreachable (ec, true))
177 FalseStatement = null;
179 if (!TrueStatement.ResolveUnreachable (ec, true))
181 TrueStatement = null;
183 if ((FalseStatement != null) &&
184 !FalseStatement.Resolve (ec))
192 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
194 ok &= TrueStatement.Resolve (ec);
196 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
198 ec.CurrentBranching.CreateSibling ();
200 if (FalseStatement != null)
201 ok &= FalseStatement.Resolve (ec);
203 ec.EndFlowBranching ();
205 Report.Debug (1, "END IF BLOCK", loc);
210 protected override void DoEmit (EmitContext ec)
212 Label false_target = ec.DefineLabel ();
216 // If we're a boolean constant, Resolve() already
217 // eliminated dead code for us.
219 Constant c = expr as Constant;
221 c.EmitSideEffect (ec);
223 if (!c.IsDefaultValue)
224 TrueStatement.Emit (ec);
225 else if (FalseStatement != null)
226 FalseStatement.Emit (ec);
231 expr.EmitBranchable (ec, false_target, false);
233 TrueStatement.Emit (ec);
235 if (FalseStatement != null){
236 bool branch_emitted = false;
238 end = ec.DefineLabel ();
240 ec.Emit (OpCodes.Br, end);
241 branch_emitted = true;
244 ec.MarkLabel (false_target);
245 FalseStatement.Emit (ec);
250 ec.MarkLabel (false_target);
254 protected override void CloneTo (CloneContext clonectx, Statement t)
258 target.expr = expr.Clone (clonectx);
259 target.TrueStatement = TrueStatement.Clone (clonectx);
260 if (FalseStatement != null)
261 target.FalseStatement = FalseStatement.Clone (clonectx);
265 public class Do : Statement {
266 public Expression expr;
267 public Statement EmbeddedStatement;
269 public Do (Statement statement, BooleanExpression bool_expr, Location l)
272 EmbeddedStatement = statement;
276 public override bool Resolve (BlockContext ec)
280 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
282 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
284 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
285 if (!EmbeddedStatement.Resolve (ec))
287 ec.EndFlowBranching ();
289 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
290 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
292 expr = expr.Resolve (ec);
295 else if (expr is Constant){
296 bool infinite = !((Constant) expr).IsDefaultValue;
298 ec.CurrentBranching.CurrentUsageVector.Goto ();
301 ec.EndFlowBranching ();
306 protected override void DoEmit (EmitContext ec)
308 Label loop = ec.DefineLabel ();
309 Label old_begin = ec.LoopBegin;
310 Label old_end = ec.LoopEnd;
312 ec.LoopBegin = ec.DefineLabel ();
313 ec.LoopEnd = ec.DefineLabel ();
316 EmbeddedStatement.Emit (ec);
317 ec.MarkLabel (ec.LoopBegin);
320 // Dead code elimination
322 if (expr is Constant){
323 bool res = !((Constant) expr).IsDefaultValue;
325 expr.EmitSideEffect (ec);
327 ec.Emit (OpCodes.Br, loop);
329 expr.EmitBranchable (ec, loop, true);
331 ec.MarkLabel (ec.LoopEnd);
333 ec.LoopBegin = old_begin;
334 ec.LoopEnd = old_end;
337 protected override void CloneTo (CloneContext clonectx, Statement t)
341 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
342 target.expr = expr.Clone (clonectx);
346 public class While : Statement {
347 public Expression expr;
348 public Statement Statement;
349 bool infinite, empty;
351 public While (BooleanExpression bool_expr, Statement statement, Location l)
353 this.expr = bool_expr;
354 Statement = statement;
358 public override bool Resolve (BlockContext ec)
362 expr = expr.Resolve (ec);
367 // Inform whether we are infinite or not
369 if (expr is Constant){
370 bool value = !((Constant) expr).IsDefaultValue;
373 if (!Statement.ResolveUnreachable (ec, true))
381 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
383 ec.CurrentBranching.CreateSibling ();
385 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
386 if (!Statement.Resolve (ec))
388 ec.EndFlowBranching ();
390 // There's no direct control flow from the end of the embedded statement to the end of the loop
391 ec.CurrentBranching.CurrentUsageVector.Goto ();
393 ec.EndFlowBranching ();
398 protected override void DoEmit (EmitContext ec)
401 expr.EmitSideEffect (ec);
405 Label old_begin = ec.LoopBegin;
406 Label old_end = ec.LoopEnd;
408 ec.LoopBegin = ec.DefineLabel ();
409 ec.LoopEnd = ec.DefineLabel ();
412 // Inform whether we are infinite or not
414 if (expr is Constant){
415 // expr is 'true', since the 'empty' case above handles the 'false' case
416 ec.MarkLabel (ec.LoopBegin);
417 expr.EmitSideEffect (ec);
419 ec.Emit (OpCodes.Br, ec.LoopBegin);
422 // Inform that we are infinite (ie, `we return'), only
423 // if we do not `break' inside the code.
425 ec.MarkLabel (ec.LoopEnd);
427 Label while_loop = ec.DefineLabel ();
429 ec.Emit (OpCodes.Br, ec.LoopBegin);
430 ec.MarkLabel (while_loop);
434 ec.MarkLabel (ec.LoopBegin);
437 expr.EmitBranchable (ec, while_loop, true);
439 ec.MarkLabel (ec.LoopEnd);
442 ec.LoopBegin = old_begin;
443 ec.LoopEnd = old_end;
446 public override void Emit (EmitContext ec)
451 protected override void CloneTo (CloneContext clonectx, Statement t)
453 While target = (While) t;
455 target.expr = expr.Clone (clonectx);
456 target.Statement = Statement.Clone (clonectx);
460 public class For : Statement {
462 Statement InitStatement;
464 public Statement Statement;
465 bool infinite, empty;
467 public For (Statement init_statement,
468 BooleanExpression test,
473 InitStatement = init_statement;
475 Increment = increment;
476 Statement = statement;
480 public override bool Resolve (BlockContext ec)
484 if (InitStatement != null){
485 if (!InitStatement.Resolve (ec))
490 Test = Test.Resolve (ec);
493 else if (Test is Constant){
494 bool value = !((Constant) Test).IsDefaultValue;
497 if (!Statement.ResolveUnreachable (ec, true))
499 if ((Increment != null) &&
500 !Increment.ResolveUnreachable (ec, false))
510 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
512 ec.CurrentBranching.CreateSibling ();
514 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
516 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
517 if (!Statement.Resolve (ec))
519 ec.EndFlowBranching ();
521 if (Increment != null){
522 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
523 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
526 if (!Increment.Resolve (ec))
531 // There's no direct control flow from the end of the embedded statement to the end of the loop
532 ec.CurrentBranching.CurrentUsageVector.Goto ();
534 ec.EndFlowBranching ();
539 protected override void DoEmit (EmitContext ec)
541 if (InitStatement != null)
542 InitStatement.Emit (ec);
545 Test.EmitSideEffect (ec);
549 Label old_begin = ec.LoopBegin;
550 Label old_end = ec.LoopEnd;
551 Label loop = ec.DefineLabel ();
552 Label test = ec.DefineLabel ();
554 ec.LoopBegin = ec.DefineLabel ();
555 ec.LoopEnd = ec.DefineLabel ();
557 ec.Emit (OpCodes.Br, test);
561 ec.MarkLabel (ec.LoopBegin);
566 // If test is null, there is no test, and we are just
571 // The Resolve code already catches the case for
572 // Test == Constant (false) so we know that
575 if (Test is Constant) {
576 Test.EmitSideEffect (ec);
577 ec.Emit (OpCodes.Br, loop);
579 Test.EmitBranchable (ec, loop, true);
583 ec.Emit (OpCodes.Br, loop);
584 ec.MarkLabel (ec.LoopEnd);
586 ec.LoopBegin = old_begin;
587 ec.LoopEnd = old_end;
590 protected override void CloneTo (CloneContext clonectx, Statement t)
592 For target = (For) t;
594 if (InitStatement != null)
595 target.InitStatement = InitStatement.Clone (clonectx);
597 target.Test = Test.Clone (clonectx);
598 if (Increment != null)
599 target.Increment = Increment.Clone (clonectx);
600 target.Statement = Statement.Clone (clonectx);
604 public class StatementExpression : Statement {
605 ExpressionStatement expr;
607 public StatementExpression (ExpressionStatement expr)
613 public override bool Resolve (BlockContext ec)
615 expr = expr.ResolveStatement (ec);
619 protected override void DoEmit (EmitContext ec)
621 expr.EmitStatement (ec);
624 public override string ToString ()
626 return "StatementExpression (" + expr + ")";
629 protected override void CloneTo (CloneContext clonectx, Statement t)
631 StatementExpression target = (StatementExpression) t;
633 target.expr = (ExpressionStatement) expr.Clone (clonectx);
638 // Simple version of statement list not requiring a block
640 public class StatementList : Statement
642 List<Statement> statements;
644 public StatementList (Statement first, Statement second)
646 statements = new List<Statement> () { first, second };
650 public IList<Statement> Statements {
657 public void Add (Statement statement)
659 statements.Add (statement);
662 public override bool Resolve (BlockContext ec)
664 foreach (var s in statements)
670 protected override void DoEmit (EmitContext ec)
672 foreach (var s in statements)
676 protected override void CloneTo (CloneContext clonectx, Statement target)
678 StatementList t = (StatementList) target;
680 t.statements = new List<Statement> (statements.Count);
681 foreach (Statement s in statements)
682 t.statements.Add (s.Clone (clonectx));
686 // A 'return' or a 'yield break'
687 public abstract class ExitStatement : Statement
689 protected bool unwind_protect;
690 protected abstract bool DoResolve (BlockContext ec);
692 public virtual void Error_FinallyClause (Report Report)
694 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
697 public sealed override bool Resolve (BlockContext ec)
702 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
704 ec.NeedReturnLabel ();
705 ec.CurrentBranching.CurrentUsageVector.Goto ();
711 /// Implements the return statement
713 public class Return : ExitStatement
715 protected Expression Expr;
716 public Return (Expression expr, Location l)
723 public Expression Expression {
730 protected override bool DoResolve (BlockContext ec)
733 if (ec.ReturnType == TypeManager.void_type)
736 if (ec.CurrentIterator != null) {
737 Error_ReturnFromIterator (ec);
739 ec.Report.Error (126, loc,
740 "An object of a type convertible to `{0}' is required for the return statement",
741 ec.ReturnType.GetSignatureForError ());
747 Expr = Expr.Resolve (ec);
749 AnonymousExpression am = ec.CurrentAnonymousMethod;
751 if (ec.ReturnType == TypeManager.void_type) {
752 ec.Report.Error (127, loc,
753 "`{0}': A return keyword must not be followed by any expression when method returns void",
754 ec.GetSignatureForError ());
758 Error_ReturnFromIterator (ec);
762 var l = am as AnonymousMethodBody;
763 if (l != null && l.ReturnTypeInference != null && Expr != null) {
764 l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
772 if (Expr.Type != ec.ReturnType) {
773 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
777 ec.Report.Error (1662, loc,
778 "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",
779 am.ContainerType, am.GetSignatureForError ());
788 protected override void DoEmit (EmitContext ec)
794 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
798 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
800 ec.Emit (OpCodes.Ret);
803 void Error_ReturnFromIterator (ResolveContext rc)
805 rc.Report.Error (1622, loc,
806 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
809 protected override void CloneTo (CloneContext clonectx, Statement t)
811 Return target = (Return) t;
812 // It's null for simple return;
814 target.Expr = Expr.Clone (clonectx);
818 public class Goto : Statement {
820 LabeledStatement label;
823 public override bool Resolve (BlockContext ec)
825 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
826 ec.CurrentBranching.CurrentUsageVector.Goto ();
830 public Goto (string label, Location l)
836 public string Target {
837 get { return target; }
840 public void SetResolvedTarget (LabeledStatement label)
843 label.AddReference ();
846 protected override void CloneTo (CloneContext clonectx, Statement target)
851 protected override void DoEmit (EmitContext ec)
854 throw new InternalErrorException ("goto emitted before target resolved");
855 Label l = label.LabelTarget (ec);
856 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
860 public class LabeledStatement : Statement {
867 FlowBranching.UsageVector vectors;
869 public LabeledStatement (string name, Block block, Location l)
876 public Label LabelTarget (EmitContext ec)
881 label = ec.DefineLabel ();
896 public bool IsDefined {
897 get { return defined; }
900 public bool HasBeenReferenced {
901 get { return referenced; }
904 public FlowBranching.UsageVector JumpOrigins {
905 get { return vectors; }
908 public void AddUsageVector (FlowBranching.UsageVector vector)
910 vector = vector.Clone ();
911 vector.Next = vectors;
915 protected override void CloneTo (CloneContext clonectx, Statement target)
920 public override bool Resolve (BlockContext ec)
922 // this flow-branching will be terminated when the surrounding block ends
923 ec.StartFlowBranching (this);
927 protected override void DoEmit (EmitContext ec)
929 if (!HasBeenReferenced)
930 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
933 ec.MarkLabel (label);
936 public void AddReference ()
944 /// `goto default' statement
946 public class GotoDefault : Statement {
948 public GotoDefault (Location l)
953 protected override void CloneTo (CloneContext clonectx, Statement target)
958 public override bool Resolve (BlockContext ec)
960 ec.CurrentBranching.CurrentUsageVector.Goto ();
962 if (ec.Switch == null) {
963 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
967 if (!ec.Switch.GotDefault) {
968 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
975 protected override void DoEmit (EmitContext ec)
977 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
982 /// `goto case' statement
984 public class GotoCase : Statement {
988 public GotoCase (Expression e, Location l)
994 public override bool Resolve (BlockContext ec)
996 if (ec.Switch == null){
997 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1001 ec.CurrentBranching.CurrentUsageVector.Goto ();
1003 expr = expr.Resolve (ec);
1007 Constant c = expr as Constant;
1009 ec.Report.Error (150, expr.Location, "A constant value is expected");
1013 TypeSpec type = ec.Switch.SwitchType;
1014 Constant res = c.TryReduce (ec, type, c.Location);
1016 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1020 if (!Convert.ImplicitStandardConversionExists (c, type))
1021 ec.Report.Warning (469, 2, loc,
1022 "The `goto case' value is not implicitly convertible to type `{0}'",
1023 TypeManager.CSharpName (type));
1025 object val = res.GetValue ();
1027 val = SwitchLabel.NullStringCase;
1029 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1030 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1031 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1038 protected override void DoEmit (EmitContext ec)
1040 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1043 protected override void CloneTo (CloneContext clonectx, Statement t)
1045 GotoCase target = (GotoCase) t;
1047 target.expr = expr.Clone (clonectx);
1051 public class Throw : Statement {
1054 public Throw (Expression expr, Location l)
1060 public override bool Resolve (BlockContext ec)
1063 ec.CurrentBranching.CurrentUsageVector.Goto ();
1064 return ec.CurrentBranching.CheckRethrow (loc);
1067 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1068 ec.CurrentBranching.CurrentUsageVector.Goto ();
1073 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1074 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1076 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1081 protected override void DoEmit (EmitContext ec)
1084 ec.Emit (OpCodes.Rethrow);
1088 ec.Emit (OpCodes.Throw);
1092 protected override void CloneTo (CloneContext clonectx, Statement t)
1094 Throw target = (Throw) t;
1097 target.expr = expr.Clone (clonectx);
1101 public class Break : Statement {
1103 public Break (Location l)
1108 bool unwind_protect;
1110 public override bool Resolve (BlockContext ec)
1112 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1113 ec.CurrentBranching.CurrentUsageVector.Goto ();
1117 protected override void DoEmit (EmitContext ec)
1119 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1122 protected override void CloneTo (CloneContext clonectx, Statement t)
1128 public class Continue : Statement {
1130 public Continue (Location l)
1135 bool unwind_protect;
1137 public override bool Resolve (BlockContext ec)
1139 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1140 ec.CurrentBranching.CurrentUsageVector.Goto ();
1144 protected override void DoEmit (EmitContext ec)
1146 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1149 protected override void CloneTo (CloneContext clonectx, Statement t)
1155 public interface ILocalVariable
1157 void Emit (EmitContext ec);
1158 void EmitAssign (EmitContext ec);
1159 void EmitAddressOf (EmitContext ec);
1162 public interface INamedBlockVariable
1164 Block Block { get; }
1165 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1166 bool IsDeclared { get; }
1167 Location Location { get; }
1170 public class BlockVariableDeclaration : Statement
1172 public class Declarator
1175 Expression initializer;
1177 public Declarator (LocalVariable li, Expression initializer)
1179 if (li.Type != null)
1180 throw new ArgumentException ("Expected null variable type");
1183 this.initializer = initializer;
1186 public Declarator (Declarator clone, Expression initializer)
1189 this.initializer = initializer;
1194 public LocalVariable Variable {
1200 public Expression Initializer {
1205 initializer = value;
1212 Expression initializer;
1213 protected FullNamedExpression type_expr;
1214 protected LocalVariable li;
1215 protected List<Declarator> declarators;
1217 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1219 this.type_expr = type;
1221 this.loc = type_expr.Location;
1224 protected BlockVariableDeclaration (LocalVariable li)
1231 public List<Declarator> Declarators {
1237 public Expression Initializer {
1242 initializer = value;
1246 public FullNamedExpression TypeExpression {
1252 public LocalVariable Variable {
1260 public void AddDeclarator (Declarator decl)
1262 if (declarators == null)
1263 declarators = new List<Declarator> ();
1265 declarators.Add (decl);
1268 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1270 var container = bc.CurrentMemberDefinition.Parent;
1272 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1273 new MemberName (li.Name, li.Location), null);
1275 container.AddField (f);
1277 Evaluator.QueueField (f);
1279 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1283 public override bool Resolve (BlockContext bc)
1285 if (li.Type == null) {
1286 TypeSpec type = null;
1287 if (type_expr is VarExpr) {
1289 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1290 // same name exists or as a keyword when no type was found
1292 var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
1293 if (texpr == null) {
1294 if (RootContext.Version < LanguageVersion.V_3)
1295 bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
1298 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1302 if (li.IsConstant) {
1303 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1307 if (Initializer == null) {
1308 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1312 if (declarators != null) {
1313 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1317 Initializer = Initializer.Resolve (bc);
1318 if (Initializer != null) {
1319 ((VarExpr) type_expr).InferType (bc, Initializer);
1320 type = type_expr.Type;
1326 var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
1332 if (li.IsConstant && !type.IsConstantCompatible) {
1333 Const.Error_InvalidConstantType (type, loc, bc.Report);
1338 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1340 if (type.IsPointer && !bc.IsUnsafe)
1341 Expression.UnsafeError (bc, loc);
1346 bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
1348 CreateEvaluatorVariable (bc, li);
1350 li.PrepareForFlowAnalysis (bc);
1353 if (initializer != null) {
1354 initializer = ResolveInitializer (bc, li, initializer);
1355 // li.Variable.DefinitelyAssigned
1358 if (declarators != null) {
1359 foreach (var d in declarators) {
1360 d.Variable.Type = li.Type;
1362 CreateEvaluatorVariable (bc, d.Variable);
1364 d.Variable.PrepareForFlowAnalysis (bc);
1367 if (d.Initializer != null) {
1368 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1369 // d.Variable.DefinitelyAssigned
1377 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1379 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1380 return a.ResolveStatement (bc);
1383 protected override void DoEmit (EmitContext ec)
1388 li.CreateBuilder (ec);
1390 if (Initializer != null)
1391 ((ExpressionStatement) Initializer).EmitStatement (ec);
1393 if (declarators != null) {
1394 foreach (var d in declarators) {
1395 d.Variable.CreateBuilder (ec);
1396 if (d.Initializer != null)
1397 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1402 protected override void CloneTo (CloneContext clonectx, Statement target)
1404 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1406 if (type_expr != null)
1407 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1409 if (initializer != null)
1410 t.initializer = initializer.Clone (clonectx);
1412 if (declarators != null) {
1413 t.declarators = null;
1414 foreach (var d in declarators)
1415 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1420 class BlockConstantDeclaration : BlockVariableDeclaration
1422 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1427 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1429 initializer = initializer.Resolve (bc);
1430 if (initializer == null)
1433 var c = initializer as Constant;
1435 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1439 c = c.ConvertImplicitly (bc, li.Type);
1441 if (TypeManager.IsReferenceType (li.Type))
1442 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1444 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1449 li.ConstantValue = c;
1455 // The information about a user-perceived local variable
1457 public class LocalVariable : INamedBlockVariable, ILocalVariable
1464 AddressTaken = 1 << 2,
1465 CompilerGenerated = 1 << 3,
1467 ForeachVariable = 1 << 5,
1468 FixedVariable = 1 << 6,
1469 UsingVariable = 1 << 7,
1470 // DefinitelyAssigned = 1 << 8,
1473 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1477 readonly string name;
1478 readonly Location loc;
1479 readonly Block block;
1481 Constant const_value;
1483 public VariableInfo VariableInfo;
1484 HoistedVariable hoisted_variant;
1486 LocalBuilder builder;
1488 public LocalVariable (Block block, string name, Location loc)
1495 public LocalVariable (Block block, string name, Flags flags, Location loc)
1496 : this (block, name, loc)
1502 // Used by variable declarators
1504 public LocalVariable (LocalVariable li, string name, Location loc)
1505 : this (li.block, name, li.flags, loc)
1511 public bool AddressTaken {
1512 get { return (flags & Flags.AddressTaken) != 0; }
1513 set { flags |= Flags.AddressTaken; }
1516 public Block Block {
1522 public Constant ConstantValue {
1527 const_value = value;
1532 // Hoisted local variable variant
1534 public HoistedVariable HoistedVariant {
1536 return hoisted_variant;
1539 hoisted_variant = value;
1543 public bool IsDeclared {
1545 return type != null;
1549 public bool IsConstant {
1551 return (flags & Flags.Constant) != 0;
1555 public bool IsLocked {
1557 return (flags & Flags.IsLocked) != 0;
1560 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1564 public bool IsThis {
1566 return (flags & Flags.IsThis) != 0;
1570 public bool IsFixed {
1572 return (flags & Flags.FixedVariable) != 0;
1576 public bool IsReadonly {
1578 return (flags & Flags.ReadonlyMask) != 0;
1582 public Location Location {
1588 public string Name {
1594 public TypeSpec Type {
1605 public void CreateBuilder (EmitContext ec)
1607 if ((flags & Flags.Used) == 0) {
1608 if (VariableInfo == null) {
1609 // Missing flow analysis or wrong variable flags
1610 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1613 if (VariableInfo.IsEverAssigned)
1614 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1616 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1619 if (HoistedVariant != null)
1622 if (builder != null) {
1623 if ((flags & Flags.CompilerGenerated) != 0)
1626 // To avoid Used warning duplicates
1627 throw new InternalErrorException ("Already created variable `{0}'", name);
1631 // All fixed variabled are pinned, a slot has to be alocated
1633 builder = ec.DeclareLocal (Type, IsFixed);
1634 if (SymbolWriter.HasSymbolWriter)
1635 ec.DefineLocalVariable (name, builder);
1638 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1640 LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
1645 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1647 if (IsConstant && const_value != null)
1648 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
1650 return new LocalVariableReference (this, loc);
1653 public void Emit (EmitContext ec)
1655 // TODO: Need something better for temporary variables
1656 if ((flags & Flags.CompilerGenerated) != 0)
1659 ec.Emit (OpCodes.Ldloc, builder);
1662 public void EmitAssign (EmitContext ec)
1664 // TODO: Need something better for temporary variables
1665 if ((flags & Flags.CompilerGenerated) != 0)
1668 ec.Emit (OpCodes.Stloc, builder);
1671 public void EmitAddressOf (EmitContext ec)
1673 ec.Emit (OpCodes.Ldloca, builder);
1676 public string GetReadOnlyContext ()
1678 switch (flags & Flags.ReadonlyMask) {
1679 case Flags.FixedVariable:
1680 return "fixed variable";
1681 case Flags.ForeachVariable:
1682 return "foreach iteration variable";
1683 case Flags.UsingVariable:
1684 return "using variable";
1687 throw new InternalErrorException ("Variable is not readonly");
1690 public bool IsThisAssigned (BlockContext ec, Block block)
1692 if (VariableInfo == null)
1693 throw new Exception ();
1695 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1698 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1701 public bool IsAssigned (BlockContext ec)
1703 if (VariableInfo == null)
1704 throw new Exception ();
1706 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1709 public void PrepareForFlowAnalysis (BlockContext bc)
1712 // No need for definitely assigned check for these guys
1714 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1717 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1718 bc.FlowOffset += VariableInfo.Length;
1722 // Mark the variables as referenced in the user code
1724 public void SetIsUsed ()
1726 flags |= Flags.Used;
1729 public override string ToString ()
1731 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1736 /// Block represents a C# block.
1740 /// This class is used in a number of places: either to represent
1741 /// explicit blocks that the programmer places or implicit blocks.
1743 /// Implicit blocks are used as labels or to introduce variable
1746 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1747 /// they contain extra information that is not necessary on normal blocks.
1749 public class Block : Statement {
1757 HasCapturedVariable = 64,
1758 HasCapturedThis = 1 << 7,
1759 IsExpressionTree = 1 << 8,
1760 CompilerGenerated = 1 << 9
1763 public Block Parent;
1764 public Location StartLocation;
1765 public Location EndLocation;
1767 public ExplicitBlock Explicit;
1768 public ParametersBlock ParametersBlock;
1770 protected Flags flags;
1773 // The statements in this block
1775 protected List<Statement> statements;
1777 protected List<Statement> scope_initializers;
1779 int? resolving_init_idx;
1785 public int ID = id++;
1787 static int clone_id_counter;
1791 // int assignable_slots;
1792 bool unreachable_shown;
1795 public Block (Block parent, Location start, Location end)
1796 : this (parent, 0, start, end)
1800 public Block (Block parent, Flags flags, Location start, Location end)
1802 if (parent != null) {
1803 // the appropriate constructors will fixup these fields
1804 ParametersBlock = parent.ParametersBlock;
1805 Explicit = parent.Explicit;
1808 this.Parent = parent;
1810 this.StartLocation = start;
1811 this.EndLocation = end;
1813 statements = new List<Statement> (4);
1815 this.original = this;
1820 public bool HasRet {
1821 get { return (flags & Flags.HasRet) != 0; }
1824 public Block Original {
1830 public bool IsCompilerGenerated {
1831 get { return (flags & Flags.CompilerGenerated) != 0; }
1832 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1835 public bool Unchecked {
1836 get { return (flags & Flags.Unchecked) != 0; }
1837 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1840 public bool Unsafe {
1841 get { return (flags & Flags.Unsafe) != 0; }
1842 set { flags |= Flags.Unsafe; }
1847 public Block CreateSwitchBlock (Location start)
1849 // FIXME: Only explicit block should be created
1850 var new_block = new Block (this, start, start);
1851 new_block.IsCompilerGenerated = true;
1855 public void SetEndLocation (Location loc)
1860 public void AddLabel (LabeledStatement target)
1862 ParametersBlock.TopBlock.AddLabel (target.Name, target);
1865 public void AddLocalName (LocalVariable li)
1867 AddLocalName (li.Name, li);
1870 public virtual void AddLocalName (string name, INamedBlockVariable li)
1872 ParametersBlock.TopBlock.AddLocalName (name, li);
1875 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1877 if (reason == null) {
1878 Error_AlreadyDeclared (name, variable);
1882 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1883 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1884 "to `{0}', which is already used in a `{1}' scope to denote something else",
1888 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1890 var pi = variable as ParametersBlock.ParameterInfo;
1892 var p = pi.Parameter;
1893 if (p is AnonymousTypeClass.GeneratedParameter) {
1894 ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
1897 ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
1903 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1904 "A local variable named `{0}' is already defined in this scope", name);
1907 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1909 ParametersBlock.TopBlock.Report.Error (412, loc,
1910 "The type parameter name `{0}' is the same as local variable or parameter name",
1915 // It should be used by expressions which require to
1916 // register a statement during resolve process.
1918 public void AddScopeStatement (Statement s)
1920 if (scope_initializers == null)
1921 scope_initializers = new List<Statement> ();
1924 // Simple recursive helper, when resolve scope initializer another
1925 // new scope initializer can be added, this ensures it's initialized
1926 // before existing one. For now this can happen with expression trees
1927 // in base ctor initializer only
1929 if (resolving_init_idx.HasValue) {
1930 scope_initializers.Insert (resolving_init_idx.Value, s);
1931 ++resolving_init_idx;
1933 scope_initializers.Add (s);
1937 public void AddStatement (Statement s)
1942 public int AssignableSlots {
1944 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1946 // return assignable_slots;
1950 public LabeledStatement LookupLabel (string name)
1952 return ParametersBlock.TopBlock.GetLabel (name, this);
1955 public override bool Resolve (BlockContext ec)
1957 Block prev_block = ec.CurrentBlock;
1960 ec.CurrentBlock = this;
1961 ec.StartFlowBranching (this);
1963 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1966 // Compiler generated scope statements
1968 if (scope_initializers != null) {
1969 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
1970 scope_initializers[resolving_init_idx.Value].Resolve (ec);
1973 resolving_init_idx = null;
1977 // This flag is used to notate nested statements as unreachable from the beginning of this block.
1978 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
1979 // from the beginning of the function. The outer Resolve() that detected the unreachability is
1980 // responsible for handling the situation.
1982 int statement_count = statements.Count;
1983 for (int ix = 0; ix < statement_count; ix++){
1984 Statement s = statements [ix];
1987 // Warn if we detect unreachable code.
1990 if (s is EmptyStatement)
1993 if (!unreachable_shown && !(s is LabeledStatement)) {
1994 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
1995 unreachable_shown = true;
1998 Block c_block = s as Block;
1999 if (c_block != null)
2000 c_block.unreachable = c_block.unreachable_shown = true;
2004 // Note that we're not using ResolveUnreachable() for unreachable
2005 // statements here. ResolveUnreachable() creates a temporary
2006 // flow branching and kills it afterwards. This leads to problems
2007 // if you have two unreachable statements where the first one
2008 // assigns a variable and the second one tries to access it.
2011 if (!s.Resolve (ec)) {
2013 if (ec.IsInProbingMode)
2016 statements [ix] = new EmptyStatement (s.loc);
2020 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2021 statements [ix] = new EmptyStatement (s.loc);
2023 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2024 if (unreachable && s is LabeledStatement)
2025 throw new InternalErrorException ("should not happen");
2028 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2029 ec.CurrentBranching, statement_count);
2031 while (ec.CurrentBranching is FlowBranchingLabeled)
2032 ec.EndFlowBranching ();
2034 bool flow_unreachable = ec.EndFlowBranching ();
2036 ec.CurrentBlock = prev_block;
2038 if (flow_unreachable)
2039 flags |= Flags.HasRet;
2041 // If we're a non-static `struct' constructor which doesn't have an
2042 // initializer, then we must initialize all of the struct's fields.
2043 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2049 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2051 unreachable_shown = true;
2055 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2057 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2058 bool ok = Resolve (ec);
2059 ec.KillFlowBranching ();
2064 protected override void DoEmit (EmitContext ec)
2066 for (int ix = 0; ix < statements.Count; ix++){
2067 statements [ix].Emit (ec);
2071 public override void Emit (EmitContext ec)
2073 if (scope_initializers != null)
2074 EmitScopeInitializers (ec);
2076 ec.Mark (StartLocation);
2079 if (SymbolWriter.HasSymbolWriter)
2080 EmitSymbolInfo (ec);
2083 protected void EmitScopeInitializers (EmitContext ec)
2085 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2087 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2088 foreach (Statement s in scope_initializers)
2092 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2095 protected virtual void EmitSymbolInfo (EmitContext ec)
2100 public override string ToString ()
2102 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2106 protected override void CloneTo (CloneContext clonectx, Statement t)
2108 Block target = (Block) t;
2110 target.clone_id = clone_id_counter++;
2113 clonectx.AddBlockMap (this, target);
2115 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2116 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2119 target.Parent = clonectx.RemapBlockCopy (Parent);
2121 target.statements = new List<Statement> (statements.Count);
2122 foreach (Statement s in statements)
2123 target.statements.Add (s.Clone (clonectx));
2127 public class ExplicitBlock : Block
2129 protected AnonymousMethodStorey am_storey;
2131 public ExplicitBlock (Block parent, Location start, Location end)
2132 : this (parent, (Flags) 0, start, end)
2136 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2137 : base (parent, flags, start, end)
2139 this.Explicit = this;
2144 public AnonymousMethodStorey AnonymousMethodStorey {
2150 public bool HasCapturedThis {
2151 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2153 return (flags & Flags.HasCapturedThis) != 0;
2157 public bool HasCapturedVariable {
2158 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2160 return (flags & Flags.HasCapturedVariable) != 0;
2167 // Creates anonymous method storey in current block
2169 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2172 // An iterator has only 1 storey block
2174 if (ec.CurrentIterator != null)
2175 return ec.CurrentIterator.Storey;
2178 // When referencing a variable in iterator storey from children anonymous method
2180 if (ParametersBlock.am_storey is IteratorStorey) {
2181 return ParametersBlock.am_storey;
2184 if (am_storey == null) {
2185 MemberBase mc = ec.MemberContext as MemberBase;
2188 // Creates anonymous method storey for this block
2190 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2196 public override void Emit (EmitContext ec)
2198 if (am_storey != null) {
2199 DefineAnonymousStorey (ec);
2200 am_storey.EmitStoreyInstantiation (ec, this);
2203 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2204 if (emit_debug_info)
2209 if (emit_debug_info)
2213 void DefineAnonymousStorey (EmitContext ec)
2216 // Creates anonymous method storey
2218 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2220 // Creates parent storey reference when hoisted this is accessible
2222 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2223 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2226 // Hoisted this exists in top-level parent storey only
2228 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2229 parent = parent.Parent.Explicit;
2231 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2234 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2236 // TODO MemberCache: Review
2237 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2240 am_storey.CreateType ();
2241 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2242 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2244 am_storey.DefineType ();
2245 am_storey.ResolveTypeParameters ();
2247 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2248 if (ref_blocks != null) {
2249 foreach (ExplicitBlock ref_block in ref_blocks) {
2250 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2251 if (b.am_storey != null) {
2252 b.am_storey.AddParentStoreyReference (ec, am_storey);
2254 // Stop propagation inside same top block
2255 if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
2258 b = b.ParametersBlock;
2261 b.HasCapturedVariable = true;
2266 am_storey.Define ();
2267 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2270 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2272 tryBlock.statements = statements;
2273 statements = new List<Statement> (1);
2274 statements.Add (tf);
2279 // ParametersBlock was introduced to support anonymous methods
2280 // and lambda expressions
2282 public class ParametersBlock : ExplicitBlock
2284 public class ParameterInfo : INamedBlockVariable
2286 readonly ParametersBlock block;
2288 public VariableInfo VariableInfo;
2291 public ParameterInfo (ParametersBlock block, int index)
2299 public Block Block {
2305 public bool IsDeclared {
2311 public bool IsLocked {
2320 public Location Location {
2322 return Parameter.Location;
2326 public Parameter Parameter {
2328 return block.Parameters [index];
2332 public TypeSpec ParameterType {
2334 return Parameter.Type;
2340 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2342 return new ParameterReference (this, loc);
2347 // Block is converted to an expression
2349 sealed class BlockScopeExpression : Expression
2352 readonly ParametersBlock block;
2354 public BlockScopeExpression (Expression child, ParametersBlock block)
2360 public override Expression CreateExpressionTree (ResolveContext ec)
2362 throw new NotSupportedException ();
2365 protected override Expression DoResolve (ResolveContext ec)
2370 child = child.Resolve (ec);
2374 eclass = child.eclass;
2379 public override void Emit (EmitContext ec)
2381 block.EmitScopeInitializers (ec);
2386 protected ParametersCompiled parameters;
2387 protected ParameterInfo[] parameter_info;
2389 protected bool unreachable;
2390 protected ToplevelBlock top_block;
2392 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2393 : base (parent, 0, start, start)
2395 if (parameters == null)
2396 throw new ArgumentNullException ("parameters");
2398 this.parameters = parameters;
2399 ParametersBlock = this;
2401 this.top_block = parent.ParametersBlock.top_block;
2402 ProcessParameters ();
2405 protected ParametersBlock (ParametersCompiled parameters, Location start)
2406 : base (null, 0, start, start)
2408 if (parameters == null)
2409 throw new ArgumentNullException ("parameters");
2411 this.parameters = parameters;
2412 ParametersBlock = this;
2415 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2416 : base (null, 0, source.StartLocation, source.EndLocation)
2418 this.parameters = parameters;
2419 this.statements = source.statements;
2420 this.scope_initializers = source.scope_initializers;
2422 this.resolved = true;
2423 this.unreachable = source.unreachable;
2424 this.am_storey = source.am_storey;
2426 ParametersBlock = this;
2432 // Block has been converted to expression tree
2434 public bool IsExpressionTree {
2436 return (flags & Flags.IsExpressionTree) != 0;
2441 // The parameters for the block.
2443 public ParametersCompiled Parameters {
2449 public ToplevelBlock TopBlock {
2455 public bool Resolved {
2464 // Check whether all `out' parameters have been assigned.
2466 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2468 if (vector.IsUnreachable)
2471 int n = parameter_info == null ? 0 : parameter_info.Length;
2473 for (int i = 0; i < n; i++) {
2474 VariableInfo var = parameter_info[i].VariableInfo;
2479 if (vector.IsAssigned (var, false))
2482 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2487 public override Expression CreateExpressionTree (ResolveContext ec)
2489 if (statements.Count == 1) {
2490 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2491 if (scope_initializers != null)
2492 expr = new BlockScopeExpression (expr, this);
2497 return base.CreateExpressionTree (ec);
2500 public ParameterInfo GetParameterInfo (Parameter p)
2502 for (int i = 0; i < parameters.Count; ++i) {
2503 if (parameters[i] == p)
2504 return parameter_info[i];
2507 throw new ArgumentException ("Invalid parameter");
2510 public Expression GetParameterReference (int index, Location loc)
2512 return new ParameterReference (parameter_info[index], loc);
2515 protected void ProcessParameters ()
2517 if (parameters.Count == 0)
2520 parameter_info = new ParameterInfo[parameters.Count];
2521 for (int i = 0; i < parameter_info.Length; ++i) {
2522 var p = parameters.FixedParameters[i];
2526 // TODO: Should use Parameter only and more block there
2527 parameter_info[i] = new ParameterInfo (this, i);
2528 AddLocalName (p.Name, parameter_info[i]);
2532 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2539 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2540 flags |= Flags.IsExpressionTree;
2545 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2546 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2551 unreachable = top_level.End ();
2553 } catch (Exception e) {
2554 if (e is CompletionResult || rc.Report.IsDisabled)
2557 if (rc.CurrentBlock != null) {
2558 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2560 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2563 if (Report.DebugFlags > 0)
2567 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2568 if (rc.CurrentAnonymousMethod == null) {
2569 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2570 if (md is IteratorMethod) {
2573 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2577 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2578 rc.CurrentAnonymousMethod.GetSignatureForError ());
2586 void ResolveMeta (BlockContext ec)
2588 int orig_count = parameters.Count;
2590 for (int i = 0; i < orig_count; ++i) {
2591 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2593 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2596 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2597 parameter_info[i].VariableInfo = vi;
2598 ec.FlowOffset += vi.Length;
2602 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2604 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2605 pb.EndLocation = EndLocation;
2606 pb.statements = statements;
2608 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2609 am_storey = new IteratorStorey (iterator);
2611 statements = new List<Statement> (1);
2612 AddStatement (new Return (iterator, iterator.Location));
2619 public class ToplevelBlock : ParametersBlock
2621 LocalVariable this_variable;
2622 CompilerContext compiler;
2623 Dictionary<string, object> names;
2624 Dictionary<string, object> labels;
2626 public HoistedVariable HoistedThisVariable;
2628 public Report Report {
2629 get { return compiler.Report; }
2632 public ToplevelBlock (CompilerContext ctx, Location loc)
2633 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2637 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2638 : base (parameters, start)
2640 this.compiler = ctx;
2643 ProcessParameters ();
2647 // Recreates a top level block from parameters block. Used for
2648 // compiler generated methods where the original block comes from
2649 // explicit child block. This works for already resolved blocks
2650 // only to ensure we resolve them in the correct flow order
2652 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2653 : base (source, parameters)
2655 this.compiler = source.TopBlock.compiler;
2659 public override void AddLocalName (string name, INamedBlockVariable li)
2662 names = new Dictionary<string, object> ();
2665 if (!names.TryGetValue (name, out value)) {
2666 names.Add (name, li);
2670 INamedBlockVariable existing = value as INamedBlockVariable;
2671 List<INamedBlockVariable> existing_list;
2672 if (existing != null) {
2673 existing_list = new List<INamedBlockVariable> ();
2674 existing_list.Add (existing);
2675 names[name] = existing_list;
2677 existing_list = (List<INamedBlockVariable>) value;
2681 // A collision checking between local names
2683 for (int i = 0; i < existing_list.Count; ++i) {
2684 existing = existing_list[i];
2685 Block b = existing.Block;
2687 // Collision at same level
2688 if (li.Block == b) {
2689 li.Block.Error_AlreadyDeclared (name, li);
2693 // Collision with parent
2695 while ((b = b.Parent) != null) {
2696 if (existing.Block == b) {
2697 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2698 i = existing_list.Count;
2703 // Collision with with children
2705 while ((b = b.Parent) != null) {
2706 if (li.Block == b) {
2707 li.Block.Error_AlreadyDeclared (name, li, "child");
2708 i = existing_list.Count;
2714 existing_list.Add (li);
2717 public void AddLabel (string name, LabeledStatement label)
2720 labels = new Dictionary<string, object> ();
2723 if (!labels.TryGetValue (name, out value)) {
2724 labels.Add (name, label);
2728 LabeledStatement existing = value as LabeledStatement;
2729 List<LabeledStatement> existing_list;
2730 if (existing != null) {
2731 existing_list = new List<LabeledStatement> ();
2732 existing_list.Add (existing);
2733 labels[name] = existing_list;
2735 existing_list = (List<LabeledStatement>) value;
2739 // A collision checking between labels
2741 for (int i = 0; i < existing_list.Count; ++i) {
2742 existing = existing_list[i];
2743 Block b = existing.Block;
2745 // Collision at same level
2746 if (label.Block == b) {
2747 Report.SymbolRelatedToPreviousError (existing.loc, name);
2748 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2752 // Collision with parent
2754 while ((b = b.Parent) != null) {
2755 if (existing.Block == b) {
2756 Report.Error (158, label.loc,
2757 "The label `{0}' shadows another label by the same name in a contained scope", name);
2758 i = existing_list.Count;
2763 // Collision with with children
2765 while ((b = b.Parent) != null) {
2766 if (label.Block == b) {
2767 Report.Error (158, label.loc,
2768 "The label `{0}' shadows another label by the same name in a contained scope", name);
2769 i = existing_list.Count;
2775 existing_list.Add (label);
2779 // Creates an arguments set from all parameters, useful for method proxy calls
2781 public Arguments GetAllParametersArguments ()
2783 int count = parameters.Count;
2784 Arguments args = new Arguments (count);
2785 for (int i = 0; i < count; ++i) {
2786 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2787 args.Add (new Argument (arg_expr));
2794 // Lookup inside a block, the returned value can represent 3 states
2796 // true+variable: A local name was found and it's valid
2797 // false+variable: A local name was found in a child block only
2798 // false+null: No local name was found
2800 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2806 if (!names.TryGetValue (name, out value))
2809 variable = value as INamedBlockVariable;
2811 if (variable != null) {
2813 if (variable.Block == b.Original)
2817 } while (b != null);
2825 } while (b != null);
2827 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2828 for (int i = 0; i < list.Count; ++i) {
2831 if (variable.Block == b.Original)
2835 } while (b != null);
2843 } while (b != null);
2853 public LabeledStatement GetLabel (string name, Block block)
2859 if (!labels.TryGetValue (name, out value)) {
2863 var label = value as LabeledStatement;
2865 if (label != null) {
2866 if (label.Block == b.Original)
2869 // TODO: Temporary workaround for the switch block implicit label block
2870 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2873 List<LabeledStatement> list = (List<LabeledStatement>) value;
2874 for (int i = 0; i < list.Count; ++i) {
2876 if (label.Block == b.Original)
2879 // TODO: Temporary workaround for the switch block implicit label block
2880 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2889 // Returns the "this" instance variable of this block.
2890 // See AddThisVariable() for more information.
2892 public LocalVariable ThisVariable {
2893 get { return this_variable; }
2897 // This is used by non-static `struct' constructors which do not have an
2898 // initializer - in this case, the constructor must initialize all of the
2899 // struct's fields. To do this, we add a "this" variable and use the flow
2900 // analysis code to ensure that it's been fully initialized before control
2901 // leaves the constructor.
2903 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
2905 if (this_variable == null) {
2906 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
2907 this_variable.Type = ds.CurrentType;
2908 this_variable.PrepareForFlowAnalysis (bc);
2911 return this_variable;
2914 public bool IsIterator {
2915 get { return (flags & Flags.IsIterator) != 0; }
2916 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2919 public bool IsThisAssigned (BlockContext ec)
2921 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2924 public override void Emit (EmitContext ec)
2926 if (Report.Errors > 0)
2932 if (ec.HasReturnLabel)
2933 ec.ReturnLabel = ec.DefineLabel ();
2937 ec.Mark (EndLocation);
2939 if (ec.HasReturnLabel)
2940 ec.MarkLabel (ec.ReturnLabel);
2942 if (ec.return_value != null) {
2943 ec.Emit (OpCodes.Ldloc, ec.return_value);
2944 ec.Emit (OpCodes.Ret);
2947 // If `HasReturnLabel' is set, then we already emitted a
2948 // jump to the end of the method, so we must emit a `ret'
2951 // Unfortunately, System.Reflection.Emit automatically emits
2952 // a leave to the end of a finally block. This is a problem
2953 // if no code is following the try/finally block since we may
2954 // jump to a point after the end of the method.
2955 // As a workaround, we're always creating a return label in
2959 if (ec.HasReturnLabel || !unreachable) {
2960 if (ec.ReturnType != TypeManager.void_type)
2961 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2962 ec.Emit (OpCodes.Ret);
2967 } catch (Exception e){
2968 Console.WriteLine ("Exception caught by the compiler while emitting:");
2969 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2971 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2977 protected override void EmitSymbolInfo (EmitContext ec)
2979 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2980 if ((ae != null) && (ae.Storey != null))
2981 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2983 base.EmitSymbolInfo (ec);
2987 public class SwitchLabel {
2994 Label il_label_code;
2995 bool il_label_code_set;
2997 public static readonly object NullStringCase = new object ();
3000 // if expr == null, then it is the default case.
3002 public SwitchLabel (Expression expr, Location l)
3008 public Expression Label {
3014 public Location Location {
3018 public object Converted {
3024 public Label GetILLabel (EmitContext ec)
3027 il_label = ec.DefineLabel ();
3028 il_label_set = true;
3033 public Label GetILLabelCode (EmitContext ec)
3035 if (!il_label_code_set){
3036 il_label_code = ec.DefineLabel ();
3037 il_label_code_set = true;
3039 return il_label_code;
3043 // Resolves the expression, reduces it to a literal if possible
3044 // and then converts it to the requested type.
3046 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3048 Expression e = label.Resolve (ec);
3053 Constant c = e as Constant;
3055 ec.Report.Error (150, loc, "A constant value is expected");
3059 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3060 converted = NullStringCase;
3064 if (allow_nullable && c.GetValue () == null) {
3065 converted = NullStringCase;
3069 c = c.ImplicitConversionRequired (ec, required_type, loc);
3073 converted = c.GetValue ();
3077 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3080 if (converted == null)
3082 else if (converted == NullStringCase)
3085 label = converted.ToString ();
3087 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3088 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3091 public SwitchLabel Clone (CloneContext clonectx)
3093 return new SwitchLabel (label.Clone (clonectx), loc);
3097 public class SwitchSection {
3098 public readonly List<SwitchLabel> Labels;
3099 public readonly Block Block;
3101 public SwitchSection (List<SwitchLabel> labels, Block block)
3107 public SwitchSection Clone (CloneContext clonectx)
3109 var cloned_labels = new List<SwitchLabel> ();
3111 foreach (SwitchLabel sl in Labels)
3112 cloned_labels.Add (sl.Clone (clonectx));
3114 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3118 public class Switch : Statement {
3119 public List<SwitchSection> Sections;
3120 public Expression Expr;
3123 /// Maps constants whose type type SwitchType to their SwitchLabels.
3125 public IDictionary<object, SwitchLabel> Elements;
3128 /// The governing switch type
3130 public TypeSpec SwitchType;
3135 Label default_target;
3137 Expression new_expr;
3140 SwitchSection constant_section;
3141 SwitchSection default_section;
3143 ExpressionStatement string_dictionary;
3144 FieldExpr switch_cache_field;
3145 static int unique_counter;
3146 ExplicitBlock block;
3149 // Nullable Types support
3151 Nullable.Unwrap unwrap;
3153 protected bool HaveUnwrap {
3154 get { return unwrap != null; }
3158 // The types allowed to be implicitly cast from
3159 // on the governing type
3161 static TypeSpec [] allowed_types;
3163 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3171 public ExplicitBlock Block {
3177 public bool GotDefault {
3179 return default_section != null;
3183 public Label DefaultTarget {
3185 return default_target;
3190 // Determines the governing type for a switch. The returned
3191 // expression might be the expression from the switch, or an
3192 // expression that includes any potential conversions to the
3193 // integral types or to string.
3195 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3197 TypeSpec t = expr.Type;
3199 if (t == TypeManager.byte_type ||
3200 t == TypeManager.sbyte_type ||
3201 t == TypeManager.ushort_type ||
3202 t == TypeManager.short_type ||
3203 t == TypeManager.uint32_type ||
3204 t == TypeManager.int32_type ||
3205 t == TypeManager.uint64_type ||
3206 t == TypeManager.int64_type ||
3207 t == TypeManager.char_type ||
3208 t == TypeManager.string_type ||
3209 t == TypeManager.bool_type ||
3210 TypeManager.IsEnumType (t))
3213 if (allowed_types == null){
3214 allowed_types = new TypeSpec [] {
3215 TypeManager.sbyte_type,
3216 TypeManager.byte_type,
3217 TypeManager.short_type,
3218 TypeManager.ushort_type,
3219 TypeManager.int32_type,
3220 TypeManager.uint32_type,
3221 TypeManager.int64_type,
3222 TypeManager.uint64_type,
3223 TypeManager.char_type,
3224 TypeManager.string_type
3229 // Try to find a *user* defined implicit conversion.
3231 // If there is no implicit conversion, or if there are multiple
3232 // conversions, we have to report an error
3234 Expression converted = null;
3235 foreach (TypeSpec tt in allowed_types){
3238 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3243 // Ignore over-worked ImplicitUserConversions that do
3244 // an implicit conversion in addition to the user conversion.
3246 if (!(e is UserCast))
3249 if (converted != null){
3250 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3260 // Performs the basic sanity checks on the switch statement
3261 // (looks for duplicate keys and non-constant expressions).
3263 // It also returns a hashtable with the keys that we will later
3264 // use to compute the switch tables
3266 bool CheckSwitch (ResolveContext ec)
3269 Elements = new Dictionary<object, SwitchLabel> ();
3271 foreach (SwitchSection ss in Sections){
3272 foreach (SwitchLabel sl in ss.Labels){
3273 if (sl.Label == null){
3274 if (default_section != null){
3275 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3278 default_section = ss;
3282 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3287 object key = sl.Converted;
3288 if (key == SwitchLabel.NullStringCase)
3289 has_null_case = true;
3292 Elements.Add (key, sl);
3293 } catch (ArgumentException) {
3294 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3302 void EmitObjectInteger (EmitContext ec, object k)
3305 ec.EmitInt ((int) k);
3306 else if (k is Constant) {
3307 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3310 ec.EmitInt (unchecked ((int) (uint) k));
3313 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3315 ec.EmitInt ((int) (long) k);
3316 ec.Emit (OpCodes.Conv_I8);
3319 ec.EmitLong ((long) k);
3321 else if (k is ulong)
3323 ulong ul = (ulong) k;
3326 ec.EmitInt (unchecked ((int) ul));
3327 ec.Emit (OpCodes.Conv_U8);
3331 ec.EmitLong (unchecked ((long) ul));
3335 ec.EmitInt ((int) ((char) k));
3336 else if (k is sbyte)
3337 ec.EmitInt ((int) ((sbyte) k));
3339 ec.EmitInt ((int) ((byte) k));
3340 else if (k is short)
3341 ec.EmitInt ((int) ((short) k));
3342 else if (k is ushort)
3343 ec.EmitInt ((int) ((ushort) k));
3345 ec.EmitInt (((bool) k) ? 1 : 0);
3347 throw new Exception ("Unhandled case");
3350 // structure used to hold blocks of keys while calculating table switch
3351 class KeyBlock : IComparable
3353 public KeyBlock (long _first)
3355 first = last = _first;
3359 public List<object> element_keys;
3360 // how many items are in the bucket
3361 public int Size = 1;
3364 get { return (int) (last - first + 1); }
3366 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3368 return kb_last.last - kb_first.first + 1;
3370 public int CompareTo (object obj)
3372 KeyBlock kb = (KeyBlock) obj;
3373 int nLength = Length;
3374 int nLengthOther = kb.Length;
3375 if (nLengthOther == nLength)
3376 return (int) (kb.first - first);
3377 return nLength - nLengthOther;
3382 /// This method emits code for a lookup-based switch statement (non-string)
3383 /// Basically it groups the cases into blocks that are at least half full,
3384 /// and then spits out individual lookup opcodes for each block.
3385 /// It emits the longest blocks first, and short blocks are just
3386 /// handled with direct compares.
3388 /// <param name="ec"></param>
3389 /// <param name="val"></param>
3390 /// <returns></returns>
3391 void TableSwitchEmit (EmitContext ec, Expression val)
3393 int element_count = Elements.Count;
3394 object [] element_keys = new object [element_count];
3395 Elements.Keys.CopyTo (element_keys, 0);
3396 Array.Sort (element_keys);
3398 // initialize the block list with one element per key
3399 var key_blocks = new List<KeyBlock> (element_count);
3400 foreach (object key in element_keys)
3401 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3403 KeyBlock current_kb;
3404 // iteratively merge the blocks while they are at least half full
3405 // there's probably a really cool way to do this with a tree...
3406 while (key_blocks.Count > 1)
3408 var key_blocks_new = new List<KeyBlock> ();
3409 current_kb = (KeyBlock) key_blocks [0];
3410 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3412 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3413 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3416 current_kb.last = kb.last;
3417 current_kb.Size += kb.Size;
3421 // start a new block
3422 key_blocks_new.Add (current_kb);
3426 key_blocks_new.Add (current_kb);
3427 if (key_blocks.Count == key_blocks_new.Count)
3429 key_blocks = key_blocks_new;
3432 // initialize the key lists
3433 foreach (KeyBlock kb in key_blocks)
3434 kb.element_keys = new List<object> ();
3436 // fill the key lists
3438 if (key_blocks.Count > 0) {
3439 current_kb = (KeyBlock) key_blocks [0];
3440 foreach (object key in element_keys)
3442 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3443 System.Convert.ToInt64 (key) > current_kb.last;
3445 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3446 current_kb.element_keys.Add (key);
3450 // sort the blocks so we can tackle the largest ones first
3453 // okay now we can start...
3454 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3455 Label lbl_default = default_target;
3457 Type type_keys = null;
3458 if (element_keys.Length > 0)
3459 type_keys = element_keys [0].GetType (); // used for conversions
3461 TypeSpec compare_type;
3463 if (TypeManager.IsEnumType (SwitchType))
3464 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3466 compare_type = SwitchType;
3468 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3470 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3471 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3474 foreach (object key in kb.element_keys) {
3475 SwitchLabel sl = (SwitchLabel) Elements [key];
3476 if (key is int && (int) key == 0) {
3477 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3480 EmitObjectInteger (ec, key);
3481 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3487 // TODO: if all the keys in the block are the same and there are
3488 // no gaps/defaults then just use a range-check.
3489 if (compare_type == TypeManager.int64_type ||
3490 compare_type == TypeManager.uint64_type)
3492 // TODO: optimize constant/I4 cases
3494 // check block range (could be > 2^31)
3496 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3497 ec.Emit (OpCodes.Blt, lbl_default);
3499 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3500 ec.Emit (OpCodes.Bgt, lbl_default);
3506 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3507 ec.Emit (OpCodes.Sub);
3509 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3515 int first = (int) kb.first;
3519 ec.Emit (OpCodes.Sub);
3523 ec.EmitInt (-first);
3524 ec.Emit (OpCodes.Add);
3528 // first, build the list of labels for the switch
3530 int cJumps = kb.Length;
3531 Label [] switch_labels = new Label [cJumps];
3532 for (int iJump = 0; iJump < cJumps; iJump++)
3534 object key = kb.element_keys [iKey];
3535 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3537 SwitchLabel sl = (SwitchLabel) Elements [key];
3538 switch_labels [iJump] = sl.GetILLabel (ec);
3542 switch_labels [iJump] = lbl_default;
3544 // emit the switch opcode
3545 ec.Emit (OpCodes.Switch, switch_labels);
3548 // mark the default for this block
3550 ec.MarkLabel (lbl_default);
3553 // TODO: find the default case and emit it here,
3554 // to prevent having to do the following jump.
3555 // make sure to mark other labels in the default section
3557 // the last default just goes to the end
3558 if (element_keys.Length > 0)
3559 ec.Emit (OpCodes.Br, lbl_default);
3561 // now emit the code for the sections
3562 bool found_default = false;
3564 foreach (SwitchSection ss in Sections) {
3565 foreach (SwitchLabel sl in ss.Labels) {
3566 if (sl.Converted == SwitchLabel.NullStringCase) {
3567 ec.MarkLabel (null_target);
3568 } else if (sl.Label == null) {
3569 ec.MarkLabel (lbl_default);
3570 found_default = true;
3572 ec.MarkLabel (null_target);
3574 ec.MarkLabel (sl.GetILLabel (ec));
3575 ec.MarkLabel (sl.GetILLabelCode (ec));
3580 if (!found_default) {
3581 ec.MarkLabel (lbl_default);
3582 if (!has_null_case) {
3583 ec.MarkLabel (null_target);
3587 ec.MarkLabel (lbl_end);
3590 SwitchSection FindSection (SwitchLabel label)
3592 foreach (SwitchSection ss in Sections){
3593 foreach (SwitchLabel sl in ss.Labels){
3602 public static void Reset ()
3605 allowed_types = null;
3608 public override bool Resolve (BlockContext ec)
3610 Expr = Expr.Resolve (ec);
3614 new_expr = SwitchGoverningType (ec, Expr);
3616 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3617 unwrap = Nullable.Unwrap.Create (Expr, false);
3621 new_expr = SwitchGoverningType (ec, unwrap);
3624 if (new_expr == null){
3625 ec.Report.Error (151, loc,
3626 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3627 TypeManager.CSharpName (Expr.Type));
3632 SwitchType = new_expr.Type;
3634 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3635 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3639 if (!CheckSwitch (ec))
3643 Elements.Remove (SwitchLabel.NullStringCase);
3645 Switch old_switch = ec.Switch;
3647 ec.Switch.SwitchType = SwitchType;
3649 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3650 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3652 var constant = new_expr as Constant;
3653 if (constant != null) {
3655 object key = constant.GetValue ();
3657 if (Elements.TryGetValue (key, out label))
3658 constant_section = FindSection (label);
3660 if (constant_section == null)
3661 constant_section = default_section;
3666 foreach (SwitchSection ss in Sections){
3668 ec.CurrentBranching.CreateSibling (
3669 null, FlowBranching.SiblingType.SwitchSection);
3673 if (is_constant && (ss != constant_section)) {
3674 // If we're a constant switch, we're only emitting
3675 // one single section - mark all the others as
3677 ec.CurrentBranching.CurrentUsageVector.Goto ();
3678 if (!ss.Block.ResolveUnreachable (ec, true)) {
3682 if (!ss.Block.Resolve (ec))
3687 if (default_section == null)
3688 ec.CurrentBranching.CreateSibling (
3689 null, FlowBranching.SiblingType.SwitchSection);
3691 ec.EndFlowBranching ();
3692 ec.Switch = old_switch;
3694 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3699 if (SwitchType == TypeManager.string_type && !is_constant) {
3700 // TODO: Optimize single case, and single+default case
3701 ResolveStringSwitchMap (ec);
3707 void ResolveStringSwitchMap (ResolveContext ec)
3709 FullNamedExpression string_dictionary_type;
3710 if (TypeManager.generic_ienumerable_type != null) {
3711 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3712 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3714 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3716 new TypeExpression (TypeManager.string_type, loc),
3717 new TypeExpression (TypeManager.int32_type, loc)), loc);
3719 MemberAccess system_collections_generic = new MemberAccess (
3720 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3722 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3725 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3726 Field field = new Field (ctype, string_dictionary_type,
3727 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3728 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3729 if (!field.Define ())
3731 ctype.AddField (field);
3733 var init = new List<Expression> ();
3736 string value = null;
3737 foreach (SwitchSection section in Sections) {
3738 int last_count = init.Count;
3739 foreach (SwitchLabel sl in section.Labels) {
3740 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3743 value = (string) sl.Converted;
3744 var init_args = new List<Expression> (2);
3745 init_args.Add (new StringLiteral (value, sl.Location));
3746 init_args.Add (new IntConstant (counter, loc));
3747 init.Add (new CollectionElementInitializer (init_args, loc));
3751 // Don't add empty sections
3753 if (last_count == init.Count)
3756 Elements.Add (counter, section.Labels [0]);
3760 Arguments args = new Arguments (1);
3761 args.Add (new Argument (new IntConstant (init.Count, loc)));
3762 Expression initializer = new NewInitialize (string_dictionary_type, args,
3763 new CollectionOrObjectInitializers (init, loc), loc);
3765 switch_cache_field = new FieldExpr (field, loc);
3766 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3769 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3771 Label l_initialized = ec.DefineLabel ();
3774 // Skip initialization when value is null
3776 value.EmitBranchable (ec, null_target, false);
3779 // Check if string dictionary is initialized and initialize
3781 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3782 string_dictionary.EmitStatement (ec);
3783 ec.MarkLabel (l_initialized);
3785 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3787 ResolveContext rc = new ResolveContext (ec.MemberContext);
3789 if (TypeManager.generic_ienumerable_type != null) {
3790 Arguments get_value_args = new Arguments (2);
3791 get_value_args.Add (new Argument (value));
3792 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3793 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3794 if (get_item == null)
3798 // A value was not found, go to default case
3800 get_item.EmitBranchable (ec, default_target, false);
3802 Arguments get_value_args = new Arguments (1);
3803 get_value_args.Add (new Argument (value));
3805 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3806 if (get_item == null)
3809 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3810 get_item_object.EmitAssign (ec, get_item, true, false);
3811 ec.Emit (OpCodes.Brfalse, default_target);
3813 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3814 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3816 get_item_int.EmitStatement (ec);
3817 get_item_object.Release (ec);
3820 TableSwitchEmit (ec, string_switch_variable);
3821 string_switch_variable.Release (ec);
3824 protected override void DoEmit (EmitContext ec)
3827 // Needed to emit anonymous storey initialization
3828 // Otherwise it does not contain any statements for now
3832 default_target = ec.DefineLabel ();
3833 null_target = ec.DefineLabel ();
3835 // Store variable for comparission purposes
3836 // TODO: Don't duplicate non-captured VariableReference
3837 LocalTemporary value;
3839 value = new LocalTemporary (SwitchType);
3840 unwrap.EmitCheck (ec);
3841 ec.Emit (OpCodes.Brfalse, null_target);
3844 } else if (!is_constant) {
3845 value = new LocalTemporary (SwitchType);
3852 // Setup the codegen context
3854 Label old_end = ec.LoopEnd;
3855 Switch old_switch = ec.Switch;
3857 ec.LoopEnd = ec.DefineLabel ();
3862 if (constant_section != null)
3863 constant_section.Block.Emit (ec);
3864 } else if (string_dictionary != null) {
3865 DoEmitStringSwitch (value, ec);
3867 TableSwitchEmit (ec, value);
3873 // Restore context state.
3874 ec.MarkLabel (ec.LoopEnd);
3877 // Restore the previous context
3879 ec.LoopEnd = old_end;
3880 ec.Switch = old_switch;
3883 protected override void CloneTo (CloneContext clonectx, Statement t)
3885 Switch target = (Switch) t;
3887 target.Expr = Expr.Clone (clonectx);
3888 target.Sections = new List<SwitchSection> ();
3889 foreach (SwitchSection ss in Sections){
3890 target.Sections.Add (ss.Clone (clonectx));
3895 // A place where execution can restart in an iterator
3896 public abstract class ResumableStatement : Statement
3899 protected Label resume_point;
3901 public Label PrepareForEmit (EmitContext ec)
3905 resume_point = ec.DefineLabel ();
3907 return resume_point;
3910 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3914 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3919 // Base class for statements that are implemented in terms of try...finally
3920 public abstract class ExceptionStatement : ResumableStatement
3924 List<ResumableStatement> resume_points;
3925 int first_resume_pc;
3926 protected Statement stmt;
3927 Label dispose_try_block;
3928 bool prepared_for_dispose, emitted_dispose;
3930 protected ExceptionStatement (Statement stmt, Location loc)
3938 public Statement Statement {
3946 protected abstract void EmitPreTryBody (EmitContext ec);
3947 protected abstract void EmitTryBody (EmitContext ec);
3948 protected abstract void EmitFinallyBody (EmitContext ec);
3950 protected sealed override void DoEmit (EmitContext ec)
3952 EmitPreTryBody (ec);
3954 if (resume_points != null) {
3955 ec.EmitInt ((int) Iterator.State.Running);
3956 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3959 ec.BeginExceptionBlock ();
3961 if (resume_points != null) {
3962 ec.MarkLabel (resume_point);
3964 // For normal control flow, we want to fall-through the Switch
3965 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3966 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3967 ec.EmitInt (first_resume_pc);
3968 ec.Emit (OpCodes.Sub);
3970 Label [] labels = new Label [resume_points.Count];
3971 for (int i = 0; i < resume_points.Count; ++i)
3972 labels [i] = resume_points [i].PrepareForEmit (ec);
3973 ec.Emit (OpCodes.Switch, labels);
3978 ec.BeginFinallyBlock ();
3980 Label start_finally = ec.DefineLabel ();
3981 if (resume_points != null) {
3982 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3983 ec.Emit (OpCodes.Brfalse_S, start_finally);
3984 ec.Emit (OpCodes.Endfinally);
3987 ec.MarkLabel (start_finally);
3988 EmitFinallyBody (ec);
3990 ec.EndExceptionBlock ();
3993 public void SomeCodeFollows ()
3995 code_follows = true;
3998 public override bool Resolve (BlockContext ec)
4000 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4001 // So, ensure there's some IL code after this statement.
4002 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4003 ec.NeedReturnLabel ();
4005 iter = ec.CurrentIterator;
4009 public void AddResumePoint (ResumableStatement stmt, int pc)
4011 if (resume_points == null) {
4012 resume_points = new List<ResumableStatement> ();
4013 first_resume_pc = pc;
4016 if (pc != first_resume_pc + resume_points.Count)
4017 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4019 resume_points.Add (stmt);
4022 public override Label PrepareForDispose (EmitContext ec, Label end)
4024 if (!prepared_for_dispose) {
4025 prepared_for_dispose = true;
4026 dispose_try_block = ec.DefineLabel ();
4028 return dispose_try_block;
4031 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4033 if (emitted_dispose)
4036 emitted_dispose = true;
4038 Label end_of_try = ec.DefineLabel ();
4040 // Ensure that the only way we can get into this code is through a dispatcher
4041 if (have_dispatcher)
4042 ec.Emit (OpCodes.Br, end);
4044 ec.BeginExceptionBlock ();
4046 ec.MarkLabel (dispose_try_block);
4048 Label [] labels = null;
4049 for (int i = 0; i < resume_points.Count; ++i) {
4050 ResumableStatement s = (ResumableStatement) resume_points [i];
4051 Label ret = s.PrepareForDispose (ec, end_of_try);
4052 if (ret.Equals (end_of_try) && labels == null)
4054 if (labels == null) {
4055 labels = new Label [resume_points.Count];
4056 for (int j = 0; j < i; ++j)
4057 labels [j] = end_of_try;
4062 if (labels != null) {
4064 for (j = 1; j < labels.Length; ++j)
4065 if (!labels [0].Equals (labels [j]))
4067 bool emit_dispatcher = j < labels.Length;
4069 if (emit_dispatcher) {
4070 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4071 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4072 ec.EmitInt (first_resume_pc);
4073 ec.Emit (OpCodes.Sub);
4074 ec.Emit (OpCodes.Switch, labels);
4075 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4078 foreach (ResumableStatement s in resume_points)
4079 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4082 ec.MarkLabel (end_of_try);
4084 ec.BeginFinallyBlock ();
4086 EmitFinallyBody (ec);
4088 ec.EndExceptionBlock ();
4092 public class Lock : ExceptionStatement {
4094 TemporaryVariableReference expr_copy;
4095 TemporaryVariableReference lock_taken;
4097 public Lock (Expression expr, Statement stmt, Location loc)
4103 public override bool Resolve (BlockContext ec)
4105 expr = expr.Resolve (ec);
4109 if (!TypeManager.IsReferenceType (expr.Type)){
4110 ec.Report.Error (185, loc,
4111 "`{0}' is not a reference type as required by the lock statement",
4112 expr.Type.GetSignatureForError ());
4115 if (expr.Type.IsGenericParameter) {
4116 expr = Convert.ImplicitTypeParameterConversion (expr, TypeManager.object_type);
4119 VariableReference lv = expr as VariableReference;
4122 locked = lv.IsLockedByStatement;
4123 lv.IsLockedByStatement = true;
4129 ec.StartFlowBranching (this);
4130 Statement.Resolve (ec);
4131 ec.EndFlowBranching ();
4134 lv.IsLockedByStatement = locked;
4140 // Have to keep original lock value around to unlock same location
4141 // in the case the original has changed or is null
4143 expr_copy = TemporaryVariableReference.Create (TypeManager.object_type, ec.CurrentBlock.Parent, loc);
4144 expr_copy.Resolve (ec);
4147 // Ensure Monitor methods are available
4149 if (ResolvePredefinedMethods (ec) > 1) {
4150 lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
4151 lock_taken.Resolve (ec);
4157 protected override void EmitPreTryBody (EmitContext ec)
4159 expr_copy.EmitAssign (ec, expr);
4161 if (lock_taken != null) {
4163 // Initialize ref variable
4165 lock_taken.EmitAssign (ec, new BoolLiteral (false, loc));
4168 // Monitor.Enter (expr_copy)
4170 expr_copy.Emit (ec);
4171 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4175 protected override void EmitTryBody (EmitContext ec)
4178 // Monitor.Enter (expr_copy, ref lock_taken)
4180 if (lock_taken != null) {
4181 expr_copy.Emit (ec);
4182 lock_taken.LocalInfo.CreateBuilder (ec);
4183 lock_taken.AddressOf (ec, AddressOp.Load);
4184 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4187 Statement.Emit (ec);
4190 protected override void EmitFinallyBody (EmitContext ec)
4193 // if (lock_taken) Monitor.Exit (expr_copy)
4195 Label skip = ec.DefineLabel ();
4197 if (lock_taken != null) {
4198 lock_taken.Emit (ec);
4199 ec.Emit (OpCodes.Brfalse_S, skip);
4202 expr_copy.Emit (ec);
4203 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4204 ec.MarkLabel (skip);
4207 int ResolvePredefinedMethods (ResolveContext rc)
4209 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4210 TypeSpec monitor_type = rc.Module.PredefinedTypes.Monitor.Resolve (loc);
4212 if (monitor_type == null)
4215 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4216 var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
4218 new ParameterData (null, Parameter.Modifier.NONE),
4219 new ParameterData (null, Parameter.Modifier.REF)
4222 TypeManager.object_type,
4223 TypeManager.bool_type
4226 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
4227 if (TypeManager.void_monitor_enter_object == null) {
4228 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4229 monitor_type, "Enter", loc, TypeManager.object_type);
4232 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4233 monitor_type, "Exit", loc, TypeManager.object_type);
4236 return TypeManager.void_monitor_enter_object.Parameters.Count;
4239 protected override void CloneTo (CloneContext clonectx, Statement t)
4241 Lock target = (Lock) t;
4243 target.expr = expr.Clone (clonectx);
4244 target.stmt = Statement.Clone (clonectx);
4248 public class Unchecked : Statement {
4251 public Unchecked (Block b, Location loc)
4258 public override bool Resolve (BlockContext ec)
4260 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4261 return Block.Resolve (ec);
4264 protected override void DoEmit (EmitContext ec)
4266 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4270 protected override void CloneTo (CloneContext clonectx, Statement t)
4272 Unchecked target = (Unchecked) t;
4274 target.Block = clonectx.LookupBlock (Block);
4278 public class Checked : Statement {
4281 public Checked (Block b, Location loc)
4284 b.Unchecked = false;
4288 public override bool Resolve (BlockContext ec)
4290 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4291 return Block.Resolve (ec);
4294 protected override void DoEmit (EmitContext ec)
4296 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4300 protected override void CloneTo (CloneContext clonectx, Statement t)
4302 Checked target = (Checked) t;
4304 target.Block = clonectx.LookupBlock (Block);
4308 public class Unsafe : Statement {
4311 public Unsafe (Block b, Location loc)
4314 Block.Unsafe = true;
4318 public override bool Resolve (BlockContext ec)
4320 if (ec.CurrentIterator != null)
4321 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4323 using (ec.Set (ResolveContext.Options.UnsafeScope))
4324 return Block.Resolve (ec);
4327 protected override void DoEmit (EmitContext ec)
4332 protected override void CloneTo (CloneContext clonectx, Statement t)
4334 Unsafe target = (Unsafe) t;
4336 target.Block = clonectx.LookupBlock (Block);
4343 public class Fixed : Statement
4345 abstract class Emitter : ShimExpression
4347 protected LocalVariable vi;
4349 protected Emitter (Expression expr, LocalVariable li)
4355 public abstract void EmitExit (EmitContext ec);
4358 class ExpressionEmitter : Emitter {
4359 public ExpressionEmitter (Expression converted, LocalVariable li) :
4360 base (converted, li)
4364 protected override Expression DoResolve (ResolveContext rc)
4366 throw new NotImplementedException ();
4369 public override void Emit (EmitContext ec) {
4371 // Store pointer in pinned location
4377 public override void EmitExit (EmitContext ec)
4379 ec.Emit (OpCodes.Ldc_I4_0);
4380 ec.Emit (OpCodes.Conv_U);
4385 class StringEmitter : Emitter
4387 LocalVariable pinned_string;
4389 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4394 protected override Expression DoResolve (ResolveContext rc)
4396 pinned_string = new LocalVariable (vi.Block, "$pinned",
4397 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4400 pinned_string.Type = TypeManager.string_type;
4402 if (TypeManager.int_get_offset_to_string_data == null) {
4403 var helper = rc.Module.PredefinedTypes.RuntimeHelpers.Resolve (loc);
4404 if (helper != null) {
4405 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (helper,
4406 "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4410 eclass = ExprClass.Variable;
4411 type = TypeManager.int32_type;
4415 public override void Emit (EmitContext ec)
4417 pinned_string.CreateBuilder (ec);
4420 pinned_string.EmitAssign (ec);
4422 // TODO: Should use Binary::Add
4423 pinned_string.Emit (ec);
4424 ec.Emit (OpCodes.Conv_I);
4426 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4427 //pe.InstanceExpression = pinned_string;
4428 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4430 ec.Emit (OpCodes.Add);
4434 public override void EmitExit (EmitContext ec)
4436 ec.Emit (OpCodes.Ldnull);
4437 pinned_string.EmitAssign (ec);
4441 public class VariableDeclaration : BlockVariableDeclaration
4443 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4448 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4450 if (!Variable.Type.IsPointer && li == Variable) {
4451 bc.Report.Error (209, TypeExpression.Location,
4452 "The type of locals declared in a fixed statement must be a pointer type");
4457 // The rules for the possible declarators are pretty wise,
4458 // but the production on the grammar is more concise.
4460 // So we have to enforce these rules here.
4462 // We do not resolve before doing the case 1 test,
4463 // because the grammar is explicit in that the token &
4464 // is present, so we need to test for this particular case.
4467 if (initializer is Cast) {
4468 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4472 initializer = initializer.Resolve (bc);
4474 if (initializer == null)
4480 if (initializer.Type.IsArray) {
4481 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4484 // Provided that array_type is unmanaged,
4486 if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4490 // and T* is implicitly convertible to the
4491 // pointer type given in the fixed statement.
4493 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4495 Expression converted = Convert.ImplicitConversionRequired (
4496 bc, array_ptr, li.Type, loc);
4497 if (converted == null)
4501 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4503 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4504 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4505 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4506 new NullPointer (loc),
4509 converted = converted.Resolve (bc);
4511 return new ExpressionEmitter (converted, li);
4517 if (initializer.Type == TypeManager.string_type) {
4518 return new StringEmitter (initializer, li, loc).Resolve (bc);
4521 // Case 3: fixed buffer
4522 if (initializer is FixedBufferPtr) {
4523 return new ExpressionEmitter (initializer, li);
4527 // Case 4: & object.
4529 bool already_fixed = true;
4530 Unary u = initializer as Unary;
4531 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4532 IVariableReference vr = u.Expr as IVariableReference;
4533 if (vr == null || !vr.IsFixed) {
4534 already_fixed = false;
4538 if (already_fixed) {
4539 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4542 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4543 return new ExpressionEmitter (initializer, li);
4548 VariableDeclaration decl;
4549 Statement statement;
4552 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4561 public Statement Statement {
4567 public BlockVariableDeclaration Variables {
4575 public override bool Resolve (BlockContext ec)
4577 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4578 if (!decl.Resolve (ec))
4582 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4583 bool ok = statement.Resolve (ec);
4584 bool flow_unreachable = ec.EndFlowBranching ();
4585 has_ret = flow_unreachable;
4590 protected override void DoEmit (EmitContext ec)
4592 decl.Variable.CreateBuilder (ec);
4593 decl.Initializer.Emit (ec);
4594 if (decl.Declarators != null) {
4595 foreach (var d in decl.Declarators) {
4596 d.Variable.CreateBuilder (ec);
4597 d.Initializer.Emit (ec);
4601 statement.Emit (ec);
4607 // Clear the pinned variable
4609 ((Emitter) decl.Initializer).EmitExit (ec);
4610 if (decl.Declarators != null) {
4611 foreach (var d in decl.Declarators) {
4612 ((Emitter)d.Initializer).EmitExit (ec);
4617 protected override void CloneTo (CloneContext clonectx, Statement t)
4619 Fixed target = (Fixed) t;
4621 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4622 target.statement = statement.Clone (clonectx);
4626 public class Catch : Statement
4630 FullNamedExpression type_expr;
4631 CompilerAssign assign;
4634 public Catch (Block block, Location loc)
4642 public Block Block {
4648 public TypeSpec CatchType {
4654 public bool IsGeneral {
4656 return type_expr == null;
4660 public FullNamedExpression TypeExpression {
4669 public LocalVariable Variable {
4680 protected override void DoEmit (EmitContext ec)
4683 ec.BeginCatchBlock (TypeManager.object_type);
4685 ec.BeginCatchBlock (CatchType);
4688 li.CreateBuilder (ec);
4691 // Special case hoisted catch variable, we have to use a temporary variable
4692 // to pass via anonymous storey initialization with the value still on top
4695 if (li.HoistedVariant != null) {
4696 LocalTemporary lt = new LocalTemporary (li.Type);
4697 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4699 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4701 // switch to assigning from the temporary variable and not from top of the stack
4702 assign.UpdateSource (lt);
4705 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4706 ec.Emit (OpCodes.Pop);
4707 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4713 public override bool Resolve (BlockContext ec)
4715 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4716 if (type_expr != null) {
4717 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4722 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4723 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4724 } else if (li != null) {
4726 li.PrepareForFlowAnalysis (ec);
4728 // source variable is at the top of the stack
4729 Expression source = new EmptyExpression (li.Type);
4730 if (li.Type.IsGenericParameter)
4731 source = new UnboxCast (source, li.Type);
4733 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4734 Block.AddScopeStatement (new StatementExpression (assign));
4738 return Block.Resolve (ec);
4742 protected override void CloneTo (CloneContext clonectx, Statement t)
4744 Catch target = (Catch) t;
4746 if (type_expr != null)
4747 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4749 target.block = clonectx.LookupBlock (block);
4753 public class TryFinally : ExceptionStatement {
4756 public TryFinally (Statement stmt, Block fini, Location loc)
4762 public override bool Resolve (BlockContext ec)
4766 ec.StartFlowBranching (this);
4768 if (!stmt.Resolve (ec))
4772 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4773 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4774 if (!fini.Resolve (ec))
4778 ec.EndFlowBranching ();
4780 ok &= base.Resolve (ec);
4785 protected override void EmitPreTryBody (EmitContext ec)
4789 protected override void EmitTryBody (EmitContext ec)
4794 protected override void EmitFinallyBody (EmitContext ec)
4799 protected override void CloneTo (CloneContext clonectx, Statement t)
4801 TryFinally target = (TryFinally) t;
4803 target.stmt = (Statement) stmt.Clone (clonectx);
4805 target.fini = clonectx.LookupBlock (fini);
4809 public class TryCatch : Statement {
4811 public List<Catch> Specific;
4812 public Catch General;
4813 bool inside_try_finally, code_follows;
4815 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4818 this.Specific = catch_clauses;
4819 this.inside_try_finally = inside_try_finally;
4821 Catch c = catch_clauses [0];
4824 catch_clauses.RemoveAt (0);
4830 public override bool Resolve (BlockContext ec)
4834 ec.StartFlowBranching (this);
4836 if (!Block.Resolve (ec))
4839 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4841 foreach (Catch c in Specific){
4842 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4844 if (!c.Resolve (ec)) {
4849 TypeSpec resolved_type = c.CatchType;
4850 for (int ii = 0; ii < last_index; ++ii) {
4851 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4852 ec.Report.Error (160, c.loc,
4853 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4854 TypeManager.CSharpName (prev_catches [ii]));
4859 prev_catches [last_index++] = resolved_type;
4862 if (General != null) {
4863 foreach (Catch c in Specific) {
4864 if (c.CatchType != TypeManager.exception_type)
4867 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
4870 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4873 ec.Report.Warning (1058, 1, c.loc,
4874 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4877 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4879 if (!General.Resolve (ec))
4883 ec.EndFlowBranching ();
4885 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4886 // So, ensure there's some IL code after this statement
4887 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4888 ec.NeedReturnLabel ();
4893 public void SomeCodeFollows ()
4895 code_follows = true;
4898 protected override void DoEmit (EmitContext ec)
4900 if (!inside_try_finally)
4901 ec.BeginExceptionBlock ();
4905 foreach (Catch c in Specific)
4908 if (General != null)
4911 if (!inside_try_finally)
4912 ec.EndExceptionBlock ();
4915 protected override void CloneTo (CloneContext clonectx, Statement t)
4917 TryCatch target = (TryCatch) t;
4919 target.Block = clonectx.LookupBlock (Block);
4920 if (General != null)
4921 target.General = (Catch) General.Clone (clonectx);
4922 if (Specific != null){
4923 target.Specific = new List<Catch> ();
4924 foreach (Catch c in Specific)
4925 target.Specific.Add ((Catch) c.Clone (clonectx));
4930 public class Using : ExceptionStatement
4932 public class VariableDeclaration : BlockVariableDeclaration
4934 Statement dispose_call;
4936 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4941 public VariableDeclaration (LocalVariable li, Location loc)
4947 public VariableDeclaration (Expression expr)
4950 loc = expr.Location;
4956 public bool IsNested { get; private set; }
4960 public void EmitDispose (EmitContext ec)
4962 dispose_call.Emit (ec);
4965 public override bool Resolve (BlockContext bc)
4970 return base.Resolve (bc);
4973 public Expression ResolveExpression (BlockContext bc)
4975 var e = Initializer.Resolve (bc);
4979 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
4980 Initializer = ResolveInitializer (bc, Variable, e);
4984 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4986 if (li.Type == InternalType.Dynamic) {
4987 initializer = initializer.Resolve (bc);
4988 if (initializer == null)
4991 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
4992 Arguments args = new Arguments (1);
4993 args.Add (new Argument (initializer));
4994 initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
4995 if (initializer == null)
4998 var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
4999 dispose_call = CreateDisposeCall (bc, var);
5000 dispose_call.Resolve (bc);
5002 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5005 if (li == Variable) {
5006 CheckIDiposableConversion (bc, li, initializer);
5007 dispose_call = CreateDisposeCall (bc, li);
5008 dispose_call.Resolve (bc);
5011 return base.ResolveInitializer (bc, li, initializer);
5014 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5018 if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5019 if (TypeManager.IsNullableType (type)) {
5020 // it's handled in CreateDisposeCall
5024 bc.Report.SymbolRelatedToPreviousError (type);
5025 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5026 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5027 type.GetSignatureForError ());
5033 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5035 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5037 var loc = lv.Location;
5039 if (TypeManager.void_dispose_void == null) {
5040 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5041 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5044 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5045 dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
5046 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
5049 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5051 // Add conditional call when disposing possible null variable
5052 if (!type.IsStruct || TypeManager.IsNullableType (type))
5053 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5058 public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
5060 for (int i = declarators.Count - 1; i >= 0; --i) {
5061 var d = declarators [i];
5062 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5063 vd.Initializer = d.Initializer;
5065 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5066 vd.dispose_call.Resolve (bc);
5068 stmt = new Using (vd, stmt, d.Variable.Location);
5076 VariableDeclaration decl;
5078 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5084 public Using (Expression expr, Statement stmt, Location loc)
5087 this.decl = new VariableDeclaration (expr);
5092 public Expression Expression {
5094 return decl.Variable == null ? decl.Initializer : null;
5098 public BlockVariableDeclaration Variables {
5106 protected override void EmitPreTryBody (EmitContext ec)
5111 protected override void EmitTryBody (EmitContext ec)
5116 protected override void EmitFinallyBody (EmitContext ec)
5118 decl.EmitDispose (ec);
5121 public override bool Resolve (BlockContext ec)
5123 VariableReference vr;
5124 bool vr_locked = false;
5126 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5127 if (decl.Variable == null) {
5128 vr = decl.ResolveExpression (ec) as VariableReference;
5130 vr_locked = vr.IsLockedByStatement;
5131 vr.IsLockedByStatement = true;
5134 if (!decl.Resolve (ec))
5137 if (decl.Declarators != null) {
5138 stmt = decl.RewriteForDeclarators (ec, stmt);
5145 ec.StartFlowBranching (this);
5149 ec.EndFlowBranching ();
5152 vr.IsLockedByStatement = vr_locked;
5159 protected override void CloneTo (CloneContext clonectx, Statement t)
5161 Using target = (Using) t;
5163 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5164 target.stmt = stmt.Clone (clonectx);
5169 /// Implementation of the foreach C# statement
5171 public class Foreach : Statement {
5173 sealed class ArrayForeach : Statement
5175 readonly Foreach for_each;
5176 readonly Statement statement;
5179 TemporaryVariableReference[] lengths;
5180 Expression [] length_exprs;
5181 StatementExpression[] counter;
5182 TemporaryVariableReference[] variables;
5184 TemporaryVariableReference copy;
5186 LocalVariableReference variable;
5188 public ArrayForeach (Foreach @foreach, int rank)
5190 for_each = @foreach;
5191 statement = for_each.statement;
5193 variable = new LocalVariableReference (for_each.variable, loc);
5195 counter = new StatementExpression[rank];
5196 variables = new TemporaryVariableReference[rank];
5197 length_exprs = new Expression [rank];
5200 // Only use temporary length variables when dealing with
5201 // multi-dimensional arrays
5204 lengths = new TemporaryVariableReference [rank];
5207 protected override void CloneTo (CloneContext clonectx, Statement target)
5209 throw new NotImplementedException ();
5212 public override bool Resolve (BlockContext ec)
5214 Block variables_block = variable.local_info.Block;
5215 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5218 int rank = length_exprs.Length;
5219 Arguments list = new Arguments (rank);
5220 for (int i = 0; i < rank; i++) {
5221 var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5223 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5224 counter[i].Resolve (ec);
5227 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5229 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5230 lengths[i].Resolve (ec);
5232 Arguments args = new Arguments (1);
5233 args.Add (new Argument (new IntConstant (i, loc)));
5234 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5237 list.Add (new Argument (v));
5240 access = new ElementAccess (copy, list, loc).Resolve (ec);
5244 Expression var_type = for_each.type;
5245 VarExpr ve = var_type as VarExpr;
5247 // Infer implicitly typed local variable from foreach array type
5248 var_type = new TypeExpression (access.Type, ve.Location);
5251 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5252 if (var_type == null)
5255 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5261 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5262 ec.CurrentBranching.CreateSibling ();
5264 variable.local_info.Type = conv.Type;
5265 variable.Resolve (ec);
5267 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5268 if (!statement.Resolve (ec))
5270 ec.EndFlowBranching ();
5272 // There's no direct control flow from the end of the embedded statement to the end of the loop
5273 ec.CurrentBranching.CurrentUsageVector.Goto ();
5275 ec.EndFlowBranching ();
5280 protected override void DoEmit (EmitContext ec)
5282 copy.EmitAssign (ec, for_each.expr);
5284 int rank = length_exprs.Length;
5285 Label[] test = new Label [rank];
5286 Label[] loop = new Label [rank];
5288 for (int i = 0; i < rank; i++) {
5289 test [i] = ec.DefineLabel ();
5290 loop [i] = ec.DefineLabel ();
5292 if (lengths != null)
5293 lengths [i].EmitAssign (ec, length_exprs [i]);
5296 IntConstant zero = new IntConstant (0, loc);
5297 for (int i = 0; i < rank; i++) {
5298 variables [i].EmitAssign (ec, zero);
5300 ec.Emit (OpCodes.Br, test [i]);
5301 ec.MarkLabel (loop [i]);
5304 variable.local_info.CreateBuilder (ec);
5305 variable.EmitAssign (ec, conv, false, false);
5307 statement.Emit (ec);
5309 ec.MarkLabel (ec.LoopBegin);
5311 for (int i = rank - 1; i >= 0; i--){
5312 counter [i].Emit (ec);
5314 ec.MarkLabel (test [i]);
5315 variables [i].Emit (ec);
5317 if (lengths != null)
5318 lengths [i].Emit (ec);
5320 length_exprs [i].Emit (ec);
5322 ec.Emit (OpCodes.Blt, loop [i]);
5325 ec.MarkLabel (ec.LoopEnd);
5329 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5331 class Body : Statement
5334 LocalVariableReference variable;
5335 Expression current, conv;
5336 Statement statement;
5338 public Body (TypeSpec type, LocalVariable variable,
5339 Expression current, Statement statement,
5343 this.variable = new LocalVariableReference (variable, loc);
5344 this.current = current;
5345 this.statement = statement;
5349 protected override void CloneTo (CloneContext clonectx, Statement target)
5351 throw new NotImplementedException ();
5354 public override bool Resolve (BlockContext ec)
5356 current = current.Resolve (ec);
5357 if (current == null)
5360 conv = Convert.ExplicitConversion (ec, current, type, loc);
5364 variable.local_info.Type = conv.Type;
5365 variable.Resolve (ec);
5367 if (!statement.Resolve (ec))
5373 protected override void DoEmit (EmitContext ec)
5375 variable.local_info.CreateBuilder (ec);
5376 variable.EmitAssign (ec, conv, false, false);
5378 statement.Emit (ec);
5382 class RuntimeDispose : Using.VariableDeclaration
5384 public RuntimeDispose (LocalVariable lv, Location loc)
5389 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5391 // Defered to runtime check
5394 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5396 if (TypeManager.void_dispose_void == null) {
5397 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5398 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5402 // Fabricates code like
5404 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5407 var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5409 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5410 dispose_variable.CreateReferenceExpression (bc, loc),
5411 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5412 loc), new NullLiteral (loc), loc);
5414 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5415 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5417 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5418 return new If (idisaposable_test, dispose, loc);
5422 LocalVariable variable;
5424 Statement statement;
5425 Expression var_type;
5426 ExpressionStatement init;
5427 TemporaryVariableReference enumerator_variable;
5428 bool ambiguous_getenumerator_name;
5430 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5432 this.var_type = var_type;
5433 this.variable = var;
5439 protected override void CloneTo (CloneContext clonectx, Statement target)
5441 throw new NotImplementedException ();
5444 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5446 rc.Report.SymbolRelatedToPreviousError (enumerator);
5447 rc.Report.Error (202, loc,
5448 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5449 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5452 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5455 // Option 1: Try to match by name GetEnumerator first
5457 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
5458 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5460 var mg = mexpr as MethodGroupExpr;
5462 mg.InstanceExpression = expr;
5463 Arguments args = new Arguments (0);
5464 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5466 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5467 if (ambiguous_getenumerator_name)
5470 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5476 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5478 TypeSpec iface_candidate = null;
5479 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5480 var ifaces = t.Interfaces;
5481 if (ifaces != null) {
5482 foreach (var iface in ifaces) {
5483 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5484 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5485 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5486 rc.Report.Error (1640, loc,
5487 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5488 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5493 iface_candidate = iface;
5497 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5498 iface_candidate = iface;
5504 if (iface_candidate == null) {
5505 rc.Report.Error (1579, loc,
5506 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5507 expr.Type.GetSignatureForError (), "GetEnumerator");
5512 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5513 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5518 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5519 mg.InstanceExpression = expr;
5523 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5525 var ms = MemberCache.FindMember (enumerator.ReturnType,
5526 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5527 BindingRestriction.InstanceOnly) as MethodSpec;
5529 if (ms == null || !ms.IsPublic) {
5530 Error_WrongEnumerator (rc, enumerator);
5534 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5537 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5539 var ps = MemberCache.FindMember (enumerator.ReturnType,
5540 MemberFilter.Property ("Current", null),
5541 BindingRestriction.InstanceOnly) as PropertySpec;
5543 if (ps == null || !ps.IsPublic) {
5544 Error_WrongEnumerator (rc, enumerator);
5551 public override bool Resolve (BlockContext ec)
5553 bool is_dynamic = expr.Type == InternalType.Dynamic;
5556 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5557 } else if (TypeManager.IsNullableType (expr.Type)) {
5558 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5561 var get_enumerator_mg = ResolveGetEnumerator (ec);
5562 if (get_enumerator_mg == null) {
5566 var get_enumerator = get_enumerator_mg.BestCandidate;
5567 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5568 enumerator_variable.Resolve (ec);
5570 // Prepare bool MoveNext ()
5571 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5572 if (move_next_mg == null) {
5576 move_next_mg.InstanceExpression = enumerator_variable;
5578 // Prepare ~T~ Current { get; }
5579 var current_prop = ResolveCurrent (ec, get_enumerator);
5580 if (current_prop == null) {
5584 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5585 if (current_pe == null)
5588 VarExpr ve = var_type as VarExpr;
5591 // Source type is dynamic, set element type to dynamic too
5592 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5594 // Infer implicitly typed local variable from foreach enumerable type
5595 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5597 } else if (is_dynamic) {
5598 // Explicit cast of dynamic collection elements has to be done at runtime
5599 current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5602 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5603 if (var_type == null)
5606 variable.Type = var_type.Type;
5608 var init = new Invocation (get_enumerator_mg, null);
5610 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5611 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5613 var enum_type = enumerator_variable.Type;
5616 // Add Dispose method call when enumerator can be IDisposable
5618 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5619 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5621 // Runtime Dispose check
5623 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5624 vd.Initializer = init;
5625 statement = new Using (vd, statement, loc);
5628 // No Dispose call needed
5630 this.init = new SimpleAssign (enumerator_variable, init);
5631 this.init.Resolve (ec);
5635 // Static Dispose check
5637 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5638 vd.Initializer = init;
5639 statement = new Using (vd, statement, loc);
5642 return statement.Resolve (ec);
5645 protected override void DoEmit (EmitContext ec)
5647 enumerator_variable.LocalInfo.CreateBuilder (ec);
5650 init.EmitStatement (ec);
5652 statement.Emit (ec);
5655 #region IErrorHandler Members
5657 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5659 ec.Report.SymbolRelatedToPreviousError (best);
5660 ec.Report.Warning (278, 2, loc,
5661 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5662 expr.Type.GetSignatureForError (), "enumerable",
5663 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5665 ambiguous_getenumerator_name = true;
5669 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5674 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5679 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5688 LocalVariable variable;
5690 Statement statement;
5692 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5695 this.variable = var;
5701 public Statement Statement {
5702 get { return statement; }
5705 public override bool Resolve (BlockContext ec)
5707 expr = expr.Resolve (ec);
5712 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5716 if (expr.Type == TypeManager.string_type) {
5717 statement = new ArrayForeach (this, 1);
5718 } else if (expr.Type is ArrayContainer) {
5719 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5721 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5722 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5723 expr.ExprClassName);
5727 statement = new CollectionForeach (type, variable, expr, statement, loc);
5730 return statement.Resolve (ec);
5733 protected override void DoEmit (EmitContext ec)
5735 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5736 ec.LoopBegin = ec.DefineLabel ();
5737 ec.LoopEnd = ec.DefineLabel ();
5739 statement.Emit (ec);
5741 ec.LoopBegin = old_begin;
5742 ec.LoopEnd = old_end;
5745 protected override void CloneTo (CloneContext clonectx, Statement t)
5747 Foreach target = (Foreach) t;
5749 target.type = type.Clone (clonectx);
5750 target.expr = expr.Clone (clonectx);
5751 target.statement = statement.Clone (clonectx);