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.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
53 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
55 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
56 bool ok = Resolve (ec);
57 ec.KillFlowBranching ();
63 /// Return value indicates whether all code paths emitted return.
65 protected abstract void DoEmit (EmitContext ec);
67 public virtual void Emit (EmitContext ec)
74 // This routine must be overrided in derived classes and make copies
75 // of all the data that might be modified if resolved
77 protected abstract void CloneTo (CloneContext clonectx, Statement target);
79 public Statement Clone (CloneContext clonectx)
81 Statement s = (Statement) this.MemberwiseClone ();
82 CloneTo (clonectx, s);
86 public virtual Expression CreateExpressionTree (ResolveContext ec)
88 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
93 public sealed class EmptyStatement : Statement
95 public EmptyStatement (Location loc)
100 public override bool Resolve (BlockContext ec)
105 public override bool ResolveUnreachable (BlockContext ec, bool warn)
110 public override void Emit (EmitContext ec)
114 protected override void DoEmit (EmitContext ec)
116 throw new NotSupportedException ();
119 protected override void CloneTo (CloneContext clonectx, Statement target)
125 public class If : Statement {
127 public Statement TrueStatement;
128 public Statement FalseStatement;
132 public If (Expression bool_expr, Statement true_statement, Location l)
133 : this (bool_expr, true_statement, null, l)
137 public If (Expression bool_expr,
138 Statement true_statement,
139 Statement false_statement,
142 this.expr = bool_expr;
143 TrueStatement = true_statement;
144 FalseStatement = false_statement;
148 public override bool Resolve (BlockContext ec)
152 expr = expr.Resolve (ec);
157 // Dead code elimination
159 if (expr is Constant) {
160 bool take = !((Constant) expr).IsDefaultValue;
163 if (!TrueStatement.Resolve (ec))
166 if ((FalseStatement != null) &&
167 !FalseStatement.ResolveUnreachable (ec, true))
169 FalseStatement = null;
171 if (!TrueStatement.ResolveUnreachable (ec, true))
173 TrueStatement = null;
175 if ((FalseStatement != null) &&
176 !FalseStatement.Resolve (ec))
184 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
186 ok &= TrueStatement.Resolve (ec);
188 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
190 ec.CurrentBranching.CreateSibling ();
192 if (FalseStatement != null)
193 ok &= FalseStatement.Resolve (ec);
195 ec.EndFlowBranching ();
200 protected override void DoEmit (EmitContext ec)
202 Label false_target = ec.DefineLabel ();
206 // If we're a boolean constant, Resolve() already
207 // eliminated dead code for us.
209 Constant c = expr as Constant;
211 c.EmitSideEffect (ec);
213 if (!c.IsDefaultValue)
214 TrueStatement.Emit (ec);
215 else if (FalseStatement != null)
216 FalseStatement.Emit (ec);
221 expr.EmitBranchable (ec, false_target, false);
223 TrueStatement.Emit (ec);
225 if (FalseStatement != null){
226 bool branch_emitted = false;
228 end = ec.DefineLabel ();
230 ec.Emit (OpCodes.Br, end);
231 branch_emitted = true;
234 ec.MarkLabel (false_target);
235 FalseStatement.Emit (ec);
240 ec.MarkLabel (false_target);
244 protected override void CloneTo (CloneContext clonectx, Statement t)
248 target.expr = expr.Clone (clonectx);
249 target.TrueStatement = TrueStatement.Clone (clonectx);
250 if (FalseStatement != null)
251 target.FalseStatement = FalseStatement.Clone (clonectx);
255 public class Do : Statement {
256 public Expression expr;
257 public Statement EmbeddedStatement;
259 public Do (Statement statement, BooleanExpression bool_expr, Location l)
262 EmbeddedStatement = statement;
266 public override bool Resolve (BlockContext ec)
270 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
272 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
274 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
275 if (!EmbeddedStatement.Resolve (ec))
277 ec.EndFlowBranching ();
279 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
280 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
282 expr = expr.Resolve (ec);
285 else if (expr is Constant){
286 bool infinite = !((Constant) expr).IsDefaultValue;
288 ec.CurrentBranching.CurrentUsageVector.Goto ();
291 ec.EndFlowBranching ();
296 protected override void DoEmit (EmitContext ec)
298 Label loop = ec.DefineLabel ();
299 Label old_begin = ec.LoopBegin;
300 Label old_end = ec.LoopEnd;
302 ec.LoopBegin = ec.DefineLabel ();
303 ec.LoopEnd = ec.DefineLabel ();
306 EmbeddedStatement.Emit (ec);
307 ec.MarkLabel (ec.LoopBegin);
310 // Dead code elimination
312 if (expr is Constant){
313 bool res = !((Constant) expr).IsDefaultValue;
315 expr.EmitSideEffect (ec);
317 ec.Emit (OpCodes.Br, loop);
319 expr.EmitBranchable (ec, loop, true);
321 ec.MarkLabel (ec.LoopEnd);
323 ec.LoopBegin = old_begin;
324 ec.LoopEnd = old_end;
327 protected override void CloneTo (CloneContext clonectx, Statement t)
331 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
332 target.expr = expr.Clone (clonectx);
336 public class While : Statement {
337 public Expression expr;
338 public Statement Statement;
339 bool infinite, empty;
341 public While (BooleanExpression bool_expr, Statement statement, Location l)
343 this.expr = bool_expr;
344 Statement = statement;
348 public override bool Resolve (BlockContext ec)
352 expr = expr.Resolve (ec);
357 // Inform whether we are infinite or not
359 if (expr is Constant){
360 bool value = !((Constant) expr).IsDefaultValue;
363 if (!Statement.ResolveUnreachable (ec, true))
371 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
373 ec.CurrentBranching.CreateSibling ();
375 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
376 if (!Statement.Resolve (ec))
378 ec.EndFlowBranching ();
380 // There's no direct control flow from the end of the embedded statement to the end of the loop
381 ec.CurrentBranching.CurrentUsageVector.Goto ();
383 ec.EndFlowBranching ();
388 protected override void DoEmit (EmitContext ec)
391 expr.EmitSideEffect (ec);
395 Label old_begin = ec.LoopBegin;
396 Label old_end = ec.LoopEnd;
398 ec.LoopBegin = ec.DefineLabel ();
399 ec.LoopEnd = ec.DefineLabel ();
402 // Inform whether we are infinite or not
404 if (expr is Constant){
405 // expr is 'true', since the 'empty' case above handles the 'false' case
406 ec.MarkLabel (ec.LoopBegin);
407 expr.EmitSideEffect (ec);
409 ec.Emit (OpCodes.Br, ec.LoopBegin);
412 // Inform that we are infinite (ie, `we return'), only
413 // if we do not `break' inside the code.
415 ec.MarkLabel (ec.LoopEnd);
417 Label while_loop = ec.DefineLabel ();
419 ec.Emit (OpCodes.Br, ec.LoopBegin);
420 ec.MarkLabel (while_loop);
424 ec.MarkLabel (ec.LoopBegin);
427 expr.EmitBranchable (ec, while_loop, true);
429 ec.MarkLabel (ec.LoopEnd);
432 ec.LoopBegin = old_begin;
433 ec.LoopEnd = old_end;
436 public override void Emit (EmitContext ec)
441 protected override void CloneTo (CloneContext clonectx, Statement t)
443 While target = (While) t;
445 target.expr = expr.Clone (clonectx);
446 target.Statement = Statement.Clone (clonectx);
450 public class For : Statement {
452 Statement InitStatement;
454 public Statement Statement;
455 bool infinite, empty;
457 public For (Statement init_statement,
458 BooleanExpression test,
463 InitStatement = init_statement;
465 Increment = increment;
466 Statement = statement;
470 public override bool Resolve (BlockContext ec)
474 if (InitStatement != null){
475 if (!InitStatement.Resolve (ec))
480 Test = Test.Resolve (ec);
483 else if (Test is Constant){
484 bool value = !((Constant) Test).IsDefaultValue;
487 if (!Statement.ResolveUnreachable (ec, true))
489 if ((Increment != null) &&
490 !Increment.ResolveUnreachable (ec, false))
500 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
502 ec.CurrentBranching.CreateSibling ();
504 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
506 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
507 if (!Statement.Resolve (ec))
509 ec.EndFlowBranching ();
511 if (Increment != null){
512 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
513 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
516 if (!Increment.Resolve (ec))
521 // There's no direct control flow from the end of the embedded statement to the end of the loop
522 ec.CurrentBranching.CurrentUsageVector.Goto ();
524 ec.EndFlowBranching ();
529 protected override void DoEmit (EmitContext ec)
531 if (InitStatement != null)
532 InitStatement.Emit (ec);
535 Test.EmitSideEffect (ec);
539 Label old_begin = ec.LoopBegin;
540 Label old_end = ec.LoopEnd;
541 Label loop = ec.DefineLabel ();
542 Label test = ec.DefineLabel ();
544 ec.LoopBegin = ec.DefineLabel ();
545 ec.LoopEnd = ec.DefineLabel ();
547 ec.Emit (OpCodes.Br, test);
551 ec.MarkLabel (ec.LoopBegin);
556 // If test is null, there is no test, and we are just
561 // The Resolve code already catches the case for
562 // Test == Constant (false) so we know that
565 if (Test is Constant) {
566 Test.EmitSideEffect (ec);
567 ec.Emit (OpCodes.Br, loop);
569 Test.EmitBranchable (ec, loop, true);
573 ec.Emit (OpCodes.Br, loop);
574 ec.MarkLabel (ec.LoopEnd);
576 ec.LoopBegin = old_begin;
577 ec.LoopEnd = old_end;
580 protected override void CloneTo (CloneContext clonectx, Statement t)
582 For target = (For) t;
584 if (InitStatement != null)
585 target.InitStatement = InitStatement.Clone (clonectx);
587 target.Test = Test.Clone (clonectx);
588 if (Increment != null)
589 target.Increment = Increment.Clone (clonectx);
590 target.Statement = Statement.Clone (clonectx);
594 public class StatementExpression : Statement
596 ExpressionStatement expr;
598 public StatementExpression (ExpressionStatement expr)
604 protected override void CloneTo (CloneContext clonectx, Statement t)
606 StatementExpression target = (StatementExpression) t;
607 target.expr = (ExpressionStatement) expr.Clone (clonectx);
610 protected override void DoEmit (EmitContext ec)
612 expr.EmitStatement (ec);
615 public override bool Resolve (BlockContext ec)
617 expr = expr.ResolveStatement (ec);
623 // Simple version of statement list not requiring a block
625 public class StatementList : Statement
627 List<Statement> statements;
629 public StatementList (Statement first, Statement second)
631 statements = new List<Statement> () { first, second };
635 public IList<Statement> Statements {
642 public void Add (Statement statement)
644 statements.Add (statement);
647 public override bool Resolve (BlockContext ec)
649 foreach (var s in statements)
655 protected override void DoEmit (EmitContext ec)
657 foreach (var s in statements)
661 protected override void CloneTo (CloneContext clonectx, Statement target)
663 StatementList t = (StatementList) target;
665 t.statements = new List<Statement> (statements.Count);
666 foreach (Statement s in statements)
667 t.statements.Add (s.Clone (clonectx));
671 // A 'return' or a 'yield break'
672 public abstract class ExitStatement : Statement
674 protected bool unwind_protect;
675 protected abstract bool DoResolve (BlockContext ec);
677 public virtual void Error_FinallyClause (Report Report)
679 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
682 public sealed override bool Resolve (BlockContext ec)
687 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
688 ec.CurrentBranching.CurrentUsageVector.Goto ();
694 /// Implements the return statement
696 public class Return : ExitStatement
698 protected Expression Expr;
700 public Return (Expression expr, Location l)
707 public Expression Expression {
714 protected override bool DoResolve (BlockContext ec)
717 if (ec.ReturnType.Kind == MemberKind.Void)
721 // Return must not be followed by an expression when
722 // the method return type is Task
724 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
725 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
726 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
728 // Extra trick not to emit ret/leave inside awaiter body
730 Expr = EmptyExpression.Null;
735 if (ec.CurrentIterator != null) {
736 Error_ReturnFromIterator (ec);
738 ec.Report.Error (126, loc,
739 "An object of a type convertible to `{0}' is required for the return statement",
740 ec.ReturnType.GetSignatureForError ());
746 Expr = Expr.Resolve (ec);
747 TypeSpec block_return_type = ec.ReturnType;
749 AnonymousExpression am = ec.CurrentAnonymousMethod;
751 if (block_return_type.Kind == MemberKind.Void) {
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 async_block = am as AsyncInitializer;
763 if (async_block != null) {
765 var storey = (AsyncTaskStorey) am.Storey;
766 var async_type = storey.ReturnType;
768 if (async_type == null && async_block.ReturnTypeInference != null) {
769 async_block.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
773 if (!async_type.IsGenericTask) {
774 if (this is ContextualReturn)
777 ec.Report.Error (1997, loc,
778 "`{0}': A return keyword must not be followed by an expression when async method returns Task. Consider using Task<T>",
779 ec.GetSignatureForError ());
784 // The return type is actually Task<T> type argument
786 block_return_type = async_type.TypeArguments[0];
789 var l = am as AnonymousMethodBody;
790 if (l != null && l.ReturnTypeInference != null && Expr != null) {
791 l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
800 if (Expr.Type != block_return_type) {
801 Expr = Convert.ImplicitConversionRequired (ec, Expr, block_return_type, loc);
804 if (am != null && block_return_type == ec.ReturnType) {
805 ec.Report.Error (1662, loc,
806 "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",
807 am.ContainerType, am.GetSignatureForError ());
816 protected override void DoEmit (EmitContext ec)
821 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
822 if (async_body != null) {
823 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
825 // It's null for await without async
826 if (async_return != null) {
827 async_return.EmitAssign (ec);
829 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
836 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
840 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
842 ec.Emit (OpCodes.Ret);
845 void Error_ReturnFromIterator (ResolveContext rc)
847 rc.Report.Error (1622, loc,
848 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
851 protected override void CloneTo (CloneContext clonectx, Statement t)
853 Return target = (Return) t;
854 // It's null for simple return;
856 target.Expr = Expr.Clone (clonectx);
860 public class Goto : Statement {
862 LabeledStatement label;
865 public override bool Resolve (BlockContext ec)
867 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
868 ec.CurrentBranching.CurrentUsageVector.Goto ();
872 public Goto (string label, Location l)
878 public string Target {
879 get { return target; }
882 public void SetResolvedTarget (LabeledStatement label)
885 label.AddReference ();
888 protected override void CloneTo (CloneContext clonectx, Statement target)
893 protected override void DoEmit (EmitContext ec)
896 throw new InternalErrorException ("goto emitted before target resolved");
897 Label l = label.LabelTarget (ec);
898 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
902 public class LabeledStatement : Statement {
909 FlowBranching.UsageVector vectors;
911 public LabeledStatement (string name, Block block, Location l)
918 public Label LabelTarget (EmitContext ec)
923 label = ec.DefineLabel ();
938 public bool IsDefined {
939 get { return defined; }
942 public bool HasBeenReferenced {
943 get { return referenced; }
946 public FlowBranching.UsageVector JumpOrigins {
947 get { return vectors; }
950 public void AddUsageVector (FlowBranching.UsageVector vector)
952 vector = vector.Clone ();
953 vector.Next = vectors;
957 protected override void CloneTo (CloneContext clonectx, Statement target)
962 public override bool Resolve (BlockContext ec)
964 // this flow-branching will be terminated when the surrounding block ends
965 ec.StartFlowBranching (this);
969 protected override void DoEmit (EmitContext ec)
971 if (!HasBeenReferenced)
972 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
975 ec.MarkLabel (label);
978 public void AddReference ()
986 /// `goto default' statement
988 public class GotoDefault : Statement {
990 public GotoDefault (Location l)
995 protected override void CloneTo (CloneContext clonectx, Statement target)
1000 public override bool Resolve (BlockContext ec)
1002 ec.CurrentBranching.CurrentUsageVector.Goto ();
1004 if (ec.Switch == null) {
1005 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1009 if (!ec.Switch.GotDefault) {
1010 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1017 protected override void DoEmit (EmitContext ec)
1019 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1024 /// `goto case' statement
1026 public class GotoCase : Statement {
1030 public GotoCase (Expression e, Location l)
1036 public override bool Resolve (BlockContext ec)
1038 if (ec.Switch == null){
1039 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1043 ec.CurrentBranching.CurrentUsageVector.Goto ();
1045 expr = expr.Resolve (ec);
1049 Constant c = expr as Constant;
1051 ec.Report.Error (150, expr.Location, "A constant value is expected");
1056 if (ec.Switch.IsNullable && c is NullLiteral) {
1059 TypeSpec type = ec.Switch.SwitchType;
1060 res = c.TryReduce (ec, type, c.Location);
1062 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1066 if (!Convert.ImplicitStandardConversionExists (c, type))
1067 ec.Report.Warning (469, 2, loc,
1068 "The `goto case' value is not implicitly convertible to type `{0}'",
1069 TypeManager.CSharpName (type));
1073 sl = ec.Switch.ResolveGotoCase (ec, res);
1077 protected override void DoEmit (EmitContext ec)
1079 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1082 protected override void CloneTo (CloneContext clonectx, Statement t)
1084 GotoCase target = (GotoCase) t;
1086 target.expr = expr.Clone (clonectx);
1090 public class Throw : Statement {
1093 public Throw (Expression expr, Location l)
1099 public override bool Resolve (BlockContext ec)
1102 ec.CurrentBranching.CurrentUsageVector.Goto ();
1103 return ec.CurrentBranching.CheckRethrow (loc);
1106 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1107 ec.CurrentBranching.CurrentUsageVector.Goto ();
1112 var et = ec.BuiltinTypes.Exception;
1113 if (Convert.ImplicitConversionExists (ec, expr, et))
1114 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1116 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1121 protected override void DoEmit (EmitContext ec)
1124 ec.Emit (OpCodes.Rethrow);
1128 ec.Emit (OpCodes.Throw);
1132 protected override void CloneTo (CloneContext clonectx, Statement t)
1134 Throw target = (Throw) t;
1137 target.expr = expr.Clone (clonectx);
1141 public class Break : Statement {
1143 public Break (Location l)
1148 bool unwind_protect;
1150 public override bool Resolve (BlockContext ec)
1152 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1153 ec.CurrentBranching.CurrentUsageVector.Goto ();
1157 protected override void DoEmit (EmitContext ec)
1159 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1162 protected override void CloneTo (CloneContext clonectx, Statement t)
1168 public class Continue : Statement {
1170 public Continue (Location l)
1175 bool unwind_protect;
1177 public override bool Resolve (BlockContext ec)
1179 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1180 ec.CurrentBranching.CurrentUsageVector.Goto ();
1184 protected override void DoEmit (EmitContext ec)
1186 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1189 protected override void CloneTo (CloneContext clonectx, Statement t)
1195 public interface ILocalVariable
1197 void Emit (EmitContext ec);
1198 void EmitAssign (EmitContext ec);
1199 void EmitAddressOf (EmitContext ec);
1202 public interface INamedBlockVariable
1204 Block Block { get; }
1205 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1206 bool IsDeclared { get; }
1207 Location Location { get; }
1210 public class BlockVariableDeclaration : Statement
1212 public class Declarator
1215 Expression initializer;
1217 public Declarator (LocalVariable li, Expression initializer)
1219 if (li.Type != null)
1220 throw new ArgumentException ("Expected null variable type");
1223 this.initializer = initializer;
1226 public Declarator (Declarator clone, Expression initializer)
1229 this.initializer = initializer;
1234 public LocalVariable Variable {
1240 public Expression Initializer {
1245 initializer = value;
1252 Expression initializer;
1253 protected FullNamedExpression type_expr;
1254 protected LocalVariable li;
1255 protected List<Declarator> declarators;
1257 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1259 this.type_expr = type;
1261 this.loc = type_expr.Location;
1264 protected BlockVariableDeclaration (LocalVariable li)
1271 public List<Declarator> Declarators {
1277 public Expression Initializer {
1282 initializer = value;
1286 public FullNamedExpression TypeExpression {
1292 public LocalVariable Variable {
1300 public void AddDeclarator (Declarator decl)
1302 if (declarators == null)
1303 declarators = new List<Declarator> ();
1305 declarators.Add (decl);
1308 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1310 var container = bc.CurrentMemberDefinition.Parent;
1312 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1313 new MemberName (li.Name, li.Location), null);
1315 container.AddField (f);
1318 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1322 public override bool Resolve (BlockContext bc)
1324 return Resolve (bc, true);
1327 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1329 if (li.Type == null) {
1330 TypeSpec type = null;
1331 var vexpr = type_expr as VarExpr;
1334 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1335 // same name exists or as a keyword when no type was found
1337 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1338 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1339 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1342 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1346 if (li.IsConstant) {
1347 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1351 if (Initializer == null) {
1352 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1356 if (declarators != null) {
1357 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1361 Initializer = Initializer.Resolve (bc);
1362 if (Initializer != null) {
1363 ((VarExpr) type_expr).InferType (bc, Initializer);
1364 type = type_expr.Type;
1369 type = type_expr.ResolveAsType (bc);
1373 if (li.IsConstant && !type.IsConstantCompatible) {
1374 Const.Error_InvalidConstantType (type, loc, bc.Report);
1379 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1384 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1386 CreateEvaluatorVariable (bc, li);
1388 li.PrepareForFlowAnalysis (bc);
1391 if (initializer != null) {
1392 initializer = ResolveInitializer (bc, li, initializer);
1393 // li.Variable.DefinitelyAssigned
1396 if (declarators != null) {
1397 foreach (var d in declarators) {
1398 d.Variable.Type = li.Type;
1400 CreateEvaluatorVariable (bc, d.Variable);
1402 d.Variable.PrepareForFlowAnalysis (bc);
1405 if (d.Initializer != null && resolveDeclaratorInitializers) {
1406 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1407 // d.Variable.DefinitelyAssigned
1415 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1417 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1418 return a.ResolveStatement (bc);
1421 protected override void DoEmit (EmitContext ec)
1426 li.CreateBuilder (ec);
1428 if (Initializer != null)
1429 ((ExpressionStatement) Initializer).EmitStatement (ec);
1431 if (declarators != null) {
1432 foreach (var d in declarators) {
1433 d.Variable.CreateBuilder (ec);
1434 if (d.Initializer != null)
1435 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1440 protected override void CloneTo (CloneContext clonectx, Statement target)
1442 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1444 if (type_expr != null)
1445 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1447 if (initializer != null)
1448 t.initializer = initializer.Clone (clonectx);
1450 if (declarators != null) {
1451 t.declarators = null;
1452 foreach (var d in declarators)
1453 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1458 public class BlockConstantDeclaration : BlockVariableDeclaration
1460 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1465 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1467 initializer = initializer.Resolve (bc);
1468 if (initializer == null)
1471 var c = initializer as Constant;
1473 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1477 c = c.ConvertImplicitly (li.Type);
1479 if (TypeSpec.IsReferenceType (li.Type))
1480 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1482 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1487 li.ConstantValue = c;
1493 // The information about a user-perceived local variable
1495 public class LocalVariable : INamedBlockVariable, ILocalVariable
1502 AddressTaken = 1 << 2,
1503 CompilerGenerated = 1 << 3,
1505 ForeachVariable = 1 << 5,
1506 FixedVariable = 1 << 6,
1507 UsingVariable = 1 << 7,
1508 // DefinitelyAssigned = 1 << 8,
1511 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1515 readonly string name;
1516 readonly Location loc;
1517 readonly Block block;
1519 Constant const_value;
1521 public VariableInfo VariableInfo;
1522 HoistedVariable hoisted_variant;
1524 LocalBuilder builder;
1526 public LocalVariable (Block block, string name, Location loc)
1533 public LocalVariable (Block block, string name, Flags flags, Location loc)
1534 : this (block, name, loc)
1540 // Used by variable declarators
1542 public LocalVariable (LocalVariable li, string name, Location loc)
1543 : this (li.block, name, li.flags, loc)
1549 public bool AddressTaken {
1550 get { return (flags & Flags.AddressTaken) != 0; }
1551 set { flags |= Flags.AddressTaken; }
1554 public Block Block {
1560 public Constant ConstantValue {
1565 const_value = value;
1570 // Hoisted local variable variant
1572 public HoistedVariable HoistedVariant {
1574 return hoisted_variant;
1577 hoisted_variant = value;
1581 public bool IsDeclared {
1583 return type != null;
1587 public bool IsConstant {
1589 return (flags & Flags.Constant) != 0;
1593 public bool IsLocked {
1595 return (flags & Flags.IsLocked) != 0;
1598 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1602 public bool IsThis {
1604 return (flags & Flags.IsThis) != 0;
1608 public bool IsFixed {
1610 return (flags & Flags.FixedVariable) != 0;
1614 public bool IsReadonly {
1616 return (flags & Flags.ReadonlyMask) != 0;
1620 public Location Location {
1626 public string Name {
1632 public TypeSpec Type {
1643 public void CreateBuilder (EmitContext ec)
1645 if ((flags & Flags.Used) == 0) {
1646 if (VariableInfo == null) {
1647 // Missing flow analysis or wrong variable flags
1648 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1651 if (VariableInfo.IsEverAssigned)
1652 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1654 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1657 if (HoistedVariant != null)
1660 if (builder != null) {
1661 if ((flags & Flags.CompilerGenerated) != 0)
1664 // To avoid Used warning duplicates
1665 throw new InternalErrorException ("Already created variable `{0}'", name);
1669 // All fixed variabled are pinned, a slot has to be alocated
1671 builder = ec.DeclareLocal (Type, IsFixed);
1672 if (SymbolWriter.HasSymbolWriter)
1673 ec.DefineLocalVariable (name, builder);
1676 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1678 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1683 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1685 if (IsConstant && const_value != null)
1686 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1688 return new LocalVariableReference (this, loc);
1691 public void Emit (EmitContext ec)
1693 // TODO: Need something better for temporary variables
1694 if ((flags & Flags.CompilerGenerated) != 0)
1697 ec.Emit (OpCodes.Ldloc, builder);
1700 public void EmitAssign (EmitContext ec)
1702 // TODO: Need something better for temporary variables
1703 if ((flags & Flags.CompilerGenerated) != 0)
1706 ec.Emit (OpCodes.Stloc, builder);
1709 public void EmitAddressOf (EmitContext ec)
1711 ec.Emit (OpCodes.Ldloca, builder);
1714 public static string GetCompilerGeneratedName (Block block)
1716 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1719 public string GetReadOnlyContext ()
1721 switch (flags & Flags.ReadonlyMask) {
1722 case Flags.FixedVariable:
1723 return "fixed variable";
1724 case Flags.ForeachVariable:
1725 return "foreach iteration variable";
1726 case Flags.UsingVariable:
1727 return "using variable";
1730 throw new InternalErrorException ("Variable is not readonly");
1733 public bool IsThisAssigned (BlockContext ec, Block block)
1735 if (VariableInfo == null)
1736 throw new Exception ();
1738 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1741 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1744 public bool IsAssigned (BlockContext ec)
1746 if (VariableInfo == null)
1747 throw new Exception ();
1749 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1752 public void PrepareForFlowAnalysis (BlockContext bc)
1755 // No need for definitely assigned check for these guys
1757 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1760 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1761 bc.FlowOffset += VariableInfo.Length;
1765 // Mark the variables as referenced in the user code
1767 public void SetIsUsed ()
1769 flags |= Flags.Used;
1772 public override string ToString ()
1774 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1779 /// Block represents a C# block.
1783 /// This class is used in a number of places: either to represent
1784 /// explicit blocks that the programmer places or implicit blocks.
1786 /// Implicit blocks are used as labels or to introduce variable
1789 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1790 /// they contain extra information that is not necessary on normal blocks.
1792 public class Block : Statement {
1800 HasCapturedVariable = 64,
1801 HasCapturedThis = 1 << 7,
1802 IsExpressionTree = 1 << 8,
1803 CompilerGenerated = 1 << 9,
1807 public Block Parent;
1808 public Location StartLocation;
1809 public Location EndLocation;
1811 public ExplicitBlock Explicit;
1812 public ParametersBlock ParametersBlock;
1814 protected Flags flags;
1817 // The statements in this block
1819 protected List<Statement> statements;
1821 protected List<Statement> scope_initializers;
1823 int? resolving_init_idx;
1825 protected Block original;
1829 public int ID = id++;
1831 static int clone_id_counter;
1835 // int assignable_slots;
1836 bool unreachable_shown;
1839 public Block (Block parent, Location start, Location end)
1840 : this (parent, 0, start, end)
1844 public Block (Block parent, Flags flags, Location start, Location end)
1846 if (parent != null) {
1847 // the appropriate constructors will fixup these fields
1848 ParametersBlock = parent.ParametersBlock;
1849 Explicit = parent.Explicit;
1852 this.Parent = parent;
1854 this.StartLocation = start;
1855 this.EndLocation = end;
1857 statements = new List<Statement> (4);
1859 this.original = this;
1864 public bool HasRet {
1865 get { return (flags & Flags.HasRet) != 0; }
1868 public Block Original {
1874 public bool IsCompilerGenerated {
1875 get { return (flags & Flags.CompilerGenerated) != 0; }
1876 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1879 public bool Unchecked {
1880 get { return (flags & Flags.Unchecked) != 0; }
1881 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1884 public bool Unsafe {
1885 get { return (flags & Flags.Unsafe) != 0; }
1886 set { flags |= Flags.Unsafe; }
1891 public Block CreateSwitchBlock (Location start)
1893 // FIXME: Only explicit block should be created
1894 var new_block = new Block (this, start, start);
1895 new_block.IsCompilerGenerated = true;
1899 public void SetEndLocation (Location loc)
1904 public void AddLabel (LabeledStatement target)
1906 ParametersBlock.TopBlock.AddLabel (target.Name, target);
1909 public void AddLocalName (LocalVariable li)
1911 AddLocalName (li.Name, li);
1914 public virtual void AddLocalName (string name, INamedBlockVariable li)
1916 ParametersBlock.TopBlock.AddLocalName (name, li);
1919 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1921 if (reason == null) {
1922 Error_AlreadyDeclared (name, variable);
1926 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1927 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1928 "to `{0}', which is already used in a `{1}' scope to denote something else",
1932 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1934 var pi = variable as ParametersBlock.ParameterInfo;
1936 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
1938 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1939 "A local variable named `{0}' is already defined in this scope", name);
1943 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1945 ParametersBlock.TopBlock.Report.Error (412, loc,
1946 "The type parameter name `{0}' is the same as local variable or parameter name",
1951 // It should be used by expressions which require to
1952 // register a statement during resolve process.
1954 public void AddScopeStatement (Statement s)
1956 if (scope_initializers == null)
1957 scope_initializers = new List<Statement> ();
1960 // Simple recursive helper, when resolve scope initializer another
1961 // new scope initializer can be added, this ensures it's initialized
1962 // before existing one. For now this can happen with expression trees
1963 // in base ctor initializer only
1965 if (resolving_init_idx.HasValue) {
1966 scope_initializers.Insert (resolving_init_idx.Value, s);
1967 ++resolving_init_idx;
1969 scope_initializers.Add (s);
1973 public void AddStatement (Statement s)
1978 public int AssignableSlots {
1980 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1982 // return assignable_slots;
1986 public LabeledStatement LookupLabel (string name)
1988 return ParametersBlock.TopBlock.GetLabel (name, this);
1991 public override bool Resolve (BlockContext ec)
1993 Block prev_block = ec.CurrentBlock;
1996 ec.CurrentBlock = this;
1997 ec.StartFlowBranching (this);
2000 // Compiler generated scope statements
2002 if (scope_initializers != null) {
2003 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2004 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2007 resolving_init_idx = null;
2011 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2012 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2013 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2014 // responsible for handling the situation.
2016 int statement_count = statements.Count;
2017 for (int ix = 0; ix < statement_count; ix++){
2018 Statement s = statements [ix];
2021 // Warn if we detect unreachable code.
2024 if (s is EmptyStatement)
2027 if (!unreachable_shown && !(s is LabeledStatement)) {
2028 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2029 unreachable_shown = true;
2032 Block c_block = s as Block;
2033 if (c_block != null)
2034 c_block.unreachable = c_block.unreachable_shown = true;
2038 // Note that we're not using ResolveUnreachable() for unreachable
2039 // statements here. ResolveUnreachable() creates a temporary
2040 // flow branching and kills it afterwards. This leads to problems
2041 // if you have two unreachable statements where the first one
2042 // assigns a variable and the second one tries to access it.
2045 if (!s.Resolve (ec)) {
2047 if (ec.IsInProbingMode)
2050 statements [ix] = new EmptyStatement (s.loc);
2054 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2055 statements [ix] = new EmptyStatement (s.loc);
2057 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2058 if (unreachable && s is LabeledStatement)
2059 throw new InternalErrorException ("should not happen");
2062 while (ec.CurrentBranching is FlowBranchingLabeled)
2063 ec.EndFlowBranching ();
2065 bool flow_unreachable = ec.EndFlowBranching ();
2067 ec.CurrentBlock = prev_block;
2069 if (flow_unreachable)
2070 flags |= Flags.HasRet;
2072 // If we're a non-static `struct' constructor which doesn't have an
2073 // initializer, then we must initialize all of the struct's fields.
2074 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2080 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2082 unreachable_shown = true;
2086 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2088 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2089 bool ok = Resolve (ec);
2090 ec.KillFlowBranching ();
2095 protected override void DoEmit (EmitContext ec)
2097 for (int ix = 0; ix < statements.Count; ix++){
2098 statements [ix].Emit (ec);
2102 public override void Emit (EmitContext ec)
2104 if (scope_initializers != null)
2105 EmitScopeInitializers (ec);
2107 ec.Mark (StartLocation);
2110 if (SymbolWriter.HasSymbolWriter)
2111 EmitSymbolInfo (ec);
2114 protected void EmitScopeInitializers (EmitContext ec)
2116 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2118 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2119 foreach (Statement s in scope_initializers)
2123 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2126 protected virtual void EmitSymbolInfo (EmitContext ec)
2131 public override string ToString ()
2133 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2137 protected override void CloneTo (CloneContext clonectx, Statement t)
2139 Block target = (Block) t;
2141 target.clone_id = clone_id_counter++;
2144 clonectx.AddBlockMap (this, target);
2145 if (original != this)
2146 clonectx.AddBlockMap (original, target);
2148 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2149 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2152 target.Parent = clonectx.RemapBlockCopy (Parent);
2154 target.statements = new List<Statement> (statements.Count);
2155 foreach (Statement s in statements)
2156 target.statements.Add (s.Clone (clonectx));
2160 public class ExplicitBlock : Block
2162 protected AnonymousMethodStorey am_storey;
2164 public ExplicitBlock (Block parent, Location start, Location end)
2165 : this (parent, (Flags) 0, start, end)
2169 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2170 : base (parent, flags, start, end)
2172 this.Explicit = this;
2177 public AnonymousMethodStorey AnonymousMethodStorey {
2183 public bool HasCapturedThis {
2184 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2186 return (flags & Flags.HasCapturedThis) != 0;
2190 public bool HasCapturedVariable {
2191 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2193 return (flags & Flags.HasCapturedVariable) != 0;
2200 // Creates anonymous method storey in current block
2202 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2205 // An iterator has only 1 storey block
2207 if (ec.CurrentAnonymousMethod.IsIterator)
2208 return ec.CurrentAnonymousMethod.Storey;
2211 // When referencing a variable in iterator storey from children anonymous method
2213 if (ParametersBlock.am_storey is IteratorStorey) {
2214 return ParametersBlock.am_storey;
2217 if (am_storey == null) {
2218 MemberBase mc = ec.MemberContext as MemberBase;
2221 // Creates anonymous method storey for this block
2223 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2229 public override void Emit (EmitContext ec)
2231 if (am_storey != null) {
2232 DefineAnonymousStorey (ec);
2233 am_storey.EmitStoreyInstantiation (ec, this);
2236 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2237 if (emit_debug_info)
2242 if (emit_debug_info)
2246 void DefineAnonymousStorey (EmitContext ec)
2249 // Creates anonymous method storey
2251 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2253 // Creates parent storey reference when hoisted this is accessible
2255 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2256 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2259 // Hoisted this exists in top-level parent storey only
2261 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2262 parent = parent.Parent.Explicit;
2264 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2267 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2269 // TODO MemberCache: Review
2270 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2273 am_storey.CreateType ();
2274 am_storey.DefineType ();
2275 am_storey.ResolveTypeParameters ();
2277 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2278 if (ref_blocks != null) {
2279 foreach (ExplicitBlock ref_block in ref_blocks) {
2280 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2281 if (b.am_storey != null) {
2282 b.am_storey.AddParentStoreyReference (ec, am_storey);
2284 // Stop propagation inside same top block
2285 if (b.ParametersBlock.Original == ParametersBlock.Original)
2288 b = b.ParametersBlock;
2291 b.HasCapturedVariable = true;
2296 am_storey.Define ();
2297 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2300 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2302 tryBlock.statements = statements;
2303 statements = new List<Statement> (1);
2304 statements.Add (tf);
2309 // ParametersBlock was introduced to support anonymous methods
2310 // and lambda expressions
2312 public class ParametersBlock : ExplicitBlock
2314 public class ParameterInfo : INamedBlockVariable
2316 readonly ParametersBlock block;
2318 public VariableInfo VariableInfo;
2321 public ParameterInfo (ParametersBlock block, int index)
2329 public Block Block {
2335 public bool IsDeclared {
2341 public bool IsLocked {
2350 public Location Location {
2352 return Parameter.Location;
2356 public Parameter Parameter {
2358 return block.Parameters [index];
2362 public TypeSpec ParameterType {
2364 return Parameter.Type;
2370 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2372 return new ParameterReference (this, loc);
2377 // Block is converted into an expression
2379 sealed class BlockScopeExpression : Expression
2382 readonly ParametersBlock block;
2384 public BlockScopeExpression (Expression child, ParametersBlock block)
2390 public override bool ContainsEmitWithAwait ()
2392 return child.ContainsEmitWithAwait ();
2395 public override Expression CreateExpressionTree (ResolveContext ec)
2397 throw new NotSupportedException ();
2400 protected override Expression DoResolve (ResolveContext ec)
2405 child = child.Resolve (ec);
2409 eclass = child.eclass;
2414 public override void Emit (EmitContext ec)
2416 block.EmitScopeInitializers (ec);
2421 protected ParametersCompiled parameters;
2422 protected ParameterInfo[] parameter_info;
2424 protected bool unreachable;
2425 protected ToplevelBlock top_block;
2427 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2428 : base (parent, 0, start, start)
2430 if (parameters == null)
2431 throw new ArgumentNullException ("parameters");
2433 this.parameters = parameters;
2434 ParametersBlock = this;
2436 this.top_block = parent.ParametersBlock.top_block;
2437 ProcessParameters ();
2440 protected ParametersBlock (ParametersCompiled parameters, Location start)
2441 : base (null, 0, start, start)
2443 if (parameters == null)
2444 throw new ArgumentNullException ("parameters");
2446 this.parameters = parameters;
2447 ParametersBlock = this;
2451 // It's supposed to be used by method body implementation of anonymous methods
2453 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2454 : base (null, 0, source.StartLocation, source.EndLocation)
2456 this.parameters = parameters;
2457 this.statements = source.statements;
2458 this.scope_initializers = source.scope_initializers;
2460 this.resolved = true;
2461 this.unreachable = source.unreachable;
2462 this.am_storey = source.am_storey;
2464 ParametersBlock = this;
2467 // Overwrite original for comparison purposes when linking cross references
2468 // between anonymous methods
2478 return (flags & Flags.IsAsync) != 0;
2481 flags = value ? flags | Flags.IsAsync : flags & ~Flags.IsAsync;
2486 // Block has been converted to expression tree
2488 public bool IsExpressionTree {
2490 return (flags & Flags.IsExpressionTree) != 0;
2495 // The parameters for the block.
2497 public ParametersCompiled Parameters {
2503 public ToplevelBlock TopBlock {
2509 public bool Resolved {
2515 public int TemporaryLocalsCount { get; set; }
2520 // Check whether all `out' parameters have been assigned.
2522 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2524 if (vector.IsUnreachable)
2527 int n = parameter_info == null ? 0 : parameter_info.Length;
2529 for (int i = 0; i < n; i++) {
2530 VariableInfo var = parameter_info[i].VariableInfo;
2535 if (vector.IsAssigned (var, false))
2538 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2543 public override Expression CreateExpressionTree (ResolveContext ec)
2545 if (statements.Count == 1) {
2546 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2547 if (scope_initializers != null)
2548 expr = new BlockScopeExpression (expr, this);
2553 return base.CreateExpressionTree (ec);
2556 public ParameterInfo GetParameterInfo (Parameter p)
2558 for (int i = 0; i < parameters.Count; ++i) {
2559 if (parameters[i] == p)
2560 return parameter_info[i];
2563 throw new ArgumentException ("Invalid parameter");
2566 public Expression GetParameterReference (int index, Location loc)
2568 return new ParameterReference (parameter_info[index], loc);
2571 public Statement PerformClone ()
2573 CloneContext clonectx = new CloneContext ();
2574 return Clone (clonectx);
2577 protected void ProcessParameters ()
2579 if (parameters.Count == 0)
2582 parameter_info = new ParameterInfo[parameters.Count];
2583 for (int i = 0; i < parameter_info.Length; ++i) {
2584 var p = parameters.FixedParameters[i];
2588 // TODO: Should use Parameter only and more block there
2589 parameter_info[i] = new ParameterInfo (this, i);
2591 AddLocalName (p.Name, parameter_info[i]);
2595 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2602 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2603 flags |= Flags.IsExpressionTree;
2608 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2609 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2614 unreachable = top_level.End ();
2616 } catch (Exception e) {
2617 if (e is CompletionResult || rc.Report.IsDisabled)
2620 if (rc.CurrentBlock != null) {
2621 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2623 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2626 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2630 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2631 if (rc.CurrentAnonymousMethod == null) {
2632 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2633 if (md is StateMachineMethod) {
2636 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2641 // If an asynchronous body of F is either an expression classified as nothing, or a
2642 // statement block where no return statements have expressions, the inferred return type is Task
2645 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2646 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2647 am.ReturnTypeInference = null;
2648 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2653 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2654 rc.CurrentAnonymousMethod.GetSignatureForError ());
2662 void ResolveMeta (BlockContext ec)
2664 int orig_count = parameters.Count;
2666 for (int i = 0; i < orig_count; ++i) {
2667 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2669 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2672 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2673 parameter_info[i].VariableInfo = vi;
2674 ec.FlowOffset += vi.Length;
2678 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2680 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2681 pb.EndLocation = EndLocation;
2682 pb.statements = statements;
2684 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2685 am_storey = new IteratorStorey (iterator);
2687 statements = new List<Statement> (1);
2688 AddStatement (new Return (iterator, iterator.Location));
2691 public void WrapIntoAsyncTask (IMemberContext context, TypeContainer host, TypeSpec returnType)
2693 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2694 pb.EndLocation = EndLocation;
2695 pb.statements = statements;
2697 var block_type = host.Module.Compiler.BuiltinTypes.Void;
2698 var initializer = new AsyncInitializer (pb, host, block_type);
2699 initializer.Type = block_type;
2701 am_storey = new AsyncTaskStorey (context, initializer, returnType);
2703 statements = new List<Statement> (1);
2704 AddStatement (new StatementExpression (initializer));
2711 public class ToplevelBlock : ParametersBlock
2713 LocalVariable this_variable;
2714 CompilerContext compiler;
2715 Dictionary<string, object> names;
2716 Dictionary<string, object> labels;
2718 public HoistedVariable HoistedThisVariable;
2720 public Report Report {
2721 get { return compiler.Report; }
2724 public ToplevelBlock (CompilerContext ctx, Location loc)
2725 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2729 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2730 : base (parameters, start)
2732 this.compiler = ctx;
2735 ProcessParameters ();
2739 // Recreates a top level block from parameters block. Used for
2740 // compiler generated methods where the original block comes from
2741 // explicit child block. This works for already resolved blocks
2742 // only to ensure we resolve them in the correct flow order
2744 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2745 : base (source, parameters)
2747 this.compiler = source.TopBlock.compiler;
2751 public bool IsIterator
2754 return (flags & Flags.IsIterator) != 0;
2757 flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator;
2761 public override void AddLocalName (string name, INamedBlockVariable li)
2764 names = new Dictionary<string, object> ();
2767 if (!names.TryGetValue (name, out value)) {
2768 names.Add (name, li);
2772 INamedBlockVariable existing = value as INamedBlockVariable;
2773 List<INamedBlockVariable> existing_list;
2774 if (existing != null) {
2775 existing_list = new List<INamedBlockVariable> ();
2776 existing_list.Add (existing);
2777 names[name] = existing_list;
2779 existing_list = (List<INamedBlockVariable>) value;
2783 // A collision checking between local names
2785 for (int i = 0; i < existing_list.Count; ++i) {
2786 existing = existing_list[i];
2787 Block b = existing.Block;
2789 // Collision at same level
2790 if (li.Block == b) {
2791 li.Block.Error_AlreadyDeclared (name, li);
2795 // Collision with parent
2797 while ((b = b.Parent) != null) {
2798 if (existing.Block == b) {
2799 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2800 i = existing_list.Count;
2805 // Collision with with children
2807 while ((b = b.Parent) != null) {
2808 if (li.Block == b) {
2809 li.Block.Error_AlreadyDeclared (name, li, "child");
2810 i = existing_list.Count;
2816 existing_list.Add (li);
2819 public void AddLabel (string name, LabeledStatement label)
2822 labels = new Dictionary<string, object> ();
2825 if (!labels.TryGetValue (name, out value)) {
2826 labels.Add (name, label);
2830 LabeledStatement existing = value as LabeledStatement;
2831 List<LabeledStatement> existing_list;
2832 if (existing != null) {
2833 existing_list = new List<LabeledStatement> ();
2834 existing_list.Add (existing);
2835 labels[name] = existing_list;
2837 existing_list = (List<LabeledStatement>) value;
2841 // A collision checking between labels
2843 for (int i = 0; i < existing_list.Count; ++i) {
2844 existing = existing_list[i];
2845 Block b = existing.Block;
2847 // Collision at same level
2848 if (label.Block == b) {
2849 Report.SymbolRelatedToPreviousError (existing.loc, name);
2850 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2854 // Collision with parent
2856 while ((b = b.Parent) != null) {
2857 if (existing.Block == b) {
2858 Report.Error (158, label.loc,
2859 "The label `{0}' shadows another label by the same name in a contained scope", name);
2860 i = existing_list.Count;
2865 // Collision with with children
2867 while ((b = b.Parent) != null) {
2868 if (label.Block == b) {
2869 Report.Error (158, label.loc,
2870 "The label `{0}' shadows another label by the same name in a contained scope", name);
2871 i = existing_list.Count;
2877 existing_list.Add (label);
2881 // Creates an arguments set from all parameters, useful for method proxy calls
2883 public Arguments GetAllParametersArguments ()
2885 int count = parameters.Count;
2886 Arguments args = new Arguments (count);
2887 for (int i = 0; i < count; ++i) {
2888 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2889 args.Add (new Argument (arg_expr));
2896 // Lookup inside a block, the returned value can represent 3 states
2898 // true+variable: A local name was found and it's valid
2899 // false+variable: A local name was found in a child block only
2900 // false+null: No local name was found
2902 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2908 if (!names.TryGetValue (name, out value))
2911 variable = value as INamedBlockVariable;
2913 if (variable != null) {
2915 if (variable.Block == b.Original)
2919 } while (b != null);
2927 } while (b != null);
2929 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2930 for (int i = 0; i < list.Count; ++i) {
2933 if (variable.Block == b.Original)
2937 } while (b != null);
2945 } while (b != null);
2955 public LabeledStatement GetLabel (string name, Block block)
2961 if (!labels.TryGetValue (name, out value)) {
2965 var label = value as LabeledStatement;
2967 if (label != null) {
2968 if (label.Block == b.Original)
2971 // TODO: Temporary workaround for the switch block implicit label block
2972 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2975 List<LabeledStatement> list = (List<LabeledStatement>) value;
2976 for (int i = 0; i < list.Count; ++i) {
2978 if (label.Block == b.Original)
2981 // TODO: Temporary workaround for the switch block implicit label block
2982 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2991 // Returns the "this" instance variable of this block.
2992 // See AddThisVariable() for more information.
2994 public LocalVariable ThisVariable {
2995 get { return this_variable; }
2999 // This is used by non-static `struct' constructors which do not have an
3000 // initializer - in this case, the constructor must initialize all of the
3001 // struct's fields. To do this, we add a "this" variable and use the flow
3002 // analysis code to ensure that it's been fully initialized before control
3003 // leaves the constructor.
3005 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
3007 if (this_variable == null) {
3008 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
3009 this_variable.Type = ds.CurrentType;
3010 this_variable.PrepareForFlowAnalysis (bc);
3013 return this_variable;
3016 public bool IsThisAssigned (BlockContext ec)
3018 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3021 public override void Emit (EmitContext ec)
3023 if (Report.Errors > 0)
3032 ec.Mark (EndLocation);
3034 if (ec.HasReturnLabel)
3035 ec.MarkLabel (ec.ReturnLabel);
3037 if (ec.return_value != null) {
3038 ec.Emit (OpCodes.Ldloc, ec.return_value);
3039 ec.Emit (OpCodes.Ret);
3042 // If `HasReturnLabel' is set, then we already emitted a
3043 // jump to the end of the method, so we must emit a `ret'
3046 // Unfortunately, System.Reflection.Emit automatically emits
3047 // a leave to the end of a finally block. This is a problem
3048 // if no code is following the try/finally block since we may
3049 // jump to a point after the end of the method.
3050 // As a workaround, we're always creating a return label in
3054 if (ec.HasReturnLabel || !unreachable) {
3055 if (ec.ReturnType.Kind != MemberKind.Void)
3056 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3057 ec.Emit (OpCodes.Ret);
3062 } catch (Exception e){
3063 Console.WriteLine ("Exception caught by the compiler while emitting:");
3064 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3066 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3072 protected override void EmitSymbolInfo (EmitContext ec)
3074 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3075 if ((ae != null) && (ae.Storey != null))
3076 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3078 base.EmitSymbolInfo (ec);
3082 public class SwitchLabel {
3085 readonly Location loc;
3090 // if expr == null, then it is the default case.
3092 public SwitchLabel (Expression expr, Location l)
3098 public bool IsDefault {
3100 return label == null;
3104 public Expression Label {
3110 public Location Location {
3116 public Constant Converted {
3125 public Label GetILLabel (EmitContext ec)
3127 if (il_label == null){
3128 il_label = ec.DefineLabel ();
3131 return il_label.Value;
3135 // Resolves the expression, reduces it to a literal if possible
3136 // and then converts it to the requested type.
3138 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3140 Expression e = label.Resolve (ec);
3145 Constant c = e as Constant;
3147 ec.Report.Error (150, loc, "A constant value is expected");
3151 if (allow_nullable && c is NullLiteral) {
3156 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3157 return converted != null;
3160 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3163 if (converted == null)
3166 label = converted.GetValueAsLiteral ();
3168 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3169 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3172 public SwitchLabel Clone (CloneContext clonectx)
3177 return new SwitchLabel (label.Clone (clonectx), loc);
3181 public class SwitchSection {
3182 public readonly List<SwitchLabel> Labels;
3183 public readonly Block Block;
3185 public SwitchSection (List<SwitchLabel> labels, Block block)
3191 public SwitchSection Clone (CloneContext clonectx)
3193 var cloned_labels = new List<SwitchLabel> ();
3195 foreach (SwitchLabel sl in Labels)
3196 cloned_labels.Add (sl.Clone (clonectx));
3198 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3202 public class Switch : Statement
3204 // structure used to hold blocks of keys while calculating table switch
3205 sealed class LabelsRange : IComparable<LabelsRange>
3207 public readonly long min;
3209 public readonly List<long> label_values;
3211 public LabelsRange (long value)
3214 label_values = new List<long> ();
3215 label_values.Add (value);
3218 public LabelsRange (long min, long max, ICollection<long> values)
3222 this.label_values = new List<long> (values);
3227 return max - min + 1;
3231 public bool AddValue (long value)
3233 var gap = value - min + 1;
3234 // Ensure the range has > 50% occupancy
3235 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3239 label_values.Add (value);
3243 public int CompareTo (LabelsRange other)
3245 int nLength = label_values.Count;
3246 int nLengthOther = other.label_values.Count;
3247 if (nLengthOther == nLength)
3248 return (int) (other.min - min);
3250 return nLength - nLengthOther;
3254 public List<SwitchSection> Sections;
3255 public Expression Expr;
3258 // Mapping of all labels to their SwitchLabels
3260 Dictionary<long, SwitchLabel> labels;
3261 Dictionary<string, SwitchLabel> string_labels;
3264 /// The governing switch type
3266 public TypeSpec SwitchType;
3271 Label default_target;
3273 Expression new_expr;
3276 SwitchSection constant_section;
3277 SwitchSection default_section;
3278 SwitchLabel null_section;
3280 ExpressionStatement string_dictionary;
3281 FieldExpr switch_cache_field;
3282 static int unique_counter;
3283 ExplicitBlock block;
3286 // Nullable Types support
3288 Nullable.Unwrap unwrap;
3290 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3298 public ExplicitBlock Block {
3304 public Label DefaultLabel {
3306 return default_target;
3310 public bool GotDefault {
3312 return default_section != null;
3316 public bool IsNullable {
3318 return unwrap != null;
3323 // Determines the governing type for a switch. The returned
3324 // expression might be the expression from the switch, or an
3325 // expression that includes any potential conversions to
3327 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3329 switch (expr.Type.BuiltinType) {
3330 case BuiltinTypeSpec.Type.Byte:
3331 case BuiltinTypeSpec.Type.SByte:
3332 case BuiltinTypeSpec.Type.UShort:
3333 case BuiltinTypeSpec.Type.Short:
3334 case BuiltinTypeSpec.Type.UInt:
3335 case BuiltinTypeSpec.Type.Int:
3336 case BuiltinTypeSpec.Type.ULong:
3337 case BuiltinTypeSpec.Type.Long:
3338 case BuiltinTypeSpec.Type.Char:
3339 case BuiltinTypeSpec.Type.String:
3340 case BuiltinTypeSpec.Type.Bool:
3344 if (expr.Type.IsEnum)
3348 // Try to find a *user* defined implicit conversion.
3350 // If there is no implicit conversion, or if there are multiple
3351 // conversions, we have to report an error
3353 Expression converted = null;
3354 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3357 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3362 // Ignore over-worked ImplicitUserConversions that do
3363 // an implicit conversion in addition to the user conversion.
3365 if (!(e is UserCast))
3368 if (converted != null){
3369 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3378 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3380 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3396 // Performs the basic sanity checks on the switch statement
3397 // (looks for duplicate keys and non-constant expressions).
3399 // It also returns a hashtable with the keys that we will later
3400 // use to compute the switch tables
3402 bool CheckSwitch (ResolveContext ec)
3405 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3406 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3408 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3410 foreach (SwitchSection ss in Sections){
3411 foreach (SwitchLabel sl in ss.Labels){
3413 if (default_section != null){
3414 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3417 default_section = ss;
3421 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3427 if (string_labels != null) {
3428 string s = sl.Converted.GetValue () as string;
3432 string_labels.Add (s, sl);
3434 if (sl.Converted is NullLiteral) {
3437 labels.Add (sl.Converted.GetValueAsLong (), sl);
3440 } catch (ArgumentException) {
3441 if (string_labels != null)
3442 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3444 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3454 // This method emits code for a lookup-based switch statement (non-string)
3455 // Basically it groups the cases into blocks that are at least half full,
3456 // and then spits out individual lookup opcodes for each block.
3457 // It emits the longest blocks first, and short blocks are just
3458 // handled with direct compares.
3460 void EmitTableSwitch (EmitContext ec, Expression val)
3462 Label lbl_default = default_target;
3464 if (labels.Count > 0) {
3465 List<LabelsRange> ranges;
3466 if (string_labels != null) {
3467 // We have done all hard work for string already
3468 // setup single range only
3469 ranges = new List<LabelsRange> (1);
3470 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3472 var element_keys = new long[labels.Count];
3473 labels.Keys.CopyTo (element_keys, 0);
3474 Array.Sort (element_keys);
3477 // Build possible ranges of switch labes to reduce number
3480 ranges = new List<LabelsRange> (element_keys.Length);
3481 var range = new LabelsRange (element_keys[0]);
3483 for (int i = 1; i < element_keys.Length; ++i) {
3484 var l = element_keys[i];
3485 if (range.AddValue (l))
3488 range = new LabelsRange (l);
3492 // sort the blocks so we can tackle the largest ones first
3496 TypeSpec compare_type = TypeManager.IsEnumType (SwitchType) ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3498 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3499 LabelsRange kb = ranges[range_index];
3500 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3502 // Optimize small ranges using simple equality check
3503 if (kb.Range <= 2) {
3504 foreach (var key in kb.label_values) {
3505 SwitchLabel sl = labels[key];
3506 if (sl.Converted.IsDefaultValue) {
3507 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3510 sl.Converted.Emit (ec);
3511 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3515 // TODO: if all the keys in the block are the same and there are
3516 // no gaps/defaults then just use a range-check.
3517 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3518 // TODO: optimize constant/I4 cases
3520 // check block range (could be > 2^31)
3522 ec.EmitLong (kb.min);
3523 ec.Emit (OpCodes.Blt, lbl_default);
3526 ec.EmitLong (kb.max);
3527 ec.Emit (OpCodes.Bgt, lbl_default);
3532 ec.EmitLong (kb.min);
3533 ec.Emit (OpCodes.Sub);
3536 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3540 int first = (int) kb.min;
3543 ec.Emit (OpCodes.Sub);
3544 } else if (first < 0) {
3545 ec.EmitInt (-first);
3546 ec.Emit (OpCodes.Add);
3550 // first, build the list of labels for the switch
3552 long cJumps = kb.Range;
3553 Label[] switch_labels = new Label[cJumps];
3554 for (int iJump = 0; iJump < cJumps; iJump++) {
3555 var key = kb.label_values[iKey];
3556 if (key == kb.min + iJump) {
3557 switch_labels[iJump] = labels[key].GetILLabel (ec);
3560 switch_labels[iJump] = lbl_default;
3564 // emit the switch opcode
3565 ec.Emit (OpCodes.Switch, switch_labels);
3568 // mark the default for this block
3569 if (range_index != 0)
3570 ec.MarkLabel (lbl_default);
3573 // the last default just goes to the end
3574 if (ranges.Count > 0)
3575 ec.Emit (OpCodes.Br, lbl_default);
3578 // now emit the code for the sections
3579 bool found_default = false;
3581 foreach (SwitchSection ss in Sections) {
3582 foreach (SwitchLabel sl in ss.Labels) {
3584 ec.MarkLabel (lbl_default);
3585 found_default = true;
3586 if (null_section == null)
3587 ec.MarkLabel (null_target);
3588 } else if (sl.Converted.IsNull) {
3589 ec.MarkLabel (null_target);
3592 ec.MarkLabel (sl.GetILLabel (ec));
3598 if (!found_default) {
3599 ec.MarkLabel (lbl_default);
3600 if (null_section == null) {
3601 ec.MarkLabel (null_target);
3606 SwitchLabel FindLabel (Constant value)
3608 SwitchLabel sl = null;
3610 if (string_labels != null) {
3611 string s = value.GetValue () as string;
3613 if (null_section != null)
3615 else if (default_section != null)
3616 sl = default_section.Labels[0];
3618 string_labels.TryGetValue (s, out sl);
3621 if (value is NullLiteral) {
3624 labels.TryGetValue (value.GetValueAsLong (), out sl);
3631 SwitchSection FindSection (SwitchLabel label)
3633 foreach (SwitchSection ss in Sections){
3634 foreach (SwitchLabel sl in ss.Labels){
3643 public static void Reset ()
3648 public override bool Resolve (BlockContext ec)
3650 Expr = Expr.Resolve (ec);
3654 new_expr = SwitchGoverningType (ec, Expr);
3656 if (new_expr == null && Expr.Type.IsNullableType) {
3657 unwrap = Nullable.Unwrap.Create (Expr, false);
3661 new_expr = SwitchGoverningType (ec, unwrap);
3664 if (new_expr == null){
3665 ec.Report.Error (151, loc,
3666 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3667 TypeManager.CSharpName (Expr.Type));
3672 SwitchType = new_expr.Type;
3674 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3675 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3679 if (!CheckSwitch (ec))
3682 Switch old_switch = ec.Switch;
3684 ec.Switch.SwitchType = SwitchType;
3686 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3688 var constant = new_expr as Constant;
3689 if (constant != null) {
3691 SwitchLabel label = FindLabel (constant);
3693 constant_section = FindSection (label);
3695 if (constant_section == null)
3696 constant_section = default_section;
3701 foreach (SwitchSection ss in Sections){
3703 ec.CurrentBranching.CreateSibling (
3704 null, FlowBranching.SiblingType.SwitchSection);
3708 if (is_constant && (ss != constant_section)) {
3709 // If we're a constant switch, we're only emitting
3710 // one single section - mark all the others as
3712 ec.CurrentBranching.CurrentUsageVector.Goto ();
3713 if (!ss.Block.ResolveUnreachable (ec, true)) {
3717 if (!ss.Block.Resolve (ec))
3722 if (default_section == null)
3723 ec.CurrentBranching.CreateSibling (
3724 null, FlowBranching.SiblingType.SwitchSection);
3726 ec.EndFlowBranching ();
3727 ec.Switch = old_switch;
3732 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && !is_constant) {
3733 // TODO: Optimize single case, and single+default case
3734 ResolveStringSwitchMap (ec);
3740 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
3742 var sl = FindLabel (value);
3745 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
3751 void ResolveStringSwitchMap (ResolveContext ec)
3753 FullNamedExpression string_dictionary_type;
3754 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
3755 string_dictionary_type = new TypeExpression (
3756 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
3757 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
3759 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
3760 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
3762 ec.Module.PredefinedTypes.Dictionary.Resolve ();
3766 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3767 Field field = new Field (ctype, string_dictionary_type,
3768 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3769 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3770 if (!field.Define ())
3772 ctype.AddField (field);
3774 var init = new List<Expression> ();
3776 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
3777 string value = null;
3778 foreach (SwitchSection section in Sections) {
3779 bool contains_label = false;
3780 foreach (SwitchLabel sl in section.Labels) {
3781 if (sl.IsDefault || sl.Converted.IsNull)
3784 if (!contains_label) {
3785 labels.Add (counter, sl);
3786 contains_label = true;
3789 value = (string) sl.Converted.GetValue ();
3790 var init_args = new List<Expression> (2);
3791 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
3793 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
3794 init_args.Add (sl.Converted);
3796 init.Add (new CollectionElementInitializer (init_args, loc));
3800 // Don't add empty sections
3806 Arguments args = new Arguments (1);
3807 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
3808 Expression initializer = new NewInitialize (string_dictionary_type, args,
3809 new CollectionOrObjectInitializers (init, loc), loc);
3811 switch_cache_field = new FieldExpr (field, loc);
3812 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3815 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3817 Label l_initialized = ec.DefineLabel ();
3820 // Skip initialization when value is null
3822 value.EmitBranchable (ec, null_target, false);
3825 // Check if string dictionary is initialized and initialize
3827 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3828 string_dictionary.EmitStatement (ec);
3829 ec.MarkLabel (l_initialized);
3831 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
3833 ResolveContext rc = new ResolveContext (ec.MemberContext);
3835 if (switch_cache_field.Type.IsGeneric) {
3836 Arguments get_value_args = new Arguments (2);
3837 get_value_args.Add (new Argument (value));
3838 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3839 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3840 if (get_item == null)
3844 // A value was not found, go to default case
3846 get_item.EmitBranchable (ec, default_target, false);
3848 Arguments get_value_args = new Arguments (1);
3849 get_value_args.Add (new Argument (value));
3851 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3852 if (get_item == null)
3855 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
3856 get_item_object.EmitAssign (ec, get_item, true, false);
3857 ec.Emit (OpCodes.Brfalse, default_target);
3859 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3860 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
3862 get_item_int.EmitStatement (ec);
3863 get_item_object.Release (ec);
3866 EmitTableSwitch (ec, string_switch_variable);
3867 string_switch_variable.Release (ec);
3870 protected override void DoEmit (EmitContext ec)
3873 // Needed to emit anonymous storey initialization
3874 // Otherwise it does not contain any statements for now
3878 default_target = ec.DefineLabel ();
3879 null_target = ec.DefineLabel ();
3881 // Store variable for comparission purposes
3882 // TODO: Don't duplicate non-captured VariableReference
3883 LocalTemporary value;
3885 value = new LocalTemporary (SwitchType);
3886 unwrap.EmitCheck (ec);
3887 ec.Emit (OpCodes.Brfalse, null_target);
3890 } else if (!is_constant) {
3891 value = new LocalTemporary (SwitchType);
3898 // Setup the codegen context
3900 Label old_end = ec.LoopEnd;
3901 Switch old_switch = ec.Switch;
3903 ec.LoopEnd = ec.DefineLabel ();
3908 if (constant_section != null)
3909 constant_section.Block.Emit (ec);
3910 } else if (string_dictionary != null) {
3911 DoEmitStringSwitch (value, ec);
3913 EmitTableSwitch (ec, value);
3919 // Restore context state.
3920 ec.MarkLabel (ec.LoopEnd);
3923 // Restore the previous context
3925 ec.LoopEnd = old_end;
3926 ec.Switch = old_switch;
3929 protected override void CloneTo (CloneContext clonectx, Statement t)
3931 Switch target = (Switch) t;
3933 target.Expr = Expr.Clone (clonectx);
3934 target.Sections = new List<SwitchSection> ();
3935 foreach (SwitchSection ss in Sections){
3936 target.Sections.Add (ss.Clone (clonectx));
3941 // A place where execution can restart in an iterator
3942 public abstract class ResumableStatement : Statement
3945 protected Label resume_point;
3947 public Label PrepareForEmit (EmitContext ec)
3951 resume_point = ec.DefineLabel ();
3953 return resume_point;
3956 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3961 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
3966 public abstract class TryFinallyBlock : ExceptionStatement
3968 protected Statement stmt;
3969 Label dispose_try_block;
3970 bool prepared_for_dispose, emitted_dispose;
3972 protected TryFinallyBlock (Statement stmt, Location loc)
3980 public Statement Statement {
3988 protected abstract void EmitTryBody (EmitContext ec);
3989 protected abstract void EmitFinallyBody (EmitContext ec);
3991 public override Label PrepareForDispose (EmitContext ec, Label end)
3993 if (!prepared_for_dispose) {
3994 prepared_for_dispose = true;
3995 dispose_try_block = ec.DefineLabel ();
3997 return dispose_try_block;
4000 protected sealed override void DoEmit (EmitContext ec)
4002 EmitTryBodyPrepare (ec);
4005 ec.BeginFinallyBlock ();
4007 Label start_finally = ec.DefineLabel ();
4008 if (resume_points != null) {
4009 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4011 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4012 ec.Emit (OpCodes.Brfalse_S, start_finally);
4013 ec.Emit (OpCodes.Endfinally);
4016 ec.MarkLabel (start_finally);
4017 EmitFinallyBody (ec);
4019 ec.EndExceptionBlock ();
4022 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4024 if (emitted_dispose)
4027 emitted_dispose = true;
4029 Label end_of_try = ec.DefineLabel ();
4031 // Ensure that the only way we can get into this code is through a dispatcher
4032 if (have_dispatcher)
4033 ec.Emit (OpCodes.Br, end);
4035 ec.BeginExceptionBlock ();
4037 ec.MarkLabel (dispose_try_block);
4039 Label[] labels = null;
4040 for (int i = 0; i < resume_points.Count; ++i) {
4041 ResumableStatement s = resume_points[i];
4042 Label ret = s.PrepareForDispose (ec, end_of_try);
4043 if (ret.Equals (end_of_try) && labels == null)
4045 if (labels == null) {
4046 labels = new Label[resume_points.Count];
4047 for (int j = 0; j < i; ++j)
4048 labels[j] = end_of_try;
4053 if (labels != null) {
4055 for (j = 1; j < labels.Length; ++j)
4056 if (!labels[0].Equals (labels[j]))
4058 bool emit_dispatcher = j < labels.Length;
4060 if (emit_dispatcher) {
4061 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4062 ec.Emit (OpCodes.Ldloc, pc);
4063 ec.EmitInt (first_resume_pc);
4064 ec.Emit (OpCodes.Sub);
4065 ec.Emit (OpCodes.Switch, labels);
4066 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4069 foreach (ResumableStatement s in resume_points)
4070 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4073 ec.MarkLabel (end_of_try);
4075 ec.BeginFinallyBlock ();
4077 EmitFinallyBody (ec);
4079 ec.EndExceptionBlock ();
4084 // Base class for blocks using exception handling
4086 public abstract class ExceptionStatement : ResumableStatement
4091 protected List<ResumableStatement> resume_points;
4092 protected int first_resume_pc;
4094 protected ExceptionStatement (Location loc)
4099 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4101 StateMachineInitializer state_machine = null;
4102 if (resume_points != null) {
4103 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4105 ec.EmitInt ((int) IteratorStorey.State.Running);
4106 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4109 ec.BeginExceptionBlock ();
4111 if (resume_points != null) {
4112 ec.MarkLabel (resume_point);
4114 // For normal control flow, we want to fall-through the Switch
4115 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4116 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4117 ec.EmitInt (first_resume_pc);
4118 ec.Emit (OpCodes.Sub);
4120 Label[] labels = new Label[resume_points.Count];
4121 for (int i = 0; i < resume_points.Count; ++i)
4122 labels[i] = resume_points[i].PrepareForEmit (ec);
4123 ec.Emit (OpCodes.Switch, labels);
4127 public void SomeCodeFollows ()
4130 code_follows = true;
4134 public override bool Resolve (BlockContext ec)
4137 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4138 // So, ensure there's some IL code after this statement.
4139 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4140 ec.NeedReturnLabel ();
4145 public void AddResumePoint (ResumableStatement stmt, int pc)
4147 if (resume_points == null) {
4148 resume_points = new List<ResumableStatement> ();
4149 first_resume_pc = pc;
4152 if (pc != first_resume_pc + resume_points.Count)
4153 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4155 resume_points.Add (stmt);
4160 public class Lock : TryFinallyBlock
4163 TemporaryVariableReference expr_copy;
4164 TemporaryVariableReference lock_taken;
4166 public Lock (Expression expr, Statement stmt, Location loc)
4172 public override bool Resolve (BlockContext ec)
4174 expr = expr.Resolve (ec);
4178 if (!TypeSpec.IsReferenceType (expr.Type)) {
4179 ec.Report.Error (185, loc,
4180 "`{0}' is not a reference type as required by the lock statement",
4181 expr.Type.GetSignatureForError ());
4184 if (expr.Type.IsGenericParameter) {
4185 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4188 VariableReference lv = expr as VariableReference;
4191 locked = lv.IsLockedByStatement;
4192 lv.IsLockedByStatement = true;
4198 using (ec.Set (ResolveContext.Options.LockScope)) {
4199 ec.StartFlowBranching (this);
4200 Statement.Resolve (ec);
4201 ec.EndFlowBranching ();
4205 lv.IsLockedByStatement = locked;
4211 // Have to keep original lock value around to unlock same location
4212 // in the case the original has changed or is null
4214 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4215 expr_copy.Resolve (ec);
4218 // Ensure Monitor methods are available
4220 if (ResolvePredefinedMethods (ec) > 1) {
4221 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4222 lock_taken.Resolve (ec);
4228 protected override void EmitTryBodyPrepare (EmitContext ec)
4230 expr_copy.EmitAssign (ec, expr);
4232 if (lock_taken != null) {
4234 // Initialize ref variable
4236 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4239 // Monitor.Enter (expr_copy)
4241 expr_copy.Emit (ec);
4242 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4245 base.EmitTryBodyPrepare (ec);
4248 protected override void EmitTryBody (EmitContext ec)
4251 // Monitor.Enter (expr_copy, ref lock_taken)
4253 if (lock_taken != null) {
4254 expr_copy.Emit (ec);
4255 lock_taken.LocalInfo.CreateBuilder (ec);
4256 lock_taken.AddressOf (ec, AddressOp.Load);
4257 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4260 Statement.Emit (ec);
4263 protected override void EmitFinallyBody (EmitContext ec)
4266 // if (lock_taken) Monitor.Exit (expr_copy)
4268 Label skip = ec.DefineLabel ();
4270 if (lock_taken != null) {
4271 lock_taken.Emit (ec);
4272 ec.Emit (OpCodes.Brfalse_S, skip);
4275 expr_copy.Emit (ec);
4276 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4278 ec.Emit (OpCodes.Call, m);
4280 ec.MarkLabel (skip);
4283 int ResolvePredefinedMethods (ResolveContext rc)
4285 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4286 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4290 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4294 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4298 protected override void CloneTo (CloneContext clonectx, Statement t)
4300 Lock target = (Lock) t;
4302 target.expr = expr.Clone (clonectx);
4303 target.stmt = Statement.Clone (clonectx);
4307 public class Unchecked : Statement {
4310 public Unchecked (Block b, Location loc)
4317 public override bool Resolve (BlockContext ec)
4319 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4320 return Block.Resolve (ec);
4323 protected override void DoEmit (EmitContext ec)
4325 using (ec.With (EmitContext.Options.CheckedScope, false))
4329 protected override void CloneTo (CloneContext clonectx, Statement t)
4331 Unchecked target = (Unchecked) t;
4333 target.Block = clonectx.LookupBlock (Block);
4337 public class Checked : Statement {
4340 public Checked (Block b, Location loc)
4343 b.Unchecked = false;
4347 public override bool Resolve (BlockContext ec)
4349 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4350 return Block.Resolve (ec);
4353 protected override void DoEmit (EmitContext ec)
4355 using (ec.With (EmitContext.Options.CheckedScope, true))
4359 protected override void CloneTo (CloneContext clonectx, Statement t)
4361 Checked target = (Checked) t;
4363 target.Block = clonectx.LookupBlock (Block);
4367 public class Unsafe : Statement {
4370 public Unsafe (Block b, Location loc)
4373 Block.Unsafe = true;
4377 public override bool Resolve (BlockContext ec)
4379 if (ec.CurrentIterator != null)
4380 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4382 using (ec.Set (ResolveContext.Options.UnsafeScope))
4383 return Block.Resolve (ec);
4386 protected override void DoEmit (EmitContext ec)
4391 protected override void CloneTo (CloneContext clonectx, Statement t)
4393 Unsafe target = (Unsafe) t;
4395 target.Block = clonectx.LookupBlock (Block);
4402 public class Fixed : Statement
4404 abstract class Emitter : ShimExpression
4406 protected LocalVariable vi;
4408 protected Emitter (Expression expr, LocalVariable li)
4414 public abstract void EmitExit (EmitContext ec);
4417 class ExpressionEmitter : Emitter {
4418 public ExpressionEmitter (Expression converted, LocalVariable li) :
4419 base (converted, li)
4423 protected override Expression DoResolve (ResolveContext rc)
4425 throw new NotImplementedException ();
4428 public override void Emit (EmitContext ec) {
4430 // Store pointer in pinned location
4436 public override void EmitExit (EmitContext ec)
4439 ec.Emit (OpCodes.Conv_U);
4444 class StringEmitter : Emitter
4446 LocalVariable pinned_string;
4448 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4453 protected override Expression DoResolve (ResolveContext rc)
4455 pinned_string = new LocalVariable (vi.Block, "$pinned",
4456 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4458 pinned_string.Type = rc.BuiltinTypes.String;
4460 eclass = ExprClass.Variable;
4461 type = rc.BuiltinTypes.Int;
4465 public override void Emit (EmitContext ec)
4467 pinned_string.CreateBuilder (ec);
4470 pinned_string.EmitAssign (ec);
4472 // TODO: Should use Binary::Add
4473 pinned_string.Emit (ec);
4474 ec.Emit (OpCodes.Conv_I);
4476 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4480 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4481 //pe.InstanceExpression = pinned_string;
4482 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4484 ec.Emit (OpCodes.Add);
4488 public override void EmitExit (EmitContext ec)
4491 pinned_string.EmitAssign (ec);
4495 public class VariableDeclaration : BlockVariableDeclaration
4497 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4502 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4504 if (!Variable.Type.IsPointer && li == Variable) {
4505 bc.Report.Error (209, TypeExpression.Location,
4506 "The type of locals declared in a fixed statement must be a pointer type");
4511 // The rules for the possible declarators are pretty wise,
4512 // but the production on the grammar is more concise.
4514 // So we have to enforce these rules here.
4516 // We do not resolve before doing the case 1 test,
4517 // because the grammar is explicit in that the token &
4518 // is present, so we need to test for this particular case.
4521 if (initializer is Cast) {
4522 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4526 initializer = initializer.Resolve (bc);
4528 if (initializer == null)
4534 if (initializer.Type.IsArray) {
4535 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4538 // Provided that array_type is unmanaged,
4540 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4544 // and T* is implicitly convertible to the
4545 // pointer type given in the fixed statement.
4547 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4549 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4550 if (converted == null)
4554 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4556 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4557 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4558 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4559 new NullLiteral (loc),
4562 converted = converted.Resolve (bc);
4564 return new ExpressionEmitter (converted, li);
4570 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4571 return new StringEmitter (initializer, li, loc).Resolve (bc);
4574 // Case 3: fixed buffer
4575 if (initializer is FixedBufferPtr) {
4576 return new ExpressionEmitter (initializer, li);
4580 // Case 4: & object.
4582 bool already_fixed = true;
4583 Unary u = initializer as Unary;
4584 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4585 IVariableReference vr = u.Expr as IVariableReference;
4586 if (vr == null || !vr.IsFixed) {
4587 already_fixed = false;
4591 if (already_fixed) {
4592 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4595 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4596 return new ExpressionEmitter (initializer, li);
4601 VariableDeclaration decl;
4602 Statement statement;
4605 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4614 public Statement Statement {
4620 public BlockVariableDeclaration Variables {
4628 public override bool Resolve (BlockContext ec)
4630 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4631 if (!decl.Resolve (ec))
4635 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4636 bool ok = statement.Resolve (ec);
4637 bool flow_unreachable = ec.EndFlowBranching ();
4638 has_ret = flow_unreachable;
4643 protected override void DoEmit (EmitContext ec)
4645 decl.Variable.CreateBuilder (ec);
4646 decl.Initializer.Emit (ec);
4647 if (decl.Declarators != null) {
4648 foreach (var d in decl.Declarators) {
4649 d.Variable.CreateBuilder (ec);
4650 d.Initializer.Emit (ec);
4654 statement.Emit (ec);
4660 // Clear the pinned variable
4662 ((Emitter) decl.Initializer).EmitExit (ec);
4663 if (decl.Declarators != null) {
4664 foreach (var d in decl.Declarators) {
4665 ((Emitter)d.Initializer).EmitExit (ec);
4670 protected override void CloneTo (CloneContext clonectx, Statement t)
4672 Fixed target = (Fixed) t;
4674 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4675 target.statement = statement.Clone (clonectx);
4679 public class Catch : Statement
4683 FullNamedExpression type_expr;
4684 CompilerAssign assign;
4687 public Catch (Block block, Location loc)
4695 public Block Block {
4701 public TypeSpec CatchType {
4707 public bool IsGeneral {
4709 return type_expr == null;
4713 public FullNamedExpression TypeExpression {
4722 public LocalVariable Variable {
4733 protected override void DoEmit (EmitContext ec)
4736 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
4738 ec.BeginCatchBlock (CatchType);
4741 li.CreateBuilder (ec);
4744 // Special case hoisted catch variable, we have to use a temporary variable
4745 // to pass via anonymous storey initialization with the value still on top
4748 if (li.HoistedVariant != null) {
4749 LocalTemporary lt = new LocalTemporary (li.Type);
4750 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4752 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4754 // switch to assigning from the temporary variable and not from top of the stack
4755 assign.UpdateSource (lt);
4758 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4759 ec.Emit (OpCodes.Pop);
4760 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4766 public override bool Resolve (BlockContext ec)
4768 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4769 if (type_expr != null) {
4770 type = type_expr.ResolveAsType (ec);
4774 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
4775 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4776 } else if (li != null) {
4778 li.PrepareForFlowAnalysis (ec);
4780 // source variable is at the top of the stack
4781 Expression source = new EmptyExpression (li.Type);
4782 if (li.Type.IsGenericParameter)
4783 source = new UnboxCast (source, li.Type);
4785 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4786 Block.AddScopeStatement (new StatementExpression (assign));
4790 return Block.Resolve (ec);
4794 protected override void CloneTo (CloneContext clonectx, Statement t)
4796 Catch target = (Catch) t;
4798 if (type_expr != null)
4799 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4801 target.block = clonectx.LookupBlock (block);
4805 public class TryFinally : TryFinallyBlock
4809 public TryFinally (Statement stmt, Block fini, Location loc)
4815 public override bool Resolve (BlockContext ec)
4819 ec.StartFlowBranching (this);
4821 if (!stmt.Resolve (ec))
4825 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4826 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4827 if (!fini.Resolve (ec))
4831 ec.EndFlowBranching ();
4833 ok &= base.Resolve (ec);
4838 protected override void EmitTryBody (EmitContext ec)
4843 protected override void EmitFinallyBody (EmitContext ec)
4848 protected override void CloneTo (CloneContext clonectx, Statement t)
4850 TryFinally target = (TryFinally) t;
4852 target.stmt = (Statement) stmt.Clone (clonectx);
4854 target.fini = clonectx.LookupBlock (fini);
4858 public class TryCatch : ExceptionStatement
4861 public List<Catch> Specific;
4862 public Catch General;
4863 readonly bool inside_try_finally;
4865 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4869 this.Specific = catch_clauses;
4870 this.inside_try_finally = inside_try_finally;
4872 Catch c = catch_clauses [0];
4875 catch_clauses.RemoveAt (0);
4879 public bool IsTryCatchFinally {
4881 return inside_try_finally;
4885 public override bool Resolve (BlockContext ec)
4889 ec.StartFlowBranching (this);
4891 if (!Block.Resolve (ec))
4894 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4896 foreach (Catch c in Specific){
4897 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4899 if (!c.Resolve (ec)) {
4904 TypeSpec resolved_type = c.CatchType;
4905 for (int ii = 0; ii < last_index; ++ii) {
4906 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4907 ec.Report.Error (160, c.loc,
4908 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4909 TypeManager.CSharpName (prev_catches [ii]));
4914 prev_catches [last_index++] = resolved_type;
4917 if (General != null) {
4918 foreach (Catch c in Specific) {
4919 if (c.CatchType.BuiltinType != BuiltinTypeSpec.Type.Exception)
4922 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
4925 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4928 ec.Report.Warning (1058, 1, c.loc,
4929 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4932 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4934 if (!General.Resolve (ec))
4938 ec.EndFlowBranching ();
4940 return base.Resolve (ec) && ok;
4943 protected sealed override void DoEmit (EmitContext ec)
4945 if (!inside_try_finally)
4946 EmitTryBodyPrepare (ec);
4950 foreach (Catch c in Specific)
4953 if (General != null)
4956 if (!inside_try_finally)
4957 ec.EndExceptionBlock ();
4960 protected override void CloneTo (CloneContext clonectx, Statement t)
4962 TryCatch target = (TryCatch) t;
4964 target.Block = clonectx.LookupBlock (Block);
4965 if (General != null)
4966 target.General = (Catch) General.Clone (clonectx);
4967 if (Specific != null){
4968 target.Specific = new List<Catch> ();
4969 foreach (Catch c in Specific)
4970 target.Specific.Add ((Catch) c.Clone (clonectx));
4975 public class Using : TryFinallyBlock
4977 public class VariableDeclaration : BlockVariableDeclaration
4979 Statement dispose_call;
4981 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4986 public VariableDeclaration (LocalVariable li, Location loc)
4992 public VariableDeclaration (Expression expr)
4995 loc = expr.Location;
5001 public bool IsNested { get; private set; }
5005 public void EmitDispose (EmitContext ec)
5007 dispose_call.Emit (ec);
5010 public override bool Resolve (BlockContext bc)
5015 return base.Resolve (bc, false);
5018 public Expression ResolveExpression (BlockContext bc)
5020 var e = Initializer.Resolve (bc);
5024 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5025 Initializer = ResolveInitializer (bc, Variable, e);
5029 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5031 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5032 initializer = initializer.Resolve (bc);
5033 if (initializer == null)
5036 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5037 Arguments args = new Arguments (1);
5038 args.Add (new Argument (initializer));
5039 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5040 if (initializer == null)
5043 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5044 dispose_call = CreateDisposeCall (bc, var);
5045 dispose_call.Resolve (bc);
5047 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5050 if (li == Variable) {
5051 CheckIDiposableConversion (bc, li, initializer);
5052 dispose_call = CreateDisposeCall (bc, li);
5053 dispose_call.Resolve (bc);
5056 return base.ResolveInitializer (bc, li, initializer);
5059 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5063 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5064 if (type.IsNullableType) {
5065 // it's handled in CreateDisposeCall
5069 bc.Report.SymbolRelatedToPreviousError (type);
5070 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5071 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5072 type.GetSignatureForError ());
5078 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5080 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5082 var loc = lv.Location;
5084 var idt = bc.BuiltinTypes.IDisposable;
5085 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5087 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5088 dispose_mg.InstanceExpression = type.IsNullableType ?
5089 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5092 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5094 // Add conditional call when disposing possible null variable
5095 if (!type.IsStruct || type.IsNullableType)
5096 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5101 public void ResolveDeclaratorInitializer (BlockContext bc)
5103 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5106 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5108 for (int i = declarators.Count - 1; i >= 0; --i) {
5109 var d = declarators [i];
5110 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5111 vd.Initializer = d.Initializer;
5113 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5114 vd.dispose_call.Resolve (bc);
5116 stmt = new Using (vd, stmt, d.Variable.Location);
5124 VariableDeclaration decl;
5126 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5132 public Using (Expression expr, Statement stmt, Location loc)
5135 this.decl = new VariableDeclaration (expr);
5140 public Expression Expression {
5142 return decl.Variable == null ? decl.Initializer : null;
5146 public BlockVariableDeclaration Variables {
5154 protected override void EmitTryBodyPrepare (EmitContext ec)
5157 base.EmitTryBodyPrepare (ec);
5160 protected override void EmitTryBody (EmitContext ec)
5165 protected override void EmitFinallyBody (EmitContext ec)
5167 decl.EmitDispose (ec);
5170 public override bool Resolve (BlockContext ec)
5172 VariableReference vr;
5173 bool vr_locked = false;
5175 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5176 if (decl.Variable == null) {
5177 vr = decl.ResolveExpression (ec) as VariableReference;
5179 vr_locked = vr.IsLockedByStatement;
5180 vr.IsLockedByStatement = true;
5183 if (decl.IsNested) {
5184 decl.ResolveDeclaratorInitializer (ec);
5186 if (!decl.Resolve (ec))
5189 if (decl.Declarators != null) {
5190 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5198 ec.StartFlowBranching (this);
5202 ec.EndFlowBranching ();
5205 vr.IsLockedByStatement = vr_locked;
5212 protected override void CloneTo (CloneContext clonectx, Statement t)
5214 Using target = (Using) t;
5216 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5217 target.stmt = stmt.Clone (clonectx);
5222 /// Implementation of the foreach C# statement
5224 public class Foreach : Statement {
5226 sealed class ArrayForeach : Statement
5228 readonly Foreach for_each;
5229 readonly Statement statement;
5232 TemporaryVariableReference[] lengths;
5233 Expression [] length_exprs;
5234 StatementExpression[] counter;
5235 TemporaryVariableReference[] variables;
5237 TemporaryVariableReference copy;
5239 LocalVariableReference variable;
5241 public ArrayForeach (Foreach @foreach, int rank)
5243 for_each = @foreach;
5244 statement = for_each.statement;
5246 variable = new LocalVariableReference (for_each.variable, loc);
5248 counter = new StatementExpression[rank];
5249 variables = new TemporaryVariableReference[rank];
5250 length_exprs = new Expression [rank];
5253 // Only use temporary length variables when dealing with
5254 // multi-dimensional arrays
5257 lengths = new TemporaryVariableReference [rank];
5260 protected override void CloneTo (CloneContext clonectx, Statement target)
5262 throw new NotImplementedException ();
5265 public override bool Resolve (BlockContext ec)
5267 Block variables_block = variable.local_info.Block;
5268 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5271 int rank = length_exprs.Length;
5272 Arguments list = new Arguments (rank);
5273 for (int i = 0; i < rank; i++) {
5274 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5276 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5277 counter[i].Resolve (ec);
5280 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5282 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5283 lengths[i].Resolve (ec);
5285 Arguments args = new Arguments (1);
5286 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5287 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5290 list.Add (new Argument (v));
5293 access = new ElementAccess (copy, list, loc).Resolve (ec);
5298 if (for_each.type is VarExpr) {
5299 // Infer implicitly typed local variable from foreach array type
5300 var_type = access.Type;
5302 var_type = for_each.type.ResolveAsType (ec);
5305 if (var_type == null)
5308 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5314 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5315 ec.CurrentBranching.CreateSibling ();
5317 variable.local_info.Type = conv.Type;
5318 variable.Resolve (ec);
5320 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5321 if (!statement.Resolve (ec))
5323 ec.EndFlowBranching ();
5325 // There's no direct control flow from the end of the embedded statement to the end of the loop
5326 ec.CurrentBranching.CurrentUsageVector.Goto ();
5328 ec.EndFlowBranching ();
5333 protected override void DoEmit (EmitContext ec)
5335 copy.EmitAssign (ec, for_each.expr);
5337 int rank = length_exprs.Length;
5338 Label[] test = new Label [rank];
5339 Label[] loop = new Label [rank];
5341 for (int i = 0; i < rank; i++) {
5342 test [i] = ec.DefineLabel ();
5343 loop [i] = ec.DefineLabel ();
5345 if (lengths != null)
5346 lengths [i].EmitAssign (ec, length_exprs [i]);
5349 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5350 for (int i = 0; i < rank; i++) {
5351 variables [i].EmitAssign (ec, zero);
5353 ec.Emit (OpCodes.Br, test [i]);
5354 ec.MarkLabel (loop [i]);
5357 variable.local_info.CreateBuilder (ec);
5358 variable.EmitAssign (ec, conv, false, false);
5360 statement.Emit (ec);
5362 ec.MarkLabel (ec.LoopBegin);
5364 for (int i = rank - 1; i >= 0; i--){
5365 counter [i].Emit (ec);
5367 ec.MarkLabel (test [i]);
5368 variables [i].Emit (ec);
5370 if (lengths != null)
5371 lengths [i].Emit (ec);
5373 length_exprs [i].Emit (ec);
5375 ec.Emit (OpCodes.Blt, loop [i]);
5378 ec.MarkLabel (ec.LoopEnd);
5382 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5384 class Body : Statement
5387 LocalVariableReference variable;
5388 Expression current, conv;
5389 Statement statement;
5391 public Body (TypeSpec type, LocalVariable variable,
5392 Expression current, Statement statement,
5396 this.variable = new LocalVariableReference (variable, loc);
5397 this.current = current;
5398 this.statement = statement;
5402 protected override void CloneTo (CloneContext clonectx, Statement target)
5404 throw new NotImplementedException ();
5407 public override bool Resolve (BlockContext ec)
5409 current = current.Resolve (ec);
5410 if (current == null)
5413 conv = Convert.ExplicitConversion (ec, current, type, loc);
5417 variable.local_info.Type = conv.Type;
5418 variable.Resolve (ec);
5420 if (!statement.Resolve (ec))
5426 protected override void DoEmit (EmitContext ec)
5428 variable.local_info.CreateBuilder (ec);
5429 variable.EmitAssign (ec, conv, false, false);
5431 statement.Emit (ec);
5435 class RuntimeDispose : Using.VariableDeclaration
5437 public RuntimeDispose (LocalVariable lv, Location loc)
5442 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5444 // Defered to runtime check
5447 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5449 var idt = bc.BuiltinTypes.IDisposable;
5452 // Fabricates code like
5454 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5457 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5459 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5460 dispose_variable.CreateReferenceExpression (bc, loc),
5461 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5462 loc), new NullLiteral (loc), loc);
5464 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5466 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5467 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5469 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5470 return new If (idisaposable_test, dispose, loc);
5474 LocalVariable variable;
5476 Statement statement;
5477 Expression var_type;
5478 ExpressionStatement init;
5479 TemporaryVariableReference enumerator_variable;
5480 bool ambiguous_getenumerator_name;
5482 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5484 this.var_type = var_type;
5485 this.variable = var;
5491 protected override void CloneTo (CloneContext clonectx, Statement target)
5493 throw new NotImplementedException ();
5496 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5498 rc.Report.SymbolRelatedToPreviousError (enumerator);
5499 rc.Report.Error (202, loc,
5500 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5501 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5504 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5507 // Option 1: Try to match by name GetEnumerator first
5509 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5510 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5512 var mg = mexpr as MethodGroupExpr;
5514 mg.InstanceExpression = expr;
5515 Arguments args = new Arguments (0);
5516 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5518 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5519 if (ambiguous_getenumerator_name)
5522 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5528 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5531 PredefinedMember<MethodSpec> iface_candidate = null;
5532 var ptypes = rc.Module.PredefinedTypes;
5533 var gen_ienumerable = ptypes.IEnumerableGeneric;
5534 if (!gen_ienumerable.Define ())
5535 gen_ienumerable = null;
5538 var ifaces = t.Interfaces;
5539 if (ifaces != null) {
5540 foreach (var iface in ifaces) {
5541 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5542 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5543 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5544 rc.Report.Error (1640, loc,
5545 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5546 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5551 // TODO: Cache this somehow
5552 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5553 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5558 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5559 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5564 if (t.IsGenericParameter)
5569 } while (t != null);
5571 if (iface_candidate == null) {
5572 rc.Report.Error (1579, loc,
5573 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5574 expr.Type.GetSignatureForError (), "GetEnumerator");
5579 var method = iface_candidate.Resolve (loc);
5583 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5584 mg.InstanceExpression = expr;
5588 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5590 var ms = MemberCache.FindMember (enumerator.ReturnType,
5591 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5592 BindingRestriction.InstanceOnly) as MethodSpec;
5594 if (ms == null || !ms.IsPublic) {
5595 Error_WrongEnumerator (rc, enumerator);
5599 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5602 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5604 var ps = MemberCache.FindMember (enumerator.ReturnType,
5605 MemberFilter.Property ("Current", null),
5606 BindingRestriction.InstanceOnly) as PropertySpec;
5608 if (ps == null || !ps.IsPublic) {
5609 Error_WrongEnumerator (rc, enumerator);
5616 public override bool Resolve (BlockContext ec)
5618 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
5621 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
5622 } else if (expr.Type.IsNullableType) {
5623 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5626 var get_enumerator_mg = ResolveGetEnumerator (ec);
5627 if (get_enumerator_mg == null) {
5631 var get_enumerator = get_enumerator_mg.BestCandidate;
5632 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5633 enumerator_variable.Resolve (ec);
5635 // Prepare bool MoveNext ()
5636 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5637 if (move_next_mg == null) {
5641 move_next_mg.InstanceExpression = enumerator_variable;
5643 // Prepare ~T~ Current { get; }
5644 var current_prop = ResolveCurrent (ec, get_enumerator);
5645 if (current_prop == null) {
5649 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5650 if (current_pe == null)
5653 VarExpr ve = var_type as VarExpr;
5657 // Source type is dynamic, set element type to dynamic too
5658 variable.Type = ec.BuiltinTypes.Dynamic;
5660 // Infer implicitly typed local variable from foreach enumerable type
5661 variable.Type = current_pe.Type;
5665 // Explicit cast of dynamic collection elements has to be done at runtime
5666 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
5669 variable.Type = var_type.ResolveAsType (ec);
5672 if (variable.Type == null)
5675 var init = new Invocation (get_enumerator_mg, null);
5677 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5678 new Body (variable.Type, variable, current_pe, statement, loc), loc);
5680 var enum_type = enumerator_variable.Type;
5683 // Add Dispose method call when enumerator can be IDisposable
5685 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
5686 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
5688 // Runtime Dispose check
5690 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5691 vd.Initializer = init;
5692 statement = new Using (vd, statement, loc);
5695 // No Dispose call needed
5697 this.init = new SimpleAssign (enumerator_variable, init);
5698 this.init.Resolve (ec);
5702 // Static Dispose check
5704 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5705 vd.Initializer = init;
5706 statement = new Using (vd, statement, loc);
5709 return statement.Resolve (ec);
5712 protected override void DoEmit (EmitContext ec)
5714 enumerator_variable.LocalInfo.CreateBuilder (ec);
5717 init.EmitStatement (ec);
5719 statement.Emit (ec);
5722 #region IErrorHandler Members
5724 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5726 ec.Report.SymbolRelatedToPreviousError (best);
5727 ec.Report.Warning (278, 2, loc,
5728 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5729 expr.Type.GetSignatureForError (), "enumerable",
5730 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5732 ambiguous_getenumerator_name = true;
5736 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5741 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5746 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5755 LocalVariable variable;
5757 Statement statement;
5759 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5762 this.variable = var;
5768 public Statement Statement {
5769 get { return statement; }
5772 public override bool Resolve (BlockContext ec)
5774 expr = expr.Resolve (ec);
5779 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5783 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5784 statement = new ArrayForeach (this, 1);
5785 } else if (expr.Type is ArrayContainer) {
5786 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5788 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5789 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5790 expr.ExprClassName);
5794 statement = new CollectionForeach (type, variable, expr, statement, loc);
5797 return statement.Resolve (ec);
5800 protected override void DoEmit (EmitContext ec)
5802 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5803 ec.LoopBegin = ec.DefineLabel ();
5804 ec.LoopEnd = ec.DefineLabel ();
5806 statement.Emit (ec);
5808 ec.LoopBegin = old_begin;
5809 ec.LoopEnd = old_end;
5812 protected override void CloneTo (CloneContext clonectx, Statement t)
5814 Foreach target = (Foreach) t;
5816 target.type = type.Clone (clonectx);
5817 target.expr = expr.Clone (clonectx);
5818 target.statement = statement.Clone (clonectx);