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)
3096 return new SwitchLabel (label.Clone (clonectx), loc);
3100 public class SwitchSection {
3101 public readonly List<SwitchLabel> Labels;
3102 public readonly Block Block;
3104 public SwitchSection (List<SwitchLabel> labels, Block block)
3110 public SwitchSection Clone (CloneContext clonectx)
3112 var cloned_labels = new List<SwitchLabel> ();
3114 foreach (SwitchLabel sl in Labels)
3115 cloned_labels.Add (sl.Clone (clonectx));
3117 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3121 public class Switch : Statement {
3122 public List<SwitchSection> Sections;
3123 public Expression Expr;
3126 /// Maps constants whose type type SwitchType to their SwitchLabels.
3128 public IDictionary<object, SwitchLabel> Elements;
3131 /// The governing switch type
3133 public TypeSpec SwitchType;
3138 Label default_target;
3140 Expression new_expr;
3143 SwitchSection constant_section;
3144 SwitchSection default_section;
3146 ExpressionStatement string_dictionary;
3147 FieldExpr switch_cache_field;
3148 static int unique_counter;
3149 ExplicitBlock block;
3152 // Nullable Types support
3154 Nullable.Unwrap unwrap;
3156 protected bool HaveUnwrap {
3157 get { return unwrap != null; }
3161 // The types allowed to be implicitly cast from
3162 // on the governing type
3164 static TypeSpec [] allowed_types;
3166 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3174 public ExplicitBlock Block {
3180 public bool GotDefault {
3182 return default_section != null;
3186 public Label DefaultTarget {
3188 return default_target;
3193 // Determines the governing type for a switch. The returned
3194 // expression might be the expression from the switch, or an
3195 // expression that includes any potential conversions to the
3196 // integral types or to string.
3198 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3200 TypeSpec t = expr.Type;
3202 if (t == TypeManager.byte_type ||
3203 t == TypeManager.sbyte_type ||
3204 t == TypeManager.ushort_type ||
3205 t == TypeManager.short_type ||
3206 t == TypeManager.uint32_type ||
3207 t == TypeManager.int32_type ||
3208 t == TypeManager.uint64_type ||
3209 t == TypeManager.int64_type ||
3210 t == TypeManager.char_type ||
3211 t == TypeManager.string_type ||
3212 t == TypeManager.bool_type ||
3213 TypeManager.IsEnumType (t))
3216 if (allowed_types == null){
3217 allowed_types = new TypeSpec [] {
3218 TypeManager.sbyte_type,
3219 TypeManager.byte_type,
3220 TypeManager.short_type,
3221 TypeManager.ushort_type,
3222 TypeManager.int32_type,
3223 TypeManager.uint32_type,
3224 TypeManager.int64_type,
3225 TypeManager.uint64_type,
3226 TypeManager.char_type,
3227 TypeManager.string_type
3232 // Try to find a *user* defined implicit conversion.
3234 // If there is no implicit conversion, or if there are multiple
3235 // conversions, we have to report an error
3237 Expression converted = null;
3238 foreach (TypeSpec tt in allowed_types){
3241 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3246 // Ignore over-worked ImplicitUserConversions that do
3247 // an implicit conversion in addition to the user conversion.
3249 if (!(e is UserCast))
3252 if (converted != null){
3253 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3263 // Performs the basic sanity checks on the switch statement
3264 // (looks for duplicate keys and non-constant expressions).
3266 // It also returns a hashtable with the keys that we will later
3267 // use to compute the switch tables
3269 bool CheckSwitch (ResolveContext ec)
3272 Elements = new Dictionary<object, SwitchLabel> ();
3274 foreach (SwitchSection ss in Sections){
3275 foreach (SwitchLabel sl in ss.Labels){
3276 if (sl.Label == null){
3277 if (default_section != null){
3278 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3281 default_section = ss;
3285 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3290 object key = sl.Converted;
3291 if (key == SwitchLabel.NullStringCase)
3292 has_null_case = true;
3295 Elements.Add (key, sl);
3296 } catch (ArgumentException) {
3297 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3305 void EmitObjectInteger (EmitContext ec, object k)
3308 ec.EmitInt ((int) k);
3309 else if (k is Constant) {
3310 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3313 ec.EmitInt (unchecked ((int) (uint) k));
3316 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3318 ec.EmitInt ((int) (long) k);
3319 ec.Emit (OpCodes.Conv_I8);
3322 ec.EmitLong ((long) k);
3324 else if (k is ulong)
3326 ulong ul = (ulong) k;
3329 ec.EmitInt (unchecked ((int) ul));
3330 ec.Emit (OpCodes.Conv_U8);
3334 ec.EmitLong (unchecked ((long) ul));
3338 ec.EmitInt ((int) ((char) k));
3339 else if (k is sbyte)
3340 ec.EmitInt ((int) ((sbyte) k));
3342 ec.EmitInt ((int) ((byte) k));
3343 else if (k is short)
3344 ec.EmitInt ((int) ((short) k));
3345 else if (k is ushort)
3346 ec.EmitInt ((int) ((ushort) k));
3348 ec.EmitInt (((bool) k) ? 1 : 0);
3350 throw new Exception ("Unhandled case");
3353 // structure used to hold blocks of keys while calculating table switch
3354 class KeyBlock : IComparable
3356 public KeyBlock (long _first)
3358 first = last = _first;
3362 public List<object> element_keys;
3363 // how many items are in the bucket
3364 public int Size = 1;
3367 get { return (int) (last - first + 1); }
3369 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3371 return kb_last.last - kb_first.first + 1;
3373 public int CompareTo (object obj)
3375 KeyBlock kb = (KeyBlock) obj;
3376 int nLength = Length;
3377 int nLengthOther = kb.Length;
3378 if (nLengthOther == nLength)
3379 return (int) (kb.first - first);
3380 return nLength - nLengthOther;
3385 /// This method emits code for a lookup-based switch statement (non-string)
3386 /// Basically it groups the cases into blocks that are at least half full,
3387 /// and then spits out individual lookup opcodes for each block.
3388 /// It emits the longest blocks first, and short blocks are just
3389 /// handled with direct compares.
3391 /// <param name="ec"></param>
3392 /// <param name="val"></param>
3393 /// <returns></returns>
3394 void TableSwitchEmit (EmitContext ec, Expression val)
3396 int element_count = Elements.Count;
3397 object [] element_keys = new object [element_count];
3398 Elements.Keys.CopyTo (element_keys, 0);
3399 Array.Sort (element_keys);
3401 // initialize the block list with one element per key
3402 var key_blocks = new List<KeyBlock> (element_count);
3403 foreach (object key in element_keys)
3404 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3406 KeyBlock current_kb;
3407 // iteratively merge the blocks while they are at least half full
3408 // there's probably a really cool way to do this with a tree...
3409 while (key_blocks.Count > 1)
3411 var key_blocks_new = new List<KeyBlock> ();
3412 current_kb = (KeyBlock) key_blocks [0];
3413 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3415 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3416 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3419 current_kb.last = kb.last;
3420 current_kb.Size += kb.Size;
3424 // start a new block
3425 key_blocks_new.Add (current_kb);
3429 key_blocks_new.Add (current_kb);
3430 if (key_blocks.Count == key_blocks_new.Count)
3432 key_blocks = key_blocks_new;
3435 // initialize the key lists
3436 foreach (KeyBlock kb in key_blocks)
3437 kb.element_keys = new List<object> ();
3439 // fill the key lists
3441 if (key_blocks.Count > 0) {
3442 current_kb = (KeyBlock) key_blocks [0];
3443 foreach (object key in element_keys)
3445 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3446 System.Convert.ToInt64 (key) > current_kb.last;
3448 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3449 current_kb.element_keys.Add (key);
3453 // sort the blocks so we can tackle the largest ones first
3456 // okay now we can start...
3457 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3458 Label lbl_default = default_target;
3460 Type type_keys = null;
3461 if (element_keys.Length > 0)
3462 type_keys = element_keys [0].GetType (); // used for conversions
3464 TypeSpec compare_type;
3466 if (TypeManager.IsEnumType (SwitchType))
3467 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3469 compare_type = SwitchType;
3471 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3473 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3474 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3477 foreach (object key in kb.element_keys) {
3478 SwitchLabel sl = (SwitchLabel) Elements [key];
3479 if (key is int && (int) key == 0) {
3480 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3483 EmitObjectInteger (ec, key);
3484 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3490 // TODO: if all the keys in the block are the same and there are
3491 // no gaps/defaults then just use a range-check.
3492 if (compare_type == TypeManager.int64_type ||
3493 compare_type == TypeManager.uint64_type)
3495 // TODO: optimize constant/I4 cases
3497 // check block range (could be > 2^31)
3499 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3500 ec.Emit (OpCodes.Blt, lbl_default);
3502 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3503 ec.Emit (OpCodes.Bgt, lbl_default);
3509 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3510 ec.Emit (OpCodes.Sub);
3512 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3518 int first = (int) kb.first;
3522 ec.Emit (OpCodes.Sub);
3526 ec.EmitInt (-first);
3527 ec.Emit (OpCodes.Add);
3531 // first, build the list of labels for the switch
3533 int cJumps = kb.Length;
3534 Label [] switch_labels = new Label [cJumps];
3535 for (int iJump = 0; iJump < cJumps; iJump++)
3537 object key = kb.element_keys [iKey];
3538 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3540 SwitchLabel sl = (SwitchLabel) Elements [key];
3541 switch_labels [iJump] = sl.GetILLabel (ec);
3545 switch_labels [iJump] = lbl_default;
3547 // emit the switch opcode
3548 ec.Emit (OpCodes.Switch, switch_labels);
3551 // mark the default for this block
3553 ec.MarkLabel (lbl_default);
3556 // TODO: find the default case and emit it here,
3557 // to prevent having to do the following jump.
3558 // make sure to mark other labels in the default section
3560 // the last default just goes to the end
3561 if (element_keys.Length > 0)
3562 ec.Emit (OpCodes.Br, lbl_default);
3564 // now emit the code for the sections
3565 bool found_default = false;
3567 foreach (SwitchSection ss in Sections) {
3568 foreach (SwitchLabel sl in ss.Labels) {
3569 if (sl.Converted == SwitchLabel.NullStringCase) {
3570 ec.MarkLabel (null_target);
3571 } else if (sl.Label == null) {
3572 ec.MarkLabel (lbl_default);
3573 found_default = true;
3575 ec.MarkLabel (null_target);
3577 ec.MarkLabel (sl.GetILLabel (ec));
3578 ec.MarkLabel (sl.GetILLabelCode (ec));
3583 if (!found_default) {
3584 ec.MarkLabel (lbl_default);
3585 if (!has_null_case) {
3586 ec.MarkLabel (null_target);
3590 ec.MarkLabel (lbl_end);
3593 SwitchSection FindSection (SwitchLabel label)
3595 foreach (SwitchSection ss in Sections){
3596 foreach (SwitchLabel sl in ss.Labels){
3605 public static void Reset ()
3608 allowed_types = null;
3611 public override bool Resolve (BlockContext ec)
3613 Expr = Expr.Resolve (ec);
3617 new_expr = SwitchGoverningType (ec, Expr);
3619 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3620 unwrap = Nullable.Unwrap.Create (Expr, false);
3624 new_expr = SwitchGoverningType (ec, unwrap);
3627 if (new_expr == null){
3628 ec.Report.Error (151, loc,
3629 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3630 TypeManager.CSharpName (Expr.Type));
3635 SwitchType = new_expr.Type;
3637 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3638 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3642 if (!CheckSwitch (ec))
3646 Elements.Remove (SwitchLabel.NullStringCase);
3648 Switch old_switch = ec.Switch;
3650 ec.Switch.SwitchType = SwitchType;
3652 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3653 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3655 var constant = new_expr as Constant;
3656 if (constant != null) {
3658 object key = constant.GetValue ();
3660 if (Elements.TryGetValue (key, out label))
3661 constant_section = FindSection (label);
3663 if (constant_section == null)
3664 constant_section = default_section;
3669 foreach (SwitchSection ss in Sections){
3671 ec.CurrentBranching.CreateSibling (
3672 null, FlowBranching.SiblingType.SwitchSection);
3676 if (is_constant && (ss != constant_section)) {
3677 // If we're a constant switch, we're only emitting
3678 // one single section - mark all the others as
3680 ec.CurrentBranching.CurrentUsageVector.Goto ();
3681 if (!ss.Block.ResolveUnreachable (ec, true)) {
3685 if (!ss.Block.Resolve (ec))
3690 if (default_section == null)
3691 ec.CurrentBranching.CreateSibling (
3692 null, FlowBranching.SiblingType.SwitchSection);
3694 ec.EndFlowBranching ();
3695 ec.Switch = old_switch;
3697 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3702 if (SwitchType == TypeManager.string_type && !is_constant) {
3703 // TODO: Optimize single case, and single+default case
3704 ResolveStringSwitchMap (ec);
3710 void ResolveStringSwitchMap (ResolveContext ec)
3712 FullNamedExpression string_dictionary_type;
3713 if (TypeManager.generic_ienumerable_type != null) {
3714 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3715 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3717 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3719 new TypeExpression (TypeManager.string_type, loc),
3720 new TypeExpression (TypeManager.int32_type, loc)), loc);
3722 MemberAccess system_collections_generic = new MemberAccess (
3723 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3725 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3728 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3729 Field field = new Field (ctype, string_dictionary_type,
3730 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3731 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3732 if (!field.Define ())
3734 ctype.AddField (field);
3736 var init = new List<Expression> ();
3739 string value = null;
3740 foreach (SwitchSection section in Sections) {
3741 int last_count = init.Count;
3742 foreach (SwitchLabel sl in section.Labels) {
3743 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3746 value = (string) sl.Converted;
3747 var init_args = new List<Expression> (2);
3748 init_args.Add (new StringLiteral (value, sl.Location));
3749 init_args.Add (new IntConstant (counter, loc));
3750 init.Add (new CollectionElementInitializer (init_args, loc));
3754 // Don't add empty sections
3756 if (last_count == init.Count)
3759 Elements.Add (counter, section.Labels [0]);
3763 Arguments args = new Arguments (1);
3764 args.Add (new Argument (new IntConstant (init.Count, loc)));
3765 Expression initializer = new NewInitialize (string_dictionary_type, args,
3766 new CollectionOrObjectInitializers (init, loc), loc);
3768 switch_cache_field = new FieldExpr (field, loc);
3769 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3772 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3774 Label l_initialized = ec.DefineLabel ();
3777 // Skip initialization when value is null
3779 value.EmitBranchable (ec, null_target, false);
3782 // Check if string dictionary is initialized and initialize
3784 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3785 string_dictionary.EmitStatement (ec);
3786 ec.MarkLabel (l_initialized);
3788 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3790 ResolveContext rc = new ResolveContext (ec.MemberContext);
3792 if (TypeManager.generic_ienumerable_type != null) {
3793 Arguments get_value_args = new Arguments (2);
3794 get_value_args.Add (new Argument (value));
3795 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3796 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3797 if (get_item == null)
3801 // A value was not found, go to default case
3803 get_item.EmitBranchable (ec, default_target, false);
3805 Arguments get_value_args = new Arguments (1);
3806 get_value_args.Add (new Argument (value));
3808 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3809 if (get_item == null)
3812 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3813 get_item_object.EmitAssign (ec, get_item, true, false);
3814 ec.Emit (OpCodes.Brfalse, default_target);
3816 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3817 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3819 get_item_int.EmitStatement (ec);
3820 get_item_object.Release (ec);
3823 TableSwitchEmit (ec, string_switch_variable);
3824 string_switch_variable.Release (ec);
3827 protected override void DoEmit (EmitContext ec)
3830 // Needed to emit anonymous storey initialization
3831 // Otherwise it does not contain any statements for now
3835 default_target = ec.DefineLabel ();
3836 null_target = ec.DefineLabel ();
3838 // Store variable for comparission purposes
3839 // TODO: Don't duplicate non-captured VariableReference
3840 LocalTemporary value;
3842 value = new LocalTemporary (SwitchType);
3843 unwrap.EmitCheck (ec);
3844 ec.Emit (OpCodes.Brfalse, null_target);
3847 } else if (!is_constant) {
3848 value = new LocalTemporary (SwitchType);
3855 // Setup the codegen context
3857 Label old_end = ec.LoopEnd;
3858 Switch old_switch = ec.Switch;
3860 ec.LoopEnd = ec.DefineLabel ();
3865 if (constant_section != null)
3866 constant_section.Block.Emit (ec);
3867 } else if (string_dictionary != null) {
3868 DoEmitStringSwitch (value, ec);
3870 TableSwitchEmit (ec, value);
3876 // Restore context state.
3877 ec.MarkLabel (ec.LoopEnd);
3880 // Restore the previous context
3882 ec.LoopEnd = old_end;
3883 ec.Switch = old_switch;
3886 protected override void CloneTo (CloneContext clonectx, Statement t)
3888 Switch target = (Switch) t;
3890 target.Expr = Expr.Clone (clonectx);
3891 target.Sections = new List<SwitchSection> ();
3892 foreach (SwitchSection ss in Sections){
3893 target.Sections.Add (ss.Clone (clonectx));
3898 // A place where execution can restart in an iterator
3899 public abstract class ResumableStatement : Statement
3902 protected Label resume_point;
3904 public Label PrepareForEmit (EmitContext ec)
3908 resume_point = ec.DefineLabel ();
3910 return resume_point;
3913 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3917 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3922 // Base class for statements that are implemented in terms of try...finally
3923 public abstract class ExceptionStatement : ResumableStatement
3927 List<ResumableStatement> resume_points;
3928 int first_resume_pc;
3929 protected Statement stmt;
3930 Label dispose_try_block;
3931 bool prepared_for_dispose, emitted_dispose;
3933 protected ExceptionStatement (Statement stmt, Location loc)
3941 public Statement Statement {
3949 protected abstract void EmitPreTryBody (EmitContext ec);
3950 protected abstract void EmitTryBody (EmitContext ec);
3951 protected abstract void EmitFinallyBody (EmitContext ec);
3953 protected sealed override void DoEmit (EmitContext ec)
3955 EmitPreTryBody (ec);
3957 if (resume_points != null) {
3958 ec.EmitInt ((int) Iterator.State.Running);
3959 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3962 ec.BeginExceptionBlock ();
3964 if (resume_points != null) {
3965 ec.MarkLabel (resume_point);
3967 // For normal control flow, we want to fall-through the Switch
3968 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3969 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3970 ec.EmitInt (first_resume_pc);
3971 ec.Emit (OpCodes.Sub);
3973 Label [] labels = new Label [resume_points.Count];
3974 for (int i = 0; i < resume_points.Count; ++i)
3975 labels [i] = resume_points [i].PrepareForEmit (ec);
3976 ec.Emit (OpCodes.Switch, labels);
3981 ec.BeginFinallyBlock ();
3983 Label start_finally = ec.DefineLabel ();
3984 if (resume_points != null) {
3985 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3986 ec.Emit (OpCodes.Brfalse_S, start_finally);
3987 ec.Emit (OpCodes.Endfinally);
3990 ec.MarkLabel (start_finally);
3991 EmitFinallyBody (ec);
3993 ec.EndExceptionBlock ();
3996 public void SomeCodeFollows ()
3998 code_follows = true;
4001 public override bool Resolve (BlockContext ec)
4003 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4004 // So, ensure there's some IL code after this statement.
4005 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4006 ec.NeedReturnLabel ();
4008 iter = ec.CurrentIterator;
4012 public void AddResumePoint (ResumableStatement stmt, int pc)
4014 if (resume_points == null) {
4015 resume_points = new List<ResumableStatement> ();
4016 first_resume_pc = pc;
4019 if (pc != first_resume_pc + resume_points.Count)
4020 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4022 resume_points.Add (stmt);
4025 public override Label PrepareForDispose (EmitContext ec, Label end)
4027 if (!prepared_for_dispose) {
4028 prepared_for_dispose = true;
4029 dispose_try_block = ec.DefineLabel ();
4031 return dispose_try_block;
4034 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4036 if (emitted_dispose)
4039 emitted_dispose = true;
4041 Label end_of_try = ec.DefineLabel ();
4043 // Ensure that the only way we can get into this code is through a dispatcher
4044 if (have_dispatcher)
4045 ec.Emit (OpCodes.Br, end);
4047 ec.BeginExceptionBlock ();
4049 ec.MarkLabel (dispose_try_block);
4051 Label [] labels = null;
4052 for (int i = 0; i < resume_points.Count; ++i) {
4053 ResumableStatement s = (ResumableStatement) resume_points [i];
4054 Label ret = s.PrepareForDispose (ec, end_of_try);
4055 if (ret.Equals (end_of_try) && labels == null)
4057 if (labels == null) {
4058 labels = new Label [resume_points.Count];
4059 for (int j = 0; j < i; ++j)
4060 labels [j] = end_of_try;
4065 if (labels != null) {
4067 for (j = 1; j < labels.Length; ++j)
4068 if (!labels [0].Equals (labels [j]))
4070 bool emit_dispatcher = j < labels.Length;
4072 if (emit_dispatcher) {
4073 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4074 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4075 ec.EmitInt (first_resume_pc);
4076 ec.Emit (OpCodes.Sub);
4077 ec.Emit (OpCodes.Switch, labels);
4078 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4081 foreach (ResumableStatement s in resume_points)
4082 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4085 ec.MarkLabel (end_of_try);
4087 ec.BeginFinallyBlock ();
4089 EmitFinallyBody (ec);
4091 ec.EndExceptionBlock ();
4095 public class Lock : ExceptionStatement {
4097 TemporaryVariableReference expr_copy;
4098 TemporaryVariableReference lock_taken;
4100 public Lock (Expression expr, Statement stmt, Location loc)
4106 public override bool Resolve (BlockContext ec)
4108 expr = expr.Resolve (ec);
4112 if (!TypeManager.IsReferenceType (expr.Type)){
4113 ec.Report.Error (185, loc,
4114 "`{0}' is not a reference type as required by the lock statement",
4115 expr.Type.GetSignatureForError ());
4118 if (expr.Type.IsGenericParameter) {
4119 expr = Convert.ImplicitTypeParameterConversion (expr, TypeManager.object_type);
4122 VariableReference lv = expr as VariableReference;
4125 locked = lv.IsLockedByStatement;
4126 lv.IsLockedByStatement = true;
4132 ec.StartFlowBranching (this);
4133 Statement.Resolve (ec);
4134 ec.EndFlowBranching ();
4137 lv.IsLockedByStatement = locked;
4143 // Have to keep original lock value around to unlock same location
4144 // in the case the original has changed or is null
4146 expr_copy = TemporaryVariableReference.Create (TypeManager.object_type, ec.CurrentBlock.Parent, loc);
4147 expr_copy.Resolve (ec);
4150 // Ensure Monitor methods are available
4152 if (ResolvePredefinedMethods (ec) > 1) {
4153 lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
4154 lock_taken.Resolve (ec);
4160 protected override void EmitPreTryBody (EmitContext ec)
4162 expr_copy.EmitAssign (ec, expr);
4164 if (lock_taken != null) {
4166 // Initialize ref variable
4168 lock_taken.EmitAssign (ec, new BoolLiteral (false, loc));
4171 // Monitor.Enter (expr_copy)
4173 expr_copy.Emit (ec);
4174 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4178 protected override void EmitTryBody (EmitContext ec)
4181 // Monitor.Enter (expr_copy, ref lock_taken)
4183 if (lock_taken != null) {
4184 expr_copy.Emit (ec);
4185 lock_taken.LocalInfo.CreateBuilder (ec);
4186 lock_taken.AddressOf (ec, AddressOp.Load);
4187 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4190 Statement.Emit (ec);
4193 protected override void EmitFinallyBody (EmitContext ec)
4196 // if (lock_taken) Monitor.Exit (expr_copy)
4198 Label skip = ec.DefineLabel ();
4200 if (lock_taken != null) {
4201 lock_taken.Emit (ec);
4202 ec.Emit (OpCodes.Brfalse_S, skip);
4205 expr_copy.Emit (ec);
4206 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4207 ec.MarkLabel (skip);
4210 int ResolvePredefinedMethods (ResolveContext rc)
4212 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4213 TypeSpec monitor_type = rc.Module.PredefinedTypes.Monitor.Resolve (loc);
4215 if (monitor_type == null)
4218 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4219 var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
4221 new ParameterData (null, Parameter.Modifier.NONE),
4222 new ParameterData (null, Parameter.Modifier.REF)
4225 TypeManager.object_type,
4226 TypeManager.bool_type
4229 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
4230 if (TypeManager.void_monitor_enter_object == null) {
4231 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4232 monitor_type, "Enter", loc, TypeManager.object_type);
4235 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4236 monitor_type, "Exit", loc, TypeManager.object_type);
4239 return TypeManager.void_monitor_enter_object.Parameters.Count;
4242 protected override void CloneTo (CloneContext clonectx, Statement t)
4244 Lock target = (Lock) t;
4246 target.expr = expr.Clone (clonectx);
4247 target.stmt = Statement.Clone (clonectx);
4251 public class Unchecked : Statement {
4254 public Unchecked (Block b, Location loc)
4261 public override bool Resolve (BlockContext ec)
4263 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4264 return Block.Resolve (ec);
4267 protected override void DoEmit (EmitContext ec)
4269 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4273 protected override void CloneTo (CloneContext clonectx, Statement t)
4275 Unchecked target = (Unchecked) t;
4277 target.Block = clonectx.LookupBlock (Block);
4281 public class Checked : Statement {
4284 public Checked (Block b, Location loc)
4287 b.Unchecked = false;
4291 public override bool Resolve (BlockContext ec)
4293 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4294 return Block.Resolve (ec);
4297 protected override void DoEmit (EmitContext ec)
4299 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4303 protected override void CloneTo (CloneContext clonectx, Statement t)
4305 Checked target = (Checked) t;
4307 target.Block = clonectx.LookupBlock (Block);
4311 public class Unsafe : Statement {
4314 public Unsafe (Block b, Location loc)
4317 Block.Unsafe = true;
4321 public override bool Resolve (BlockContext ec)
4323 if (ec.CurrentIterator != null)
4324 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4326 using (ec.Set (ResolveContext.Options.UnsafeScope))
4327 return Block.Resolve (ec);
4330 protected override void DoEmit (EmitContext ec)
4335 protected override void CloneTo (CloneContext clonectx, Statement t)
4337 Unsafe target = (Unsafe) t;
4339 target.Block = clonectx.LookupBlock (Block);
4346 public class Fixed : Statement
4348 abstract class Emitter : ShimExpression
4350 protected LocalVariable vi;
4352 protected Emitter (Expression expr, LocalVariable li)
4358 public abstract void EmitExit (EmitContext ec);
4361 class ExpressionEmitter : Emitter {
4362 public ExpressionEmitter (Expression converted, LocalVariable li) :
4363 base (converted, li)
4367 protected override Expression DoResolve (ResolveContext rc)
4369 throw new NotImplementedException ();
4372 public override void Emit (EmitContext ec) {
4374 // Store pointer in pinned location
4380 public override void EmitExit (EmitContext ec)
4382 ec.Emit (OpCodes.Ldc_I4_0);
4383 ec.Emit (OpCodes.Conv_U);
4388 class StringEmitter : Emitter
4390 LocalVariable pinned_string;
4392 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4397 protected override Expression DoResolve (ResolveContext rc)
4399 pinned_string = new LocalVariable (vi.Block, "$pinned",
4400 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4403 pinned_string.Type = TypeManager.string_type;
4405 if (TypeManager.int_get_offset_to_string_data == null) {
4406 var helper = rc.Module.PredefinedTypes.RuntimeHelpers.Resolve (loc);
4407 if (helper != null) {
4408 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (helper,
4409 "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4413 eclass = ExprClass.Variable;
4414 type = TypeManager.int32_type;
4418 public override void Emit (EmitContext ec)
4420 pinned_string.CreateBuilder (ec);
4423 pinned_string.EmitAssign (ec);
4425 // TODO: Should use Binary::Add
4426 pinned_string.Emit (ec);
4427 ec.Emit (OpCodes.Conv_I);
4429 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4430 //pe.InstanceExpression = pinned_string;
4431 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4433 ec.Emit (OpCodes.Add);
4437 public override void EmitExit (EmitContext ec)
4439 ec.Emit (OpCodes.Ldnull);
4440 pinned_string.EmitAssign (ec);
4444 public class VariableDeclaration : BlockVariableDeclaration
4446 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4451 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4453 if (!Variable.Type.IsPointer && li == Variable) {
4454 bc.Report.Error (209, TypeExpression.Location,
4455 "The type of locals declared in a fixed statement must be a pointer type");
4460 // The rules for the possible declarators are pretty wise,
4461 // but the production on the grammar is more concise.
4463 // So we have to enforce these rules here.
4465 // We do not resolve before doing the case 1 test,
4466 // because the grammar is explicit in that the token &
4467 // is present, so we need to test for this particular case.
4470 if (initializer is Cast) {
4471 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4475 initializer = initializer.Resolve (bc);
4477 if (initializer == null)
4483 if (initializer.Type.IsArray) {
4484 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4487 // Provided that array_type is unmanaged,
4489 if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4493 // and T* is implicitly convertible to the
4494 // pointer type given in the fixed statement.
4496 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4498 Expression converted = Convert.ImplicitConversionRequired (
4499 bc, array_ptr, li.Type, loc);
4500 if (converted == null)
4504 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4506 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4507 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4508 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4509 new NullPointer (loc),
4512 converted = converted.Resolve (bc);
4514 return new ExpressionEmitter (converted, li);
4520 if (initializer.Type == TypeManager.string_type) {
4521 return new StringEmitter (initializer, li, loc).Resolve (bc);
4524 // Case 3: fixed buffer
4525 if (initializer is FixedBufferPtr) {
4526 return new ExpressionEmitter (initializer, li);
4530 // Case 4: & object.
4532 bool already_fixed = true;
4533 Unary u = initializer as Unary;
4534 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4535 IVariableReference vr = u.Expr as IVariableReference;
4536 if (vr == null || !vr.IsFixed) {
4537 already_fixed = false;
4541 if (already_fixed) {
4542 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4545 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4546 return new ExpressionEmitter (initializer, li);
4551 VariableDeclaration decl;
4552 Statement statement;
4555 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4564 public Statement Statement {
4570 public BlockVariableDeclaration Variables {
4578 public override bool Resolve (BlockContext ec)
4580 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4581 if (!decl.Resolve (ec))
4585 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4586 bool ok = statement.Resolve (ec);
4587 bool flow_unreachable = ec.EndFlowBranching ();
4588 has_ret = flow_unreachable;
4593 protected override void DoEmit (EmitContext ec)
4595 decl.Variable.CreateBuilder (ec);
4596 decl.Initializer.Emit (ec);
4597 if (decl.Declarators != null) {
4598 foreach (var d in decl.Declarators) {
4599 d.Variable.CreateBuilder (ec);
4600 d.Initializer.Emit (ec);
4604 statement.Emit (ec);
4610 // Clear the pinned variable
4612 ((Emitter) decl.Initializer).EmitExit (ec);
4613 if (decl.Declarators != null) {
4614 foreach (var d in decl.Declarators) {
4615 ((Emitter)d.Initializer).EmitExit (ec);
4620 protected override void CloneTo (CloneContext clonectx, Statement t)
4622 Fixed target = (Fixed) t;
4624 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4625 target.statement = statement.Clone (clonectx);
4629 public class Catch : Statement
4633 FullNamedExpression type_expr;
4634 CompilerAssign assign;
4637 public Catch (Block block, Location loc)
4645 public Block Block {
4651 public TypeSpec CatchType {
4657 public bool IsGeneral {
4659 return type_expr == null;
4663 public FullNamedExpression TypeExpression {
4672 public LocalVariable Variable {
4683 protected override void DoEmit (EmitContext ec)
4686 ec.BeginCatchBlock (TypeManager.object_type);
4688 ec.BeginCatchBlock (CatchType);
4691 li.CreateBuilder (ec);
4694 // Special case hoisted catch variable, we have to use a temporary variable
4695 // to pass via anonymous storey initialization with the value still on top
4698 if (li.HoistedVariant != null) {
4699 LocalTemporary lt = new LocalTemporary (li.Type);
4700 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4702 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4704 // switch to assigning from the temporary variable and not from top of the stack
4705 assign.UpdateSource (lt);
4708 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4709 ec.Emit (OpCodes.Pop);
4710 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4716 public override bool Resolve (BlockContext ec)
4718 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4719 if (type_expr != null) {
4720 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4725 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4726 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4727 } else if (li != null) {
4729 li.PrepareForFlowAnalysis (ec);
4731 // source variable is at the top of the stack
4732 Expression source = new EmptyExpression (li.Type);
4733 if (li.Type.IsGenericParameter)
4734 source = new UnboxCast (source, li.Type);
4736 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4737 Block.AddScopeStatement (new StatementExpression (assign));
4741 return Block.Resolve (ec);
4745 protected override void CloneTo (CloneContext clonectx, Statement t)
4747 Catch target = (Catch) t;
4749 if (type_expr != null)
4750 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4752 target.block = clonectx.LookupBlock (block);
4756 public class TryFinally : ExceptionStatement {
4759 public TryFinally (Statement stmt, Block fini, Location loc)
4765 public override bool Resolve (BlockContext ec)
4769 ec.StartFlowBranching (this);
4771 if (!stmt.Resolve (ec))
4775 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4776 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4777 if (!fini.Resolve (ec))
4781 ec.EndFlowBranching ();
4783 ok &= base.Resolve (ec);
4788 protected override void EmitPreTryBody (EmitContext ec)
4792 protected override void EmitTryBody (EmitContext ec)
4797 protected override void EmitFinallyBody (EmitContext ec)
4802 protected override void CloneTo (CloneContext clonectx, Statement t)
4804 TryFinally target = (TryFinally) t;
4806 target.stmt = (Statement) stmt.Clone (clonectx);
4808 target.fini = clonectx.LookupBlock (fini);
4812 public class TryCatch : Statement {
4814 public List<Catch> Specific;
4815 public Catch General;
4816 bool inside_try_finally, code_follows;
4818 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4821 this.Specific = catch_clauses;
4822 this.inside_try_finally = inside_try_finally;
4824 Catch c = catch_clauses [0];
4827 catch_clauses.RemoveAt (0);
4833 public override bool Resolve (BlockContext ec)
4837 ec.StartFlowBranching (this);
4839 if (!Block.Resolve (ec))
4842 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4844 foreach (Catch c in Specific){
4845 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4847 if (!c.Resolve (ec)) {
4852 TypeSpec resolved_type = c.CatchType;
4853 for (int ii = 0; ii < last_index; ++ii) {
4854 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4855 ec.Report.Error (160, c.loc,
4856 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4857 TypeManager.CSharpName (prev_catches [ii]));
4862 prev_catches [last_index++] = resolved_type;
4865 if (General != null) {
4866 foreach (Catch c in Specific) {
4867 if (c.CatchType != TypeManager.exception_type)
4870 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
4873 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4876 ec.Report.Warning (1058, 1, c.loc,
4877 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4880 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4882 if (!General.Resolve (ec))
4886 ec.EndFlowBranching ();
4888 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4889 // So, ensure there's some IL code after this statement
4890 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4891 ec.NeedReturnLabel ();
4896 public void SomeCodeFollows ()
4898 code_follows = true;
4901 protected override void DoEmit (EmitContext ec)
4903 if (!inside_try_finally)
4904 ec.BeginExceptionBlock ();
4908 foreach (Catch c in Specific)
4911 if (General != null)
4914 if (!inside_try_finally)
4915 ec.EndExceptionBlock ();
4918 protected override void CloneTo (CloneContext clonectx, Statement t)
4920 TryCatch target = (TryCatch) t;
4922 target.Block = clonectx.LookupBlock (Block);
4923 if (General != null)
4924 target.General = (Catch) General.Clone (clonectx);
4925 if (Specific != null){
4926 target.Specific = new List<Catch> ();
4927 foreach (Catch c in Specific)
4928 target.Specific.Add ((Catch) c.Clone (clonectx));
4933 public class Using : ExceptionStatement
4935 public class VariableDeclaration : BlockVariableDeclaration
4937 Statement dispose_call;
4939 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4944 public VariableDeclaration (LocalVariable li, Location loc)
4950 public VariableDeclaration (Expression expr)
4953 loc = expr.Location;
4959 public bool IsNested { get; private set; }
4963 public void EmitDispose (EmitContext ec)
4965 dispose_call.Emit (ec);
4968 public override bool Resolve (BlockContext bc)
4973 return base.Resolve (bc);
4976 public Expression ResolveExpression (BlockContext bc)
4978 var e = Initializer.Resolve (bc);
4982 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
4983 Initializer = ResolveInitializer (bc, Variable, e);
4987 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4989 if (li.Type == InternalType.Dynamic) {
4990 initializer = initializer.Resolve (bc);
4991 if (initializer == null)
4994 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
4995 Arguments args = new Arguments (1);
4996 args.Add (new Argument (initializer));
4997 initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
4998 if (initializer == null)
5001 var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5002 dispose_call = CreateDisposeCall (bc, var);
5003 dispose_call.Resolve (bc);
5005 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5008 if (li == Variable) {
5009 CheckIDiposableConversion (bc, li, initializer);
5010 dispose_call = CreateDisposeCall (bc, li);
5011 dispose_call.Resolve (bc);
5014 return base.ResolveInitializer (bc, li, initializer);
5017 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5021 if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5022 if (TypeManager.IsNullableType (type)) {
5023 // it's handled in CreateDisposeCall
5027 bc.Report.SymbolRelatedToPreviousError (type);
5028 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5029 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5030 type.GetSignatureForError ());
5036 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5038 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5040 var loc = lv.Location;
5042 if (TypeManager.void_dispose_void == null) {
5043 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5044 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5047 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5048 dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
5049 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
5052 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5054 // Add conditional call when disposing possible null variable
5055 if (!type.IsStruct || TypeManager.IsNullableType (type))
5056 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5061 public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
5063 for (int i = declarators.Count - 1; i >= 0; --i) {
5064 var d = declarators [i];
5065 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5066 vd.Initializer = d.Initializer;
5068 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5069 vd.dispose_call.Resolve (bc);
5071 stmt = new Using (vd, stmt, d.Variable.Location);
5079 VariableDeclaration decl;
5081 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5087 public Using (Expression expr, Statement stmt, Location loc)
5090 this.decl = new VariableDeclaration (expr);
5095 public Expression Expression {
5097 return decl.Variable == null ? decl.Initializer : null;
5101 public BlockVariableDeclaration Variables {
5109 protected override void EmitPreTryBody (EmitContext ec)
5114 protected override void EmitTryBody (EmitContext ec)
5119 protected override void EmitFinallyBody (EmitContext ec)
5121 decl.EmitDispose (ec);
5124 public override bool Resolve (BlockContext ec)
5126 VariableReference vr;
5127 bool vr_locked = false;
5129 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5130 if (decl.Variable == null) {
5131 vr = decl.ResolveExpression (ec) as VariableReference;
5133 vr_locked = vr.IsLockedByStatement;
5134 vr.IsLockedByStatement = true;
5137 if (!decl.Resolve (ec))
5140 if (decl.Declarators != null) {
5141 stmt = decl.RewriteForDeclarators (ec, stmt);
5148 ec.StartFlowBranching (this);
5152 ec.EndFlowBranching ();
5155 vr.IsLockedByStatement = vr_locked;
5162 protected override void CloneTo (CloneContext clonectx, Statement t)
5164 Using target = (Using) t;
5166 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5167 target.stmt = stmt.Clone (clonectx);
5172 /// Implementation of the foreach C# statement
5174 public class Foreach : Statement {
5176 sealed class ArrayForeach : Statement
5178 readonly Foreach for_each;
5179 readonly Statement statement;
5182 TemporaryVariableReference[] lengths;
5183 Expression [] length_exprs;
5184 StatementExpression[] counter;
5185 TemporaryVariableReference[] variables;
5187 TemporaryVariableReference copy;
5189 LocalVariableReference variable;
5191 public ArrayForeach (Foreach @foreach, int rank)
5193 for_each = @foreach;
5194 statement = for_each.statement;
5196 variable = new LocalVariableReference (for_each.variable, loc);
5198 counter = new StatementExpression[rank];
5199 variables = new TemporaryVariableReference[rank];
5200 length_exprs = new Expression [rank];
5203 // Only use temporary length variables when dealing with
5204 // multi-dimensional arrays
5207 lengths = new TemporaryVariableReference [rank];
5210 protected override void CloneTo (CloneContext clonectx, Statement target)
5212 throw new NotImplementedException ();
5215 public override bool Resolve (BlockContext ec)
5217 Block variables_block = variable.local_info.Block;
5218 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5221 int rank = length_exprs.Length;
5222 Arguments list = new Arguments (rank);
5223 for (int i = 0; i < rank; i++) {
5224 var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5226 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5227 counter[i].Resolve (ec);
5230 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5232 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5233 lengths[i].Resolve (ec);
5235 Arguments args = new Arguments (1);
5236 args.Add (new Argument (new IntConstant (i, loc)));
5237 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5240 list.Add (new Argument (v));
5243 access = new ElementAccess (copy, list, loc).Resolve (ec);
5247 Expression var_type = for_each.type;
5248 VarExpr ve = var_type as VarExpr;
5250 // Infer implicitly typed local variable from foreach array type
5251 var_type = new TypeExpression (access.Type, ve.Location);
5254 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5255 if (var_type == null)
5258 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5264 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5265 ec.CurrentBranching.CreateSibling ();
5267 variable.local_info.Type = conv.Type;
5268 variable.Resolve (ec);
5270 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5271 if (!statement.Resolve (ec))
5273 ec.EndFlowBranching ();
5275 // There's no direct control flow from the end of the embedded statement to the end of the loop
5276 ec.CurrentBranching.CurrentUsageVector.Goto ();
5278 ec.EndFlowBranching ();
5283 protected override void DoEmit (EmitContext ec)
5285 copy.EmitAssign (ec, for_each.expr);
5287 int rank = length_exprs.Length;
5288 Label[] test = new Label [rank];
5289 Label[] loop = new Label [rank];
5291 for (int i = 0; i < rank; i++) {
5292 test [i] = ec.DefineLabel ();
5293 loop [i] = ec.DefineLabel ();
5295 if (lengths != null)
5296 lengths [i].EmitAssign (ec, length_exprs [i]);
5299 IntConstant zero = new IntConstant (0, loc);
5300 for (int i = 0; i < rank; i++) {
5301 variables [i].EmitAssign (ec, zero);
5303 ec.Emit (OpCodes.Br, test [i]);
5304 ec.MarkLabel (loop [i]);
5307 variable.local_info.CreateBuilder (ec);
5308 variable.EmitAssign (ec, conv, false, false);
5310 statement.Emit (ec);
5312 ec.MarkLabel (ec.LoopBegin);
5314 for (int i = rank - 1; i >= 0; i--){
5315 counter [i].Emit (ec);
5317 ec.MarkLabel (test [i]);
5318 variables [i].Emit (ec);
5320 if (lengths != null)
5321 lengths [i].Emit (ec);
5323 length_exprs [i].Emit (ec);
5325 ec.Emit (OpCodes.Blt, loop [i]);
5328 ec.MarkLabel (ec.LoopEnd);
5332 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5334 class Body : Statement
5337 LocalVariableReference variable;
5338 Expression current, conv;
5339 Statement statement;
5341 public Body (TypeSpec type, LocalVariable variable,
5342 Expression current, Statement statement,
5346 this.variable = new LocalVariableReference (variable, loc);
5347 this.current = current;
5348 this.statement = statement;
5352 protected override void CloneTo (CloneContext clonectx, Statement target)
5354 throw new NotImplementedException ();
5357 public override bool Resolve (BlockContext ec)
5359 current = current.Resolve (ec);
5360 if (current == null)
5363 conv = Convert.ExplicitConversion (ec, current, type, loc);
5367 variable.local_info.Type = conv.Type;
5368 variable.Resolve (ec);
5370 if (!statement.Resolve (ec))
5376 protected override void DoEmit (EmitContext ec)
5378 variable.local_info.CreateBuilder (ec);
5379 variable.EmitAssign (ec, conv, false, false);
5381 statement.Emit (ec);
5385 class RuntimeDispose : Using.VariableDeclaration
5387 public RuntimeDispose (LocalVariable lv, Location loc)
5392 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5394 // Defered to runtime check
5397 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5399 if (TypeManager.void_dispose_void == null) {
5400 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5401 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5405 // Fabricates code like
5407 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5410 var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5412 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5413 dispose_variable.CreateReferenceExpression (bc, loc),
5414 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5415 loc), new NullLiteral (loc), loc);
5417 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5418 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5420 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5421 return new If (idisaposable_test, dispose, loc);
5425 LocalVariable variable;
5427 Statement statement;
5428 Expression var_type;
5429 ExpressionStatement init;
5430 TemporaryVariableReference enumerator_variable;
5431 bool ambiguous_getenumerator_name;
5433 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5435 this.var_type = var_type;
5436 this.variable = var;
5442 protected override void CloneTo (CloneContext clonectx, Statement target)
5444 throw new NotImplementedException ();
5447 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5449 rc.Report.SymbolRelatedToPreviousError (enumerator);
5450 rc.Report.Error (202, loc,
5451 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5452 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5455 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5458 // Option 1: Try to match by name GetEnumerator first
5460 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
5461 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5463 var mg = mexpr as MethodGroupExpr;
5465 mg.InstanceExpression = expr;
5466 Arguments args = new Arguments (0);
5467 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5469 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5470 if (ambiguous_getenumerator_name)
5473 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5479 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5481 TypeSpec iface_candidate = null;
5484 var ifaces = t.Interfaces;
5485 if (ifaces != null) {
5486 foreach (var iface in ifaces) {
5487 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5488 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5489 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5490 rc.Report.Error (1640, loc,
5491 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5492 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5497 iface_candidate = iface;
5501 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5502 iface_candidate = iface;
5507 if (t.IsGenericParameter)
5512 } while (t != null);
5514 if (iface_candidate == null) {
5515 rc.Report.Error (1579, loc,
5516 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5517 expr.Type.GetSignatureForError (), "GetEnumerator");
5522 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5523 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5528 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5529 mg.InstanceExpression = expr;
5533 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5535 var ms = MemberCache.FindMember (enumerator.ReturnType,
5536 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5537 BindingRestriction.InstanceOnly) as MethodSpec;
5539 if (ms == null || !ms.IsPublic) {
5540 Error_WrongEnumerator (rc, enumerator);
5544 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5547 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5549 var ps = MemberCache.FindMember (enumerator.ReturnType,
5550 MemberFilter.Property ("Current", null),
5551 BindingRestriction.InstanceOnly) as PropertySpec;
5553 if (ps == null || !ps.IsPublic) {
5554 Error_WrongEnumerator (rc, enumerator);
5561 public override bool Resolve (BlockContext ec)
5563 bool is_dynamic = expr.Type == InternalType.Dynamic;
5566 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5567 } else if (TypeManager.IsNullableType (expr.Type)) {
5568 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5571 var get_enumerator_mg = ResolveGetEnumerator (ec);
5572 if (get_enumerator_mg == null) {
5576 var get_enumerator = get_enumerator_mg.BestCandidate;
5577 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5578 enumerator_variable.Resolve (ec);
5580 // Prepare bool MoveNext ()
5581 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5582 if (move_next_mg == null) {
5586 move_next_mg.InstanceExpression = enumerator_variable;
5588 // Prepare ~T~ Current { get; }
5589 var current_prop = ResolveCurrent (ec, get_enumerator);
5590 if (current_prop == null) {
5594 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5595 if (current_pe == null)
5598 VarExpr ve = var_type as VarExpr;
5601 // Source type is dynamic, set element type to dynamic too
5602 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5604 // Infer implicitly typed local variable from foreach enumerable type
5605 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5607 } else if (is_dynamic) {
5608 // Explicit cast of dynamic collection elements has to be done at runtime
5609 current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5612 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5613 if (var_type == null)
5616 variable.Type = var_type.Type;
5618 var init = new Invocation (get_enumerator_mg, null);
5620 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5621 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5623 var enum_type = enumerator_variable.Type;
5626 // Add Dispose method call when enumerator can be IDisposable
5628 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5629 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5631 // Runtime Dispose check
5633 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5634 vd.Initializer = init;
5635 statement = new Using (vd, statement, loc);
5638 // No Dispose call needed
5640 this.init = new SimpleAssign (enumerator_variable, init);
5641 this.init.Resolve (ec);
5645 // Static Dispose check
5647 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5648 vd.Initializer = init;
5649 statement = new Using (vd, statement, loc);
5652 return statement.Resolve (ec);
5655 protected override void DoEmit (EmitContext ec)
5657 enumerator_variable.LocalInfo.CreateBuilder (ec);
5660 init.EmitStatement (ec);
5662 statement.Emit (ec);
5665 #region IErrorHandler Members
5667 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5669 ec.Report.SymbolRelatedToPreviousError (best);
5670 ec.Report.Warning (278, 2, loc,
5671 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5672 expr.Type.GetSignatureForError (), "enumerable",
5673 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5675 ambiguous_getenumerator_name = true;
5679 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5684 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5689 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5698 LocalVariable variable;
5700 Statement statement;
5702 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5705 this.variable = var;
5711 public Statement Statement {
5712 get { return statement; }
5715 public override bool Resolve (BlockContext ec)
5717 expr = expr.Resolve (ec);
5722 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5726 if (expr.Type == TypeManager.string_type) {
5727 statement = new ArrayForeach (this, 1);
5728 } else if (expr.Type is ArrayContainer) {
5729 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5731 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5732 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5733 expr.ExprClassName);
5737 statement = new CollectionForeach (type, variable, expr, statement, loc);
5740 return statement.Resolve (ec);
5743 protected override void DoEmit (EmitContext ec)
5745 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5746 ec.LoopBegin = ec.DefineLabel ();
5747 ec.LoopEnd = ec.DefineLabel ();
5749 statement.Emit (ec);
5751 ec.LoopBegin = old_begin;
5752 ec.LoopEnd = old_end;
5755 protected override void CloneTo (CloneContext clonectx, Statement t)
5757 Foreach target = (Foreach) t;
5759 target.type = type.Clone (clonectx);
5760 target.expr = expr.Clone (clonectx);
5761 target.statement = statement.Clone (clonectx);